diff --git a/sql/optional/mall/mall.sql b/sql/optional/mall/mall.sql index 43e44c910c..8dce48143d 100644 --- a/sql/optional/mall/mall.sql +++ b/sql/optional/mall/mall.sql @@ -248,7 +248,7 @@ DROP TABLE IF EXISTS `product_spu`; CREATE TABLE `product_spu` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号', - `brand_id` int DEFAULT NULL COMMENT '商品品牌编号', + `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号', `category_id` bigint NOT NULL COMMENT '分类id', `spec_type` int NOT NULL COMMENT '规格类型:0 单规格 1 多规格', `code` varchar(128) DEFAULT NULL COMMENT '商品编码', diff --git a/sql/optional/mall/order.sql b/sql/optional/mall/order.sql index 21e60200d9..23bdc79480 100644 --- a/sql/optional/mall/order.sql +++ b/sql/optional/mall/order.sql @@ -1,4 +1,5 @@ /**todo cancelType 设置默认值 0?*/ +DROP TABLE IF EXISTS `trade_order`; CREATE TABLE `trade_order` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', @@ -10,9 +11,10 @@ CREATE TABLE `trade_order` `user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注', `status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]', `product_count` int NOT NULL COMMENT '购买的商品数量', - `cancel_type` int NOT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]', + `cancel_type` int DEFAULT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]', `remark` varchar(200) DEFAULT NULL COMMENT '商家备注', `payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]', + `pay_time` datetime DEFAULT NULL COMMENT '订单支付时间', `finish_time` datetime DEFAULT NULL COMMENT '订单完成时间', `cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间', `sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分', @@ -20,11 +22,11 @@ CREATE TABLE `trade_order` `order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分', `delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分', `pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分', - `pay_order_id` int NOT NULL COMMENT '支付订单编号', - `pay_channel` int NOT NULL COMMENT '支付成功的支付渠道', - `delivery_type` int NOT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]', - `actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]', - `delivery_templateid` int DEFAULT NULL COMMENT '配置模板的编号', + `pay_order_id` int DEFAULT NULL COMMENT '支付订单编号', + `pay_channel` int DEFAULT NULL COMMENT '支付成功的支付渠道', + `delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]', + `actual_delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]', + `delivery_template_id` int DEFAULT NULL COMMENT '配置模板的编号', `express_no` int DEFAULT NULL COMMENT '物流公司单号', `delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]', `delivery_time` datetime DEFAULT NULL COMMENT '发货时间', @@ -73,4 +75,4 @@ CREATE TABLE `trade_order_item` `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB COMMENT ='交易订单明细表'; \ No newline at end of file +) ENGINE = InnoDB COMMENT ='交易订单明细表'; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java index b304462e57..a96ee240cd 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java @@ -12,7 +12,8 @@ import lombok.Getter; @AllArgsConstructor public enum DocumentEnum { - REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"); + REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"), + TENANT("https://doc.iocoder.cn", "SaaS 多租户文档"); private final String url; private final String memo; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java index fd7bb85c42..b4cb94e1f3 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java @@ -1,13 +1,8 @@ package cn.iocoder.yudao.framework.common.util.date; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.LocalDateTimeUtil; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.util.Calendar; import java.util.Date; @@ -58,6 +53,7 @@ public class DateUtils { return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); } + @Deprecated public static Date addTime(Duration duration) { return new Date(System.currentTimeMillis() + duration.toMillis()); } @@ -87,6 +83,7 @@ public class DateUtils { return buildTime(year, mouth, day, 0, 0, 0); } + @Deprecated public static LocalDateTime buildLocalDateTime(int year, int mouth, int day) { return LocalDateTime.of(year, mouth, day, 0, 0, 0); } @@ -135,14 +132,7 @@ public class DateUtils { return a.isAfter(b) ? a : b; } - public static boolean beforeNow(Date date) { - return date.getTime() < System.currentTimeMillis(); - } - - public static boolean afterNow(Date date) { - return date.getTime() >= System.currentTimeMillis(); - } - + @Deprecated public static boolean afterNow(LocalDateTime localDateTime) { return localDateTime.isAfter(LocalDateTime.now()); } @@ -178,19 +168,6 @@ public class DateUtils { return c.getTime(); } - /** - * 是否今天 - * - * @param date 日期 - * @return 是否 - */ - public static boolean isToday(Date date) { - if (date == null) { - return false; - } - return DateUtil.isSameDay(date, new Date()); - } - /** * 是否今天 * diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java new file mode 100644 index 0000000000..7af32ea82c --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.framework.common.util.date; + +import cn.hutool.core.date.LocalDateTimeUtil; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 时间工具类,用于 {@link java.time.LocalDateTime} + * + * @author 芋道源码 + */ +public class LocalDateTimeUtils { + + public static LocalDateTime addTime(Duration duration) { + return LocalDateTime.now().plus(duration); + } + + public static boolean beforeNow(LocalDateTime date) { + return date.isBefore(LocalDateTime.now()); + } + + public static boolean afterNow(LocalDateTime date) { + return date.isAfter(LocalDateTime.now()); + } + + /** + * 创建指定时间 + * + * @param year 年 + * @param mouth 月 + * @param day 日 + * @return 指定时间 + */ + public static LocalDateTime buildTime(int year, int mouth, int day) { + return LocalDateTime.of(year, mouth, day, 0, 0, 0); + } + + /** + * 判断当前时间是否在该时间范围内 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 是否 + */ + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) { + if (startTime == null || endTime == null) { + return false; + } + return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime); + } + +} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java index 3f79ceea61..88ba22d87b 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java @@ -3,7 +3,10 @@ package cn.iocoder.yudao.framework.common.util.string; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; /** * 字符串工具类 @@ -37,4 +40,9 @@ public class StrUtils { return false; } + public static List splitToLong(String value, CharSequence separator) { + long[] longs = StrUtil.splitToLong(value, separator); + return Arrays.stream(longs).boxed().collect(Collectors.toList()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java index a28745e6c3..3fc05ce779 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java @@ -124,7 +124,7 @@ public class YunpianSmsClientTest extends BaseMockitoUnitTest { @SuppressWarnings("unchecked") public void testDoGetSmsTemplate() throws Throwable { // 准备参数 - String apiTemplateId = randomString(); + String apiTemplateId = String.valueOf(randomLongId()); // mock tpl 方法 TplApi tplApi = mock(TplApi.class); when(client.tpl()).thenReturn(tplApi); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index 46955d3abc..e351d82e14 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -119,6 +119,8 @@ public class YudaoTenantAutoConfiguration { }; } + // ========== Redis ========== + @Bean @Primary // 引入租户时,tenantRedisCacheManager 为主 Bean public RedisCacheManager tenantRedisCacheManager(RedisTemplate redisTemplate, diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java index a42acc41cd..93a17b376e 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.tenant.core.context; +import cn.iocoder.yudao.framework.common.enums.DocumentEnum; import com.alibaba.ttl.TransmittableThreadLocal; /** @@ -36,7 +37,8 @@ public class TenantContextHolder { public static Long getRequiredTenantId() { Long tenantId = getTenantId(); if (tenantId == null) { - throw new NullPointerException("TenantContextHolder 不存在租户编号"); // TODO 芋艿:增加文档链接 + throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:" + + DocumentEnum.TENANT.getUrl()); } return tenantId; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java index 10d62232d8..8058b2f54c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.tenant.core.redis; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; import org.springframework.cache.Cache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; @@ -11,7 +10,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter; /** * 多租户的 {@link RedisCacheManager} 实现类 * - * 操作指定 name 的 {@link Cache} 时,自动拼接租户后缀,格式为 name + ":" + tenantId + * 操作指定 name 的 {@link Cache} 时,自动拼接租户后缀,格式为 name + ":" + tenantId + 后缀 * * @author airhead */ @@ -24,7 +23,7 @@ public class TenantRedisCacheManager extends RedisCacheManager { } @Override - public Cache getCache(@NotNull String name) { + public Cache getCache(String name) { // 如果开启多租户,则 name 拼接租户后缀 if (!TenantContextHolder.isIgnore() && TenantContextHolder.getTenantId() != null) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java index 9a047312c1..efb808475e 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java @@ -28,8 +28,6 @@ import java.util.Objects; * 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。 * 3. 校验租户是合法,例如说被禁用、到期 * - * 校验用户访问的租户,是否是其所在的租户, - * * @author 芋道源码 */ @Slf4j diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongListTypeHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongListTypeHandler.java new file mode 100644 index 0000000000..8c30f3df8b --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongListTypeHandler.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.framework.mybatis.core.type; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; +import org.apache.ibatis.type.TypeHandler; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * List 的类型转换器实现类,对应数据库的 varchar 类型 + * + * @author 芋道源码 + */ +@MappedJdbcTypes(JdbcType.VARCHAR) +@MappedTypes(List.class) +public class LongListTypeHandler implements TypeHandler> { + + private static final String COMMA = ","; + + @Override + public void setParameter(PreparedStatement ps, int i, List strings, JdbcType jdbcType) throws SQLException { + // 设置占位符 + ps.setString(i, CollUtil.join(strings, COMMA)); + } + + @Override + public List getResult(ResultSet rs, String columnName) throws SQLException { + String value = rs.getString(columnName); + return getResult(value); + } + + @Override + public List getResult(ResultSet rs, int columnIndex) throws SQLException { + String value = rs.getString(columnIndex); + return getResult(value); + } + + @Override + public List getResult(CallableStatement cs, int columnIndex) throws SQLException { + String value = cs.getString(columnIndex); + return getResult(value); + } + + private List getResult(String value) { + if (value == null) { + return null; + } + return StrUtils.splitToLong(value, COMMA); + } +} diff --git a/yudao-module-mall/pom.xml b/yudao-module-mall/pom.xml index 68c1825cc1..4c362d526e 100644 --- a/yudao-module-mall/pom.xml +++ b/yudao-module-mall/pom.xml @@ -15,13 +15,13 @@ ${project.artifactId} - 商城大模块,由 product 商品、market 营销、trade 交易 coupon等组成 + 商城大模块,由 product 商品、promotion 营销、trade 交易 coupon等组成 - yudao-module-coupon-api - yudao-module-coupon-biz - yudao-module-market-api - yudao-module-market-biz + + + yudao-module-promotion-api + yudao-module-promotion-biz yudao-module-product-api yudao-module-product-biz yudao-module-trade-api diff --git a/yudao-module-mall/yudao-module-coupon-api/pom.xml b/yudao-module-mall/yudao-module-coupon-api/pom.xml deleted file mode 100644 index 6dab80613f..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-mall - ${revision} - - - 4.0.0 - yudao-module-coupon-api - jar - - - ${project.artifactId} - - coupon 模块 API,暴露给其它模块调用 - - - - - cn.iocoder.boot - yudao-common - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - - \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java deleted file mode 100644 index 0e1e17ecee..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 是否开启过期提醒 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponExpireTimeTypeEnum { - - OPEN(1,"不开启"), - CLOSE(0,"开启"),; - - /** - * 是否开启过期提醒 - */ - private final Integer type; - /** - * 是否开启过期提醒 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java deleted file mode 100644 index 66a86bc84c..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 领取是否无限制 0 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponFetchTypeEnum { - - LIMIT(1,"限制"), - NOT_LIMIT(0,"不限制"),; - - /** - * 是否开启过期提醒 - */ - private final Integer type; - /** - * 是否开启过期提醒 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java deleted file mode 100644 index 1ac9e9f681..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 优惠叠加类型 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponForbidPreferenceEnum { - - UN_FORBID(0,"不限制"), - FORBID(1,"优惠券仅原价购买商品时可用"); - - /** - * 优惠券类型 - */ - private final Integer type; - /** - * 优惠券类型名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java deleted file mode 100644 index e0946b7a08..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 优惠券商品使用类型 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponGoodsTypeEnum { - - ALL(1,"全部商品可用"), - POINT_PRODUCT(2,"指定商品可用"), - POINT_PRODUCT_NOT(3,"指定商品不可用"),; - - /** - * 优惠券商品使用类型 - */ - private final Integer type; - /** - * 优惠券商品使用类型名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java deleted file mode 100644 index 8130847315..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 优惠券状态类型 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponStatusTypeEnum { - - PROCESSING(1,"进行中"), - END(2,"已结束"), - CLOSE(3,"已关闭"),; - - /** - * 优惠券类型 - */ - private final Integer type; - /** - * 优惠券类型名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java deleted file mode 100644 index 639d5080d9..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券 - 优惠券类型 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponTypeEnum { - - REWARD(1,"满减"), - DISCOUNT(2,"折扣"), - RANDOW(3,"随机"),; - - /** - * 优惠券类型 - */ - private final Integer type; - /** - * 优惠券类型名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java deleted file mode 100644 index beb530f0cd..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - - -/** - * 优惠券使用类型 - 优惠券使用类型类型 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponUseLimitEnum { - - HAS_LIMIT(1,"无门槛"), - NO_LIMIT(2,"有门槛"),; - - /** - * 优惠券使用类型 - */ - private final Integer type; - /** - * 优惠券使用类型名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java deleted file mode 100644 index 798cf1e0f5..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * 过期类型 - 状态 - * - * @author Sin - */ -@RequiredArgsConstructor -@Getter -public enum CouponValidityTypeEnum { - - TIME_RANGE_EXPIRTED(1,"时间范围过期"), - EXPIRES_AFTER_FIXED_DATE(2,"领取之日固定日期后过期"), - EXPIRES_DATE_NEXT_FIEXD_DATE(3,"领取次日固定日期后过期"),; - - - /** - * 状态值 - */ - private final Integer status; - /** - * 状态名 - */ - private final String name; - -} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java deleted file mode 100644 index fced9d1401..0000000000 --- a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.CouponTemplete.enums; - -import cn.iocoder.yudao.framework.common.exception.ErrorCode; - -/** - * coupon 优惠券错误码枚举类 - * - * coupon 优惠券系统,使用 1-010-000-000 段 - */ -public interface ErrorCodeConstants { - // ========== COUPON分类相关 1010001000 ============ - - ErrorCode COUPON_TEMPLETE_NOT_EXISTS = new ErrorCode(1010001000, "优惠券模板不存在"); - ErrorCode MONEY_NOT_NULL = new ErrorCode(1010001001, "当type为reward时需要添加发放面额不能为空"); - ErrorCode DISCOUNT_NOT_NULL = new ErrorCode(1010001001, "当type为discount时需要添加折扣不能为空"); - ErrorCode DISCOUNT_LIMIT_NOT_NULL = new ErrorCode(1010001001, "当type为discount时可选择性添加最多折扣金额不能为空"); - ErrorCode MIN_MAX_NOT_NULL = new ErrorCode(1010001001, "当type为radom时需要添加最低金额"); - ErrorCode START_END_TIME_NOT_NULL = new ErrorCode(1010001001, "使用开始日期,使用结束日期不能为空"); - ErrorCode FIXED_TERM_NOT_NULL = new ErrorCode(1010001001, "领取之日起或者次日N天内有效不能为空"); - - - // ========== COUPON分类相关 1010002000 ============ - ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1010001000, "优惠券模板不存在"); - - -} - diff --git a/yudao-module-mall/yudao-module-coupon-biz/pom.xml b/yudao-module-mall/yudao-module-coupon-biz/pom.xml deleted file mode 100644 index 225c24d4e0..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-mall - ${revision} - - 4.0.0 - jar - yudao-module-coupon-biz - - ${project.artifactId} - - - coupon模块,主要负责优惠券的一些业务,含发布优惠券模板,分发优惠券等 - - - - - - cn.iocoder.boot - yudao-module-coupon-api - ${revision} - - - - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - - - - - cn.iocoder.boot - yudao-spring-boot-starter-web - - - cn.iocoder.boot - yudao-spring-boot-starter-security - - - - - cn.iocoder.boot - yudao-spring-boot-starter-mybatis - - - - - cn.iocoder.boot - yudao-spring-boot-starter-test - - - - - cn.iocoder.boot - yudao-spring-boot-starter-excel - - - - - diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java deleted file mode 100644 index 8ee02c3661..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.annotations.*; - -import javax.validation.*; -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - - -import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; -import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; -import cn.iocoder.yudao.module.coupon.convert.coupon.CouponConvert; -import cn.iocoder.yudao.module.coupon.service.coupon.CouponService; - -@Api(tags = "管理后台 - 优惠券") -@RestController -@RequestMapping("/coupon/item") -@Validated -public class CouponController { - - @Resource - private CouponService couponService; - - - //todo 用户优惠券 - @PostMapping("/create") - @ApiOperation("用户领取优惠券") - @PreAuthorize("@ss.hasPermission('coupon::create')") - public CommonResult create(@RequestParam("couponTemplateId") Long couponTemplateId) { - - return success(couponService.create(couponTemplateId)); - } - - - @PutMapping("/update") - @ApiOperation("更新优惠券") - @PreAuthorize("@ss.hasPermission('coupon::update')") - public CommonResult update(@Valid @RequestBody CouponUpdateReqVO updateReqVO) { - couponService.update(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @ApiOperation("删除优惠券") - @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('coupon::delete')") - public CommonResult delete(@RequestParam("id") Long id) { - couponService.delete(id); - return success(true); - } - - @GetMapping("/get") - @ApiOperation("获得优惠券") - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('coupon::query')") - public CommonResult get(@RequestParam("id") Long id) { - CouponDO couponDO = couponService.get(id); - return success(CouponConvert.INSTANCE.convert(couponDO)); - } - - - - @GetMapping("/list") - @ApiOperation("获得优惠券列表") - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) - @PreAuthorize("@ss.hasPermission('coupon::query')") - public CommonResult> getList(@RequestParam("ids") Collection ids) { - List list = couponService.getList(ids); - return success(CouponConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @ApiOperation("获得优惠券分页") - @PreAuthorize("@ss.hasPermission('coupon::query')") - public CommonResult> getPage(@Valid CouponPageReqVO pageVO) { - PageResult pageResult = couponService.getPage(pageVO); - return success(CouponConvert.INSTANCE.convertPage(pageResult)); - } - - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java deleted file mode 100644 index 71dc22305a..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; -import io.swagger.annotations.*; -import javax.validation.constraints.*; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -/** -* 优惠券 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ -@Data -public class CouponBaseVO { - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机", required = true) - @NotNull(message = "优惠券类型 reward-满减 discount-折扣 random-随机不能为空") - private String type; - - @ApiModelProperty(value = "优惠券名称", required = true) - @NotNull(message = "优惠券名称不能为空") - private String name; - - @ApiModelProperty(value = "优惠券类型id") - private Long couponTypeId; - - @ApiModelProperty(value = "优惠券编码", required = true) - @NotNull(message = "优惠券编码不能为空") - private String couponCode; - - @ApiModelProperty(value = "领用人", required = true) - @NotNull(message = "领用人不能为空") - private Long memberId; - - @ApiModelProperty(value = "优惠券使用订单id", required = true) - @NotNull(message = "优惠券使用订单id不能为空") - private Long useOrderId; - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用", required = true) - @NotNull(message = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用不能为空") - private Boolean goodsType; - - @ApiModelProperty(value = "适用商品id", required = true) - @NotNull(message = "适用商品id不能为空") - private String goodsIds; - - @ApiModelProperty(value = "最小金额", required = true) - @NotNull(message = "最小金额不能为空") - private BigDecimal atLeast; - - @ApiModelProperty(value = "面额", required = true) - @NotNull(message = "面额不能为空") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加", required = true) - @NotNull(message = "1 =< 折扣 <= 9.9 当type为discount时需要添加不能为空") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加", required = true) - @NotNull(message = "最多折扣金额 当type为discount时可选择性添加不能为空") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用", required = true) - @NotNull(message = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用不能为空") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启", required = true) - @NotNull(message = "是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒", required = true) - @NotNull(message = "过期前N天提醒不能为空") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "是否已提醒", required = true) - @NotNull(message = "是否已提醒不能为空") - private Boolean whetherNoticed; - - @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期", required = true) - @NotNull(message = "优惠券状态 1已领用(未使用) 2已使用 3已过期不能为空") - private Integer state; - - @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取", required = true) - @NotNull(message = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取不能为空") - private Boolean getType; - - @ApiModelProperty(value = "领取时间", required = true) - @NotNull(message = "领取时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime fetchTime; - - @ApiModelProperty(value = "使用时间", required = true) - @NotNull(message = "使用时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime useTime; - - @ApiModelProperty(value = "可使用的开始时间", required = true) - @NotNull(message = "可使用的开始时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime startTime; - - @ApiModelProperty(value = "有效期结束时间", required = true) - @NotNull(message = "有效期结束时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime endTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java deleted file mode 100644 index 2cd3cb4198..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 优惠券创建 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponCreateReqVO extends CouponBaseVO { - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java deleted file mode 100644 index 20a2b5a657..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; - -import com.alibaba.excel.annotation.ExcelProperty; - -/** - * 优惠券 Excel VO - * - * @author wxr - */ -@Data -public class CouponExcelVO { - - @ExcelProperty("用户ID") - private Long id; - - @ExcelProperty("优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ExcelProperty("优惠券名称") - private String name; - - @ExcelProperty("优惠券类型id") - private Long couponTypeId; - - @ExcelProperty("优惠券编码") - private String couponCode; - - @ExcelProperty("领用人") - private Long memberId; - - @ExcelProperty("优惠券使用订单id") - private Long useOrderId; - - @ExcelProperty("适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Boolean goodsType; - - @ExcelProperty("适用商品id") - private String goodsIds; - - @ExcelProperty("最小金额") - private BigDecimal atLeast; - - @ExcelProperty("面额") - private BigDecimal money; - - @ExcelProperty("1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ExcelProperty("最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ExcelProperty("优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ExcelProperty("是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ExcelProperty("过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ExcelProperty("是否已提醒") - private Boolean whetherNoticed; - - @ExcelProperty("优惠券状态 1已领用(未使用) 2已使用 3已过期") - private Integer state; - - @ExcelProperty("获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") - private Boolean getType; - - @ExcelProperty("领取时间") - private LocalDateTime fetchTime; - - @ExcelProperty("使用时间") - private LocalDateTime useTime; - - @ExcelProperty("可使用的开始时间") - private LocalDateTime startTime; - - @ExcelProperty("有效期结束时间") - private LocalDateTime endTime; - - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java deleted file mode 100644 index 6065a4b594..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import io.swagger.annotations.*; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 优惠券 Excel 导出 Request VO", description = "参数和 CouponPageReqVO 是一致的") -@Data -public class CouponExportReqVO { - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ApiModelProperty(value = "优惠券名称") - private String name; - - @ApiModelProperty(value = "优惠券类型id") - private Long couponTypeId; - - @ApiModelProperty(value = "优惠券编码") - private String couponCode; - - @ApiModelProperty(value = "领用人") - private Long memberId; - - @ApiModelProperty(value = "优惠券使用订单id") - private Long useOrderId; - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Boolean goodsType; - - @ApiModelProperty(value = "适用商品id") - private String goodsIds; - - @ApiModelProperty(value = "最小金额") - private BigDecimal atLeast; - - @ApiModelProperty(value = "面额") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "是否已提醒") - private Boolean whetherNoticed; - - @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期") - private Integer state; - - @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") - private Boolean getType; - - @ApiModelProperty(value = "领取时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] fetchTime; - - @ApiModelProperty(value = "使用时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] useTime; - - @ApiModelProperty(value = "可使用的开始时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] startTime; - - @ApiModelProperty(value = "有效期结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @ApiModelProperty(value = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java deleted file mode 100644 index 1b563d9f2a..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel("管理后台 - 优惠券分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponPageReqVO extends PageParam { - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ApiModelProperty(value = "优惠券名称") - private String name; - - @ApiModelProperty(value = "优惠券类型id") - private Long couponTypeId; - - @ApiModelProperty(value = "优惠券编码") - private String couponCode; - - @ApiModelProperty(value = "领用人") - private Long memberId; - - @ApiModelProperty(value = "优惠券使用订单id") - private Long useOrderId; - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Boolean goodsType; - - @ApiModelProperty(value = "适用商品id") - private String goodsIds; - - @ApiModelProperty(value = "最小金额") - private BigDecimal atLeast; - - @ApiModelProperty(value = "面额") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "是否已提醒") - private Boolean whetherNoticed; - - @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期") - private Integer state; - - @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") - private Boolean getType; - - @ApiModelProperty(value = "领取时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] fetchTime; - - @ApiModelProperty(value = "使用时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] useTime; - - @ApiModelProperty(value = "可使用的开始时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] startTime; - - @ApiModelProperty(value = "有效期结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @ApiModelProperty(value = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java deleted file mode 100644 index 8f9a5c42fc..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import io.swagger.annotations.*; - -@ApiModel("管理后台 - 优惠券 Response VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponRespVO extends CouponBaseVO { - - @ApiModelProperty(value = "用户ID", required = true) - private Long id; - - @ApiModelProperty(value = "创建时间", required = true) - private LocalDateTime createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java deleted file mode 100644 index 0c1a741ca8..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 优惠券更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponUpdateReqVO extends CouponBaseVO { - - @ApiModelProperty(value = "用户ID", required = true) - @NotNull(message = "用户ID不能为空") - private Long id; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java deleted file mode 100644 index a81f53c092..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete; - -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.*; -import cn.iocoder.yudao.module.coupon.convert.CouponTemplete.CouponTempleteConvert; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; -import cn.iocoder.yudao.module.coupon.service.CouponTemplete.CouponTempleteService; -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.annotations.*; - -import javax.validation.*; -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Api(tags = "管理后台 - 优惠券模板") -@RestController -@RequestMapping("/coupon/template") -@Validated -public class CouponTempleteController { - - @Resource - private CouponTempleteService couponTempleteServiceService; - - @PostMapping("/create") - @ApiOperation("创建优惠券模板") - @PreAuthorize("@ss.hasPermission('CouponTemplete::create')") - public CommonResult create(@Valid @RequestBody CouponTempleteCreateReqVO createReqVO) { - return success(couponTempleteServiceService.create(createReqVO)); - } - -// @PutMapping("/update") -// @ApiOperation("更新优惠券模板") -// @PreAuthorize("@ss.hasPermission('CouponTemplete::update')") -// public CommonResult update(@Valid @RequestBody CouponTempleteUpdateReqVO updateReqVO) { -// couponTempleteServiceService.update(updateReqVO); -// return success(true); -// } -// -// @DeleteMapping("/delete") -// @ApiOperation("删除优惠券模板") -// @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) -// @PreAuthorize("@ss.hasPermission('CouponTemplete::delete')") -// public CommonResult delete(@RequestParam("id") Long id) { -// couponTempleteServiceService.delete(id); -// return success(true); -// } -// -// @GetMapping("/get") -// @ApiOperation("获得优惠券模板") -// @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) -// @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") -// public CommonResult get(@RequestParam("id") Long id) { -// CouponTempleteDO couponTempleteDO = couponTempleteServiceService.get(id); -// return success(CouponTempleteConvert.INSTANCE.convert(couponTempleteDO)); -// } -// -// @GetMapping("/list") -// @ApiOperation("获得优惠券模板列表") -// @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) -// @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") -// public CommonResult> getList(@RequestParam("ids") Collection ids) { -// List list = couponTempleteServiceService.getList(ids); -// return success(CouponTempleteConvert.INSTANCE.convertList(list)); -// } -// - @GetMapping("/page") - @ApiOperation("获得优惠券模板分页") - @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") - public CommonResult> getPage(@Valid CouponTempletePageReqVO pageVO) { - PageResult pageResult = couponTempleteServiceService.getPage(pageVO); - return success(CouponTempleteConvert.INSTANCE.convertPage(pageResult)); - } - - - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java deleted file mode 100644 index eb35587e95..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java +++ /dev/null @@ -1,173 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; -import io.swagger.annotations.*; -import javax.validation.constraints.*; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -/** -* 优惠券模板 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ -@Data -public class CouponTempleteBaseVO { - - - //基本信息 - - @ApiModelProperty(value = "优惠券名称", required = true) - @NotNull(message = "优惠券名称不能为空") - private String name; - - @ApiModelProperty(value = "名称备注") - private String couponNameRemark; - - @ApiModelProperty(value = "优惠券图片") - private String image; - - /* ============判断适用商品——开始============= */ - - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用", required = true) - @NotNull(message = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用不能为空") - private Integer goodsType; - - @ApiModelProperty(value = "适用商品id") - private String productIds; - - @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛", required = true) - @NotNull(message = "使用门槛0-无门槛 1-有门槛不能为空") - private Boolean hasUseLimit; - - @ApiModelProperty(value = "满多少元使用 0代表无限制", required = true) - @NotNull(message = "满多少元使用 0代表无限制不能为空") - private BigDecimal atLeast; - - - /* ============折扣类型——开始============= */ - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机", required = true) - @NotNull(message = "优惠券类型 reward-满减 discount-折扣 random-随机不能为空") - private String type; - - @ApiModelProperty(value = "发放面额 当type为reward时需要添加") - @NotNull(message = "发放面额 当type为reward时需要添加不能为空") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") - @NotNull(message = "1 =< 折扣 <= 9.9 当type为discount时需要添加不能为空") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") - @NotNull(message = "最多折扣金额 当type为discount时可选择性添加不能为空") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "最低金额 当type为radom时需要添加", required = true) - @NotNull(message = "最低金额 当type为radom时需要添加不能为空") - private BigDecimal minMoney; - - @ApiModelProperty(value = "最大金额 当type为radom时需要添加", required = true) - @NotNull(message = "最大金额 当type为radom时需要添加不能为空") - private BigDecimal maxMoney; - - /* ============折扣类型——结束============= */ - - - /* ============过期类型——开始============= */ - - - @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期", required = true) - @NotNull(message = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期 不能为空") - private Integer validityType; - - @ApiModelProperty(value = "使用开始日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime startUseTime; - - @ApiModelProperty(value = "使用结束日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime endUseTime; - - @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") - @NotNull(message = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效不能为空") - private Integer fixedTerm; - - @ApiModelProperty(value = "有效日期结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime endTime; - - /* ============过期类型——结束============= */ - - - @ApiModelProperty(value = "领取是否无限制0-否 1是", required = true) - @NotNull(message = "是否无限制0-否 1是") - private Boolean whetherLimitless; - - @ApiModelProperty(value = "每人最大领取个数", required = true) - @NotNull(message = "每人最大领取个数不能为空") - private Integer maxFetch; - - @ApiModelProperty(value = "是否开启过期提醒 0-不开启 1-开启", required = true) - @NotNull(message = "是否开启过期提醒0-不开启 1-开启不能为空") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒", required = true) - @NotNull(message = "过期前N天提醒不能为空") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用", required = true) - @NotNull(message = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用不能为空") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否显示", required = true) - @NotNull(message = "是否显示不能为空") - private Integer whetherShow; - - @ApiModelProperty(value = "是否禁止发放0-否 1-是", required = true) - @NotNull(message = "是否禁止发放0-否 1-是不能为空") - private Boolean whetherForbidden; - - /* ============汇总计算——开始============= */ - - - - @ApiModelProperty(value = "使用优惠券购买的商品数量", required = true) - @NotNull(message = "使用优惠券购买的商品数量不能为空") - private Integer orderGoodsNum; - - @ApiModelProperty(value = "订单的优惠总金额", required = true) - @NotNull(message = "订单的优惠总金额不能为空") - private BigDecimal discountOrderMoney; - - @ApiModelProperty(value = "用券总成交额", required = true) - @NotNull(message = "用券总成交额不能为空") - private BigDecimal orderMoney; - - @ApiModelProperty(value = "发放数量", required = true) - @NotNull(message = "发放数量不能为空") - private Integer count; - - @ApiModelProperty(value = "已领取数量", required = true) - @NotNull(message = "已领取数量不能为空") - private Integer leadCount; - - @ApiModelProperty(value = "已使用数量", required = true) - @NotNull(message = "已使用数量不能为空") - private Integer usedCount; - - - /* ============汇总计算——结束============= */ - - - @ApiModelProperty(value = "状态(1进行中2已结束3已关闭)", required = true) - @NotNull(message = "状态(1进行中2已结束-1已关闭)不能为空") - private Integer status; - - - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java deleted file mode 100644 index 8e1d9a51a6..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; -import io.swagger.annotations.*; - -@ApiModel("管理后台 - 优惠券模板创建 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponTempleteCreateReqVO extends CouponTempleteBaseVO { - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java deleted file mode 100644 index 39abc69624..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java +++ /dev/null @@ -1,120 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; - -import com.alibaba.excel.annotation.ExcelProperty; - -/** - * 优惠券模板 Excel VO - * - * @author wxr - */ -@Data -public class CouponTempleteExcelVO { - - @ExcelProperty("用户ID") - private Long id; - - @ExcelProperty("优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ExcelProperty("优惠券名称") - private String name; - - @ExcelProperty("名称备注") - private String couponNameRemark; - - @ExcelProperty("优惠券图片") - private String image; - - @ExcelProperty("发放数量") - private Integer count; - - @ExcelProperty("已领取数量") - private Integer leadCount; - - @ExcelProperty("已使用数量") - private Integer usedCount; - - @ExcelProperty("适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Integer goodsType; - - @ExcelProperty("适用商品id") - private String productIds; - - @ExcelProperty("使用门槛0-无门槛 1-有门槛") - private Boolean hasUseLimit; - - @ExcelProperty("满多少元使用 0代表无限制") - private BigDecimal atLeast; - - @ExcelProperty("发放面额 当type为reward时需要添加") - private BigDecimal money; - - @ExcelProperty("1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ExcelProperty("最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ExcelProperty("最低金额 当type为radom时需要添加") - private BigDecimal minMoney; - - @ExcelProperty("最大金额 当type为radom时需要添加") - private BigDecimal maxMoney; - - @ExcelProperty("过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") - private Integer validityType; - - @ExcelProperty("使用开始日期 过期类型1时必填") - private LocalDateTime startUseTime; - - @ExcelProperty("使用结束日期 过期类型1时必填") - private LocalDateTime endUseTime; - - @ExcelProperty("当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") - private Integer fixedTerm; - - @ExcelProperty("是否无限制0-否 1是") - private Boolean whetherLimitless; - - @ExcelProperty("每人最大领取个数") - private Integer maxFetch; - - @ExcelProperty("是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ExcelProperty("过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ExcelProperty("优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ExcelProperty("是否显示") - private Integer whetherShow; - - @ExcelProperty("订单的优惠总金额") - private BigDecimal discountOrderMoney; - - @ExcelProperty("用券总成交额") - private BigDecimal orderMoney; - - @ExcelProperty("是否禁止发放0-否 1-是") - private Boolean whetherForbidden; - - @ExcelProperty("使用优惠券购买的商品数量") - private Integer orderGoodsNum; - - @ExcelProperty("状态(1进行中2已结束-1已关闭)") - private Integer status; - - @ExcelProperty("有效日期结束时间") - private LocalDateTime endTime; - - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java deleted file mode 100644 index 23edb8adf7..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java +++ /dev/null @@ -1,119 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import io.swagger.annotations.*; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 优惠券模板 Excel 导出 Request VO", description = "参数和 CouponTempletePageReqVO 是一致的") -@Data -public class CouponTempleteExportReqVO { - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ApiModelProperty(value = "优惠券名称") - private String name; - - @ApiModelProperty(value = "名称备注") - private String couponNameRemark; - - @ApiModelProperty(value = "优惠券图片") - private String image; - - @ApiModelProperty(value = "发放数量") - private Integer count; - - @ApiModelProperty(value = "已领取数量") - private Integer leadCount; - - @ApiModelProperty(value = "已使用数量") - private Integer usedCount; - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Boolean goodsType; - - @ApiModelProperty(value = "适用商品id") - private String productIds; - - @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛") - private Boolean hasUseLimit; - - @ApiModelProperty(value = "满多少元使用 0代表无限制") - private BigDecimal atLeast; - - @ApiModelProperty(value = "发放面额 当type为reward时需要添加") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "最低金额 当type为radom时需要添加") - private BigDecimal minMoney; - - @ApiModelProperty(value = "最大金额 当type为radom时需要添加") - private BigDecimal maxMoney; - - @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") - private Boolean validityType; - - @ApiModelProperty(value = "使用开始日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] startUseTime; - - @ApiModelProperty(value = "使用结束日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endUseTime; - - @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") - private Integer fixedTerm; - - @ApiModelProperty(value = "是否无限制0-否 1是") - private Boolean whetherLimitless; - - @ApiModelProperty(value = "每人最大领取个数") - private Integer maxFetch; - - @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否显示") - private Integer whetherShow; - - @ApiModelProperty(value = "订单的优惠总金额") - private BigDecimal discountOrderMoney; - - @ApiModelProperty(value = "用券总成交额") - private BigDecimal orderMoney; - - @ApiModelProperty(value = "是否禁止发放0-否 1-是") - private Boolean whetherForbidden; - - @ApiModelProperty(value = "使用优惠券购买的商品数量") - private Integer orderGoodsNum; - - @ApiModelProperty(value = "状态(1进行中2已结束-1已关闭)") - private Integer status; - - @ApiModelProperty(value = "有效日期结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @ApiModelProperty(value = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java deleted file mode 100644 index b66c24b143..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java +++ /dev/null @@ -1,122 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel("管理后台 - 优惠券模板分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponTempletePageReqVO extends PageParam { - - @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") - private String type; - - @ApiModelProperty(value = "优惠券名称") - private String name; - - @ApiModelProperty(value = "名称备注") - private String couponNameRemark; - - @ApiModelProperty(value = "优惠券图片") - private String image; - - @ApiModelProperty(value = "发放数量") - private Integer count; - - @ApiModelProperty(value = "已领取数量") - private Integer leadCount; - - @ApiModelProperty(value = "已使用数量") - private Integer usedCount; - - @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") - private Boolean goodsType; - - @ApiModelProperty(value = "适用商品id") - private String productIds; - - @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛") - private Boolean hasUseLimit; - - @ApiModelProperty(value = "满多少元使用 0代表无限制") - private BigDecimal atLeast; - - @ApiModelProperty(value = "发放面额 当type为reward时需要添加") - private BigDecimal money; - - @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") - private BigDecimal discount; - - @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") - private BigDecimal discountLimit; - - @ApiModelProperty(value = "最低金额 当type为radom时需要添加") - private BigDecimal minMoney; - - @ApiModelProperty(value = "最大金额 当type为radom时需要添加") - private BigDecimal maxMoney; - - @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") - private Boolean validityType; - - @ApiModelProperty(value = "使用开始日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] startUseTime; - - @ApiModelProperty(value = "使用结束日期 过期类型1时必填") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endUseTime; - - @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") - private Integer fixedTerm; - - @ApiModelProperty(value = "是否无限制0-否 1是") - private Boolean whetherLimitless; - - @ApiModelProperty(value = "每人最大领取个数") - private Integer maxFetch; - - @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") - private Boolean whetherExpireNotice; - - @ApiModelProperty(value = "过期前N天提醒") - private Integer expireNoticeFixedTerm; - - @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") - private Boolean whetherForbidPreference; - - @ApiModelProperty(value = "是否显示") - private Integer whetherShow; - - @ApiModelProperty(value = "订单的优惠总金额") - private BigDecimal discountOrderMoney; - - @ApiModelProperty(value = "用券总成交额") - private BigDecimal orderMoney; - - @ApiModelProperty(value = "是否禁止发放0-否 1-是") - private Boolean whetherForbidden; - - @ApiModelProperty(value = "使用优惠券购买的商品数量") - private Integer orderGoodsNum; - - @ApiModelProperty(value = "状态(1进行中2已结束-1已关闭)") - private Integer status; - - @ApiModelProperty(value = "有效日期结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @ApiModelProperty(value = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java deleted file mode 100644 index 0b14dbb6b7..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; - -import java.time.LocalDateTime; -import io.swagger.annotations.*; - -@ApiModel("管理后台 - 优惠券模板 Response VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponTempleteRespVO extends CouponTempleteBaseVO { - - @ApiModelProperty(value = "用户ID", required = true) - private Long id; - - @ApiModelProperty(value = "创建时间", required = true) - private LocalDateTime createTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java deleted file mode 100644 index 1ee8b539d6..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; - -import lombok.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 优惠券模板更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CouponTempleteUpdateReqVO extends CouponTempleteBaseVO { - - @ApiModelProperty(value = "用户ID", required = true) - @NotNull(message = "用户ID不能为空") - private Long id; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java deleted file mode 100644 index a7f37919da..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.coupon.convert.CouponTemplete; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteCreateReqVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExcelVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteRespVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteUpdateReqVO; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - - -/** - * 优惠券模板 Convert - * - * @author wxr - */ -@Mapper -public interface CouponTempleteConvert { - - CouponTempleteConvert INSTANCE = Mappers.getMapper(CouponTempleteConvert.class); - - CouponTempleteDO convert(CouponTempleteCreateReqVO bean); - - CouponTempleteDO convert(CouponTempleteUpdateReqVO bean); - - CouponTempleteRespVO convert(CouponTempleteDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - List convertList02(List list); - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java deleted file mode 100644 index e4dab938b2..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.coupon.convert.coupon; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; -import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; - -/** - * 优惠券 Convert - * - * @author wxr - */ -@Mapper -public interface CouponConvert { - - CouponConvert INSTANCE = Mappers.getMapper(CouponConvert.class); - - CouponDO convert(CouponCreateReqVO bean); - - CouponDO convert(CouponUpdateReqVO bean); - - CouponRespVO convert(CouponDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - List convertList02(List list); - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java deleted file mode 100644 index 4bb6bf8786..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java +++ /dev/null @@ -1,159 +0,0 @@ -package cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete; - -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 优惠券模板 DO - * - * @author wxr - */ -@TableName("coupon_templete") -@KeySequence("coupon_templete_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class CouponTempleteDO extends BaseDO { - - /** - * 用户ID - */ - @TableId - private Long id; - /** - * 优惠券类型 reward-满减 discount-折扣 random-随机 - */ - private String type; - /** - * 优惠券名称 - */ - private String name; - /** - * 名称备注 - */ - private String couponNameRemark; - /** - * 优惠券图片 - */ - private String image; - /** - * 发放数量 - */ - private Integer count; - /** - * 已领取数量 - */ - private Integer leadCount; - /** - * 已使用数量 - */ - private Integer usedCount; - /** - * 适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用 - */ - private Integer goodsType; - /** - * 适用商品id - */ - private String productIds; - /** - * 使用门槛0-无门槛 1-有门槛 - */ - private Boolean hasUseLimit; - /** - * 满多少元使用 0代表无限制 - */ - private BigDecimal atLeast; - /** - * 发放面额 当type为reward时需要添加 - */ - private BigDecimal money; - /** - * 1 =< 折扣 <= 9.9 当type为discount时需要添加 - */ - private BigDecimal discount; - /** - * 最多折扣金额 当type为discount时可选择性添加 - */ - private BigDecimal discountLimit; - /** - * 最低金额 当type为radom时需要添加 - */ - private BigDecimal minMoney; - /** - * 最大金额 当type为radom时需要添加 - */ - private BigDecimal maxMoney; - /** - * 过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期 - */ - private Integer validityType; - /** - * 使用开始日期 过期类型1时必填 - */ - private LocalDateTime startUseTime; - /** - * 使用结束日期 过期类型1时必填 - */ - private LocalDateTime endUseTime; - /** - * 当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效 - */ - private Integer fixedTerm; - /** - * 是否无限制0-否 1是 - */ - private Boolean whetherLimitless; - /** - * 每人最大领取个数 - */ - private Integer maxFetch; - /** - * 是否开启过期提醒0-不开启 1-开启 - */ - private Boolean whetherExpireNotice; - /** - * 过期前N天提醒 - */ - private Integer expireNoticeFixedTerm; - /** - * 优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用 - */ - private Boolean whetherForbidPreference; - /** - * 是否显示 - */ - private Integer whetherShow; - /** - * 订单的优惠总金额 - */ - private BigDecimal discountOrderMoney; - /** - * 用券总成交额 - */ - private BigDecimal orderMoney; - /** - * 是否禁止发放0-否 1-是 - */ - private Boolean whetherForbidden; - /** - * 使用优惠券购买的商品数量 - */ - private Integer orderGoodsNum; - /** - * 状态(1进行中2已结束-1已关闭) - */ - private Integer status; - /** - * 有效日期结束时间 - */ - private LocalDateTime endTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java deleted file mode 100644 index 4e6f2916b1..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java +++ /dev/null @@ -1,119 +0,0 @@ -package cn.iocoder.yudao.module.coupon.dal.dataobject.coupon; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import lombok.*; - -import java.time.LocalDateTime; -import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.*; - -/** - * 优惠券 DO - * - * @author wxr - */ -@TableName("coupon") -@KeySequence("coupon_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class CouponDO extends BaseDO { - - /** - * 用户ID - */ - @TableId - private Long id; - /** - * 优惠券类型 reward-满减 discount-折扣 random-随机 - */ - private String type; - /** - * 优惠券名称 - */ - private String name; - /** - * 优惠券类型id - */ - private Long couponTypeId; - /** - * 优惠券编码 - */ - private String couponCode; - /** - * 领用人 - */ - private Long memberId; - /** - * 优惠券使用订单id - */ - private Long useOrderId; - /** - * 适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用 - */ - private Boolean goodsType; - /** - * 适用商品id - */ - private String goodsIds; - /** - * 最小金额 - */ - private BigDecimal atLeast; - /** - * 面额 - */ - private BigDecimal money; - /** - * 1 =< 折扣 <= 9.9 当type为discount时需要添加 - */ - private BigDecimal discount; - /** - * 最多折扣金额 当type为discount时可选择性添加 - */ - private BigDecimal discountLimit; - /** - * 优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用 - */ - private Boolean whetherForbidPreference; - /** - * 是否开启过期提醒0-不开启 1-开启 - */ - private Boolean whetherExpireNotice; - /** - * 过期前N天提醒 - */ - private Integer expireNoticeFixedTerm; - /** - * 是否已提醒 - */ - private Boolean whetherNoticed; - /** - * 优惠券状态 1已领用(未使用) 2已使用 3已过期 - */ - private Integer state; - /** - * 获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取 - */ - private Boolean getType; - /** - * 领取时间 - */ - private LocalDateTime fetchTime; - /** - * 使用时间 - */ - private LocalDateTime useTime; - /** - * 可使用的开始时间 - */ - private LocalDateTime startTime; - /** - * 有效期结束时间 - */ - private LocalDateTime endTime; - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java deleted file mode 100644 index 3243ce17a7..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java +++ /dev/null @@ -1,98 +0,0 @@ -package cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExportReqVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempletePageReqVO; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; -import org.apache.ibatis.annotations.Mapper; - - -/** - * 优惠券模板 Mapper - * - * @author wxr - */ -@Mapper -public interface CouponTempleteMapper extends BaseMapperX { - - default PageResult selectPage(CouponTempletePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(CouponTempleteDO::getType, reqVO.getType()) - .likeIfPresent(CouponTempleteDO::getName, reqVO.getName()) - .eqIfPresent(CouponTempleteDO::getCouponNameRemark, reqVO.getCouponNameRemark()) - .eqIfPresent(CouponTempleteDO::getImage, reqVO.getImage()) - .eqIfPresent(CouponTempleteDO::getCount, reqVO.getCount()) - .eqIfPresent(CouponTempleteDO::getLeadCount, reqVO.getLeadCount()) - .eqIfPresent(CouponTempleteDO::getUsedCount, reqVO.getUsedCount()) - .eqIfPresent(CouponTempleteDO::getGoodsType, reqVO.getGoodsType()) - .eqIfPresent(CouponTempleteDO::getProductIds, reqVO.getProductIds()) - .eqIfPresent(CouponTempleteDO::getHasUseLimit, reqVO.getHasUseLimit()) - .eqIfPresent(CouponTempleteDO::getAtLeast, reqVO.getAtLeast()) - .eqIfPresent(CouponTempleteDO::getMoney, reqVO.getMoney()) - .eqIfPresent(CouponTempleteDO::getDiscount, reqVO.getDiscount()) - .eqIfPresent(CouponTempleteDO::getDiscountLimit, reqVO.getDiscountLimit()) - .eqIfPresent(CouponTempleteDO::getMinMoney, reqVO.getMinMoney()) - .eqIfPresent(CouponTempleteDO::getMaxMoney, reqVO.getMaxMoney()) - .eqIfPresent(CouponTempleteDO::getValidityType, reqVO.getValidityType()) - .betweenIfPresent(CouponTempleteDO::getStartUseTime, reqVO.getStartUseTime()) - .betweenIfPresent(CouponTempleteDO::getEndUseTime, reqVO.getEndUseTime()) - .eqIfPresent(CouponTempleteDO::getFixedTerm, reqVO.getFixedTerm()) - .eqIfPresent(CouponTempleteDO::getWhetherLimitless, reqVO.getWhetherLimitless()) - .eqIfPresent(CouponTempleteDO::getMaxFetch, reqVO.getMaxFetch()) - .eqIfPresent(CouponTempleteDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) - .eqIfPresent(CouponTempleteDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) - .eqIfPresent(CouponTempleteDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) - .eqIfPresent(CouponTempleteDO::getWhetherShow, reqVO.getWhetherShow()) - .eqIfPresent(CouponTempleteDO::getDiscountOrderMoney, reqVO.getDiscountOrderMoney()) - .eqIfPresent(CouponTempleteDO::getOrderMoney, reqVO.getOrderMoney()) - .eqIfPresent(CouponTempleteDO::getWhetherForbidden, reqVO.getWhetherForbidden()) - .eqIfPresent(CouponTempleteDO::getOrderGoodsNum, reqVO.getOrderGoodsNum()) - .eqIfPresent(CouponTempleteDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(CouponTempleteDO::getEndTime, reqVO.getEndTime()) - .betweenIfPresent(CouponTempleteDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CouponTempleteDO::getId)); - } - - default List selectList(CouponTempleteExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(CouponTempleteDO::getType, reqVO.getType()) - .likeIfPresent(CouponTempleteDO::getName, reqVO.getName()) - .eqIfPresent(CouponTempleteDO::getCouponNameRemark, reqVO.getCouponNameRemark()) - .eqIfPresent(CouponTempleteDO::getImage, reqVO.getImage()) - .eqIfPresent(CouponTempleteDO::getCount, reqVO.getCount()) - .eqIfPresent(CouponTempleteDO::getLeadCount, reqVO.getLeadCount()) - .eqIfPresent(CouponTempleteDO::getUsedCount, reqVO.getUsedCount()) - .eqIfPresent(CouponTempleteDO::getGoodsType, reqVO.getGoodsType()) - .eqIfPresent(CouponTempleteDO::getProductIds, reqVO.getProductIds()) - .eqIfPresent(CouponTempleteDO::getHasUseLimit, reqVO.getHasUseLimit()) - .eqIfPresent(CouponTempleteDO::getAtLeast, reqVO.getAtLeast()) - .eqIfPresent(CouponTempleteDO::getMoney, reqVO.getMoney()) - .eqIfPresent(CouponTempleteDO::getDiscount, reqVO.getDiscount()) - .eqIfPresent(CouponTempleteDO::getDiscountLimit, reqVO.getDiscountLimit()) - .eqIfPresent(CouponTempleteDO::getMinMoney, reqVO.getMinMoney()) - .eqIfPresent(CouponTempleteDO::getMaxMoney, reqVO.getMaxMoney()) - .eqIfPresent(CouponTempleteDO::getValidityType, reqVO.getValidityType()) - .betweenIfPresent(CouponTempleteDO::getStartUseTime, reqVO.getStartUseTime()) - .betweenIfPresent(CouponTempleteDO::getEndUseTime, reqVO.getEndUseTime()) - .eqIfPresent(CouponTempleteDO::getFixedTerm, reqVO.getFixedTerm()) - .eqIfPresent(CouponTempleteDO::getWhetherLimitless, reqVO.getWhetherLimitless()) - .eqIfPresent(CouponTempleteDO::getMaxFetch, reqVO.getMaxFetch()) - .eqIfPresent(CouponTempleteDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) - .eqIfPresent(CouponTempleteDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) - .eqIfPresent(CouponTempleteDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) - .eqIfPresent(CouponTempleteDO::getWhetherShow, reqVO.getWhetherShow()) - .eqIfPresent(CouponTempleteDO::getDiscountOrderMoney, reqVO.getDiscountOrderMoney()) - .eqIfPresent(CouponTempleteDO::getOrderMoney, reqVO.getOrderMoney()) - .eqIfPresent(CouponTempleteDO::getWhetherForbidden, reqVO.getWhetherForbidden()) - .eqIfPresent(CouponTempleteDO::getOrderGoodsNum, reqVO.getOrderGoodsNum()) - .eqIfPresent(CouponTempleteDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(CouponTempleteDO::getEndTime, reqVO.getEndTime()) - .betweenIfPresent(CouponTempleteDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CouponTempleteDO::getId)); - } - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java deleted file mode 100644 index 8c90146945..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.iocoder.yudao.module.coupon.dal.mysql.coupon; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; - -/** - * 优惠券 Mapper - * - * @author wxr - */ -@Mapper -public interface CouponMapper extends BaseMapperX { - - default PageResult selectPage(CouponPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(CouponDO::getType, reqVO.getType()) - .likeIfPresent(CouponDO::getName, reqVO.getName()) - .eqIfPresent(CouponDO::getCouponTypeId, reqVO.getCouponTypeId()) - .eqIfPresent(CouponDO::getCouponCode, reqVO.getCouponCode()) - .eqIfPresent(CouponDO::getMemberId, reqVO.getMemberId()) - .eqIfPresent(CouponDO::getUseOrderId, reqVO.getUseOrderId()) - .eqIfPresent(CouponDO::getGoodsType, reqVO.getGoodsType()) - .eqIfPresent(CouponDO::getGoodsIds, reqVO.getGoodsIds()) - .eqIfPresent(CouponDO::getAtLeast, reqVO.getAtLeast()) - .eqIfPresent(CouponDO::getMoney, reqVO.getMoney()) - .eqIfPresent(CouponDO::getDiscount, reqVO.getDiscount()) - .eqIfPresent(CouponDO::getDiscountLimit, reqVO.getDiscountLimit()) - .eqIfPresent(CouponDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) - .eqIfPresent(CouponDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) - .eqIfPresent(CouponDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) - .eqIfPresent(CouponDO::getWhetherNoticed, reqVO.getWhetherNoticed()) - .eqIfPresent(CouponDO::getState, reqVO.getState()) - .eqIfPresent(CouponDO::getGetType, reqVO.getGetType()) - .betweenIfPresent(CouponDO::getFetchTime, reqVO.getFetchTime()) - .betweenIfPresent(CouponDO::getUseTime, reqVO.getUseTime()) - .betweenIfPresent(CouponDO::getStartTime, reqVO.getStartTime()) - .betweenIfPresent(CouponDO::getEndTime, reqVO.getEndTime()) - .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CouponDO::getId)); - } - - default List selectList(CouponExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(CouponDO::getType, reqVO.getType()) - .likeIfPresent(CouponDO::getName, reqVO.getName()) - .eqIfPresent(CouponDO::getCouponTypeId, reqVO.getCouponTypeId()) - .eqIfPresent(CouponDO::getCouponCode, reqVO.getCouponCode()) - .eqIfPresent(CouponDO::getMemberId, reqVO.getMemberId()) - .eqIfPresent(CouponDO::getUseOrderId, reqVO.getUseOrderId()) - .eqIfPresent(CouponDO::getGoodsType, reqVO.getGoodsType()) - .eqIfPresent(CouponDO::getGoodsIds, reqVO.getGoodsIds()) - .eqIfPresent(CouponDO::getAtLeast, reqVO.getAtLeast()) - .eqIfPresent(CouponDO::getMoney, reqVO.getMoney()) - .eqIfPresent(CouponDO::getDiscount, reqVO.getDiscount()) - .eqIfPresent(CouponDO::getDiscountLimit, reqVO.getDiscountLimit()) - .eqIfPresent(CouponDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) - .eqIfPresent(CouponDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) - .eqIfPresent(CouponDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) - .eqIfPresent(CouponDO::getWhetherNoticed, reqVO.getWhetherNoticed()) - .eqIfPresent(CouponDO::getState, reqVO.getState()) - .eqIfPresent(CouponDO::getGetType, reqVO.getGetType()) - .betweenIfPresent(CouponDO::getFetchTime, reqVO.getFetchTime()) - .betweenIfPresent(CouponDO::getUseTime, reqVO.getUseTime()) - .betweenIfPresent(CouponDO::getStartTime, reqVO.getStartTime()) - .betweenIfPresent(CouponDO::getEndTime, reqVO.getEndTime()) - .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CouponDO::getId)); - } - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java deleted file mode 100644 index 6743fd1415..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * coupon模块,主要负责麦一些优惠券的额增删 - * - * 1. Controller URL:以 /coumon/ 开头,避免和其它 Module 冲突 - */ -package cn.iocoder.yudao.module.coupon; diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java deleted file mode 100644 index 0527d11b70..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.coupon.service.CouponTemplete; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.*; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; - -/** - * 优惠券模板 Service 接口 - * - * @author wxr - */ -public interface CouponTempleteService { - - /** - * 创建优惠券模板 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long create(@Valid CouponTempleteCreateReqVO createReqVO); - - /** - * 更新优惠券模板 - * - * @param updateReqVO 更新信息 - */ - void update(@Valid CouponTempleteUpdateReqVO updateReqVO); - - /** - * 删除优惠券模板 - * - * @param id 编号 - */ - void delete(Long id); - - /** - * 获得优惠券模板 - * - * @param id 编号 - * @return 优惠券模板 - */ - CouponTempleteDO get(Long id); - - /** - * 获得优惠券模板列表 - * - * @param ids 编号 - * @return 优惠券模板列表 - */ - List getList(Collection ids); - - /** - * 获得优惠券模板分页 - * - * @param pageReqVO 分页查询 - * @return 优惠券模板分页 - */ - PageResult getPage(CouponTempletePageReqVO pageReqVO); - - /** - * 获得优惠券模板列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 优惠券模板列表 - */ - List getList(CouponTempleteExportReqVO exportReqVO); - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java deleted file mode 100644 index 6f07c4b2af..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java +++ /dev/null @@ -1,134 +0,0 @@ -package cn.iocoder.yudao.module.coupon.service.CouponTemplete; - -import cn.iocoder.yudao.module.CouponTemplete.enums.CouponTypeEnum; -import cn.iocoder.yudao.module.CouponTemplete.enums.CouponValidityTypeEnum; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteCreateReqVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExportReqVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempletePageReqVO; -import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteUpdateReqVO; -import cn.iocoder.yudao.module.coupon.convert.CouponTemplete.CouponTempleteConvert; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; -import cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete.CouponTempleteMapper; -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; - -import java.util.*; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - - - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.CouponTemplete.enums.ErrorCodeConstants.*; - -/** - * 优惠券模板 Service 实现类 - * - * @author wxr - */ -@Service -@Validated -public class CouponTempleteServiceImpl implements CouponTempleteService { - - @Resource - private CouponTempleteMapper couponTempleteMapper; - - @Override - public Long create(CouponTempleteCreateReqVO createReqVO) { - // 插入 - CouponTempleteDO couponTempleteDO = CouponTempleteConvert.INSTANCE.convert(createReqVO); - /* 验证类型、判断必填*/ - checkCouponType(createReqVO); - - /*验证过期类型、判断必填*/ - checkValidityType(createReqVO); - - - - couponTempleteMapper.insert(couponTempleteDO); - // 返回 - return couponTempleteDO.getId(); - } - - /*确认优惠券类型*/ - private void checkValidityType(CouponTempleteCreateReqVO createReqVO) { - Integer validtyType = createReqVO.getValidityType(); - - if(CouponValidityTypeEnum.TIME_RANGE_EXPIRTED.getStatus().equals(validtyType)){ - if(createReqVO.getStartUseTime() == null||createReqVO.getEndUseTime() == null){ - throw exception(START_END_TIME_NOT_NULL); - } - }else{ - if(createReqVO.getFixedTerm() == null){ - throw exception(FIXED_TERM_NOT_NULL); - } - } - } - - private void checkCouponType(CouponTempleteCreateReqVO createReqVO) { - - String couponType = createReqVO.getType(); - //当type=reward时候,需要添加 - if(couponType.equals(CouponTypeEnum.REWARD.getName())){ - if(createReqVO.getMoney()==null){ - throw exception(MONEY_NOT_NULL); - } - }else if(couponType.equals(CouponTypeEnum.DISCOUNT.getName())){ - if(createReqVO.getDiscount()==null){ - throw exception(DISCOUNT_NOT_NULL); - } - if(createReqVO.getDiscountLimit()==null){ - throw exception(DISCOUNT_LIMIT_NOT_NULL); - } - }else if (couponType.equals(CouponTypeEnum.RANDOW.getName())){ - //当type为radom时需要添加不能为空 - if(createReqVO.getMinMoney()==null||createReqVO.getMaxMoney()==null){ - throw exception(MIN_MAX_NOT_NULL); - } - } - } - - @Override - public void update(CouponTempleteUpdateReqVO updateReqVO) { - // 校验存在 - this.validateExists(updateReqVO.getId()); - // 更新 - CouponTempleteDO updateObj = CouponTempleteConvert.INSTANCE.convert(updateReqVO); - couponTempleteMapper.updateById(updateObj); - } - - @Override - public void delete(Long id) { - // 校验存在 - this.validateExists(id); - // 删除 - couponTempleteMapper.deleteById(id); - } - - private void validateExists(Long id) { - if (couponTempleteMapper.selectById(id) == null) { - throw exception(COUPON_TEMPLETE_NOT_EXISTS); - } - } - - @Override - public CouponTempleteDO get(Long id) { - return couponTempleteMapper.selectById(id); - } - - @Override - public List getList(Collection ids) { - return couponTempleteMapper.selectBatchIds(ids); - } - - @Override - public PageResult getPage(CouponTempletePageReqVO pageReqVO) { - return couponTempleteMapper.selectPage(pageReqVO); - } - - @Override - public List getList(CouponTempleteExportReqVO exportReqVO) { - return couponTempleteMapper.selectList(exportReqVO); - } - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java deleted file mode 100644 index 6a9d31520c..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.coupon.service.coupon; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; -import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -/** - * 优惠券 Service 接口 - * - * @author wxr - */ -public interface CouponService { - - /** - * 创建优惠券 - * - * @param templateId 优惠券模板id - * @return 编号 - */ - Long create(Long templateId); - - /** - * 更新优惠券 - * - * @param updateReqVO 更新信息 - */ - void update(@Valid CouponUpdateReqVO updateReqVO); - - /** - * 删除优惠券 - * - * @param id 编号 - */ - void delete(Long id); - - /** - * 获得优惠券 - * - * @param id 编号 - * @return 优惠券 - */ - CouponDO get(Long id); - - /** - * 获得优惠券列表 - * - * @param ids 编号 - * @return 优惠券列表 - */ - List getList(Collection ids); - - /** - * 获得优惠券分页 - * - * @param pageReqVO 分页查询 - * @return 优惠券分页 - */ - PageResult getPage(CouponPageReqVO pageReqVO); - - /** - * 获得优惠券列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 优惠券列表 - */ - List getList(CouponExportReqVO exportReqVO); - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java deleted file mode 100644 index 14f3cacc48..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.iocoder.yudao.module.coupon.service.coupon; - -import cn.hutool.core.bean.BeanUtil; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; -import cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete.CouponTempleteMapper; -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; - -import java.util.*; -import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; -import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import cn.iocoder.yudao.module.coupon.convert.coupon.CouponConvert; -import cn.iocoder.yudao.module.coupon.dal.mysql.coupon.CouponMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.CouponTemplete.enums.ErrorCodeConstants.COUPON_NOT_EXISTS; - -/** - * 优惠券 Service 实现类 - * - * @author wxr - */ -@Service -@Validated -public class CouponServiceImpl implements CouponService { - - @Resource - private CouponMapper couponMapper; - - @Resource - private CouponTempleteMapper couponTempleteMapper; - - public Long create(CouponCreateReqVO createReqVO) { - // 插入 - CouponDO couponDO = CouponConvert.INSTANCE.convert(createReqVO); - couponMapper.insert(couponDO); - // 返回 - return couponDO.getId(); - } - - - /** - *todo 获取用户id收获优惠券 - *@author:wxr - *@date:2022-08-13 3:11 - *@Description - */ - @Override - public Long create(Long templateId) { - Long userid = SecurityFrameworkUtils.getLoginUserId(); - CouponDO couponDO = CouponDO.builder().memberId(userid).build(); - CouponTempleteDO couponTempleteDO = couponTempleteMapper.selectById(templateId); - //todo 缺少判空 - BeanUtil.copyProperties(couponTempleteDO,couponTempleteDO); - couponMapper.insert(couponDO); - return couponDO.getId(); - } - - @Override - public void update(CouponUpdateReqVO updateReqVO) { - // 校验存在 - this.validateExists(updateReqVO.getId()); - // 更新 - CouponDO updateObj = CouponConvert.INSTANCE.convert(updateReqVO); - couponMapper.updateById(updateObj); - } - - @Override - public void delete(Long id) { - // 校验存在 - this.validateExists(id); - // 删除 - couponMapper.deleteById(id); - } - - private void validateExists(Long id) { - if (couponMapper.selectById(id) == null) { - throw exception(COUPON_NOT_EXISTS); - } - } - - @Override - public CouponDO get(Long id) { - return couponMapper.selectById(id); - } - - @Override - public List getList(Collection ids) { - return couponMapper.selectBatchIds(ids); - } - - @Override - public PageResult getPage(CouponPageReqVO pageReqVO) { - return couponMapper.selectPage(pageReqVO); - } - - @Override - public List getList(CouponExportReqVO exportReqVO) { - return couponMapper.selectList(exportReqVO); - } - -} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml deleted file mode 100644 index b7db75a771..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml deleted file mode 100644 index a5e70cf5d0..0000000000 --- a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java deleted file mode 100644 index cb45004b6b..0000000000 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package cn.iocoder.yudao.module.market.api; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java deleted file mode 100644 index dc4f10a108..0000000000 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java +++ /dev/null @@ -1,202 +0,0 @@ -package cn.iocoder.yudao.module.market.api.price.dto; - -import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum; -import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum; -import lombok.Data; - -import java.util.List; - -/** - * 价格计算 Response DTO - * - * @author 芋道源码 - */ -@Data -public class PriceCalculateRespDTO { - - /** - * 订单 - */ - private Order order; - - /** - * 商品 SKU 数组 - */ - private List items; - - /** - * 营销活动数组 - * - * 只对应 {@link #items} 商品匹配的活动 - */ - private List promotions; - - /** - * 订单 - */ - @Data - public static class Order { - - /** - * 商品原价(总),单位:分 - * - * 基于 {@link Item#getTotalOriginalPrice()} 求和 - */ - private Integer skuOriginalPrice; - /** - * 商品优惠(总),单位:分 - * - * 基于 {@link Item#getTotalPromotionPrice()} 求和 - */ - private Integer skuPromotionPrice; - /** - * 订单优惠(总),单位:分 - * - * 例如说:满减折扣;不包括优惠劵、商品优惠 - */ - private Integer orderPromotionPrice; - /** - * 运费金额,单位:分 - */ - private Integer deliveryPrice; - /** - * 应付金额(总),单位:分 - * - * = {@link #skuOriginalPrice} - * + {@link #deliveryPrice} - * - {@link #skuPromotionPrice} - * - {@link #orderPromotionPrice} - */ - // * - {@link #couponPrice} // TODO 芋艿:靠营销表记录 - private Integer payPrice; - - // ========== 营销基本信息 ========== - /** - * 优惠劵编号 - */ - private Long couponId; -// /** -// * 优惠劵减免金额,单位:分 -// * -// * // TODO 芋艿:靠营销表记录 -// */ -// private Integer couponPrice; - - } - - /** - * 商品 SKU - */ - @Data - public static class Item extends PriceCalculateReqDTO.Item { - - /** - * 商品原价(单),单位:分 - * - * 对应 ProductSkuDO 的 price 字段 - */ - private Integer originalPrice; - /** - * 商品原价(总),单位:分 - * - * = {@link #originalPrice} * {@link #getCount()} - */ - private Integer totalOriginalPrice; - /** - * 商品级优惠(总),单位:分 - * - * 例如说“限时折扣”:商品原价的 8 折;商品原价的减 50 元 - */ - private Integer totalPromotionPrice; - /** - * 最终购买金额(总),单位:分。 - * - * = {@link #totalOriginalPrice} - * - {@link #totalPromotionPrice} - */ - private Integer totalPresentPrice; - /** - * 最终购买金额(单),单位:分。 - * - * = {@link #totalPresentPrice} / {@link #getCount()} - */ - private Integer presentPrice; - /** - * 应付金额(总),单位:分 - */ - private Integer totalPayPrice; - - } - - /** - * 营销活动 - */ - @Data - public static class Promotion { - - /** - * 营销编号 - * - * 例如说:营销活动的编号、优惠劵的编号 - */ - private Long id; - /** - * 营销类型 - * - * 枚举 {@link PromotionTypeEnum} - */ - private Integer type; - /** - * 营销级别 - * - * 枚举 {@link PromotionLevelEnum} - */ - private Integer level; - /** - * 匹配的商品 SKU 数组 - */ - private List items; - /** - * 计算时的原价(总),单位:分 - */ - private Integer totalOriginalPrice; - /** - * 计算时的优惠(总),单位:分 - */ - private Integer totalPromotionPrice; - /** - * 是否满足优惠条件 - */ - private Boolean meet; - /** - * 满足条件的提示 - * - * 如果 {@link #meet} = true 满足,则提示“圣诞价:省 150.00 元” - * 如果 {@link #meet} = false 不满足,则提示“购满 85 元,可减 40 元” - */ - private String meetTip; - - /** - * 匹配的商品 SKU - */ - @Data - public static class Item { - - /** - * 商品 SKU 编号 - */ - private Long skuId; - /** - * 计算时的原价(总),单位:分 - */ - private Integer totalOriginalPrice; - /** - * 计算时的优惠(总),单位:分 - */ - private Integer totalPromotionPrice; - - } - - } - -} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java deleted file mode 100644 index d63b524657..0000000000 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.market.enums; - -import cn.iocoder.yudao.framework.common.exception.ErrorCode; - -/** - * market 错误码枚举类 - *

- * market 系统,使用 1-003-000-000 段 - */ -public interface ErrorCodeConstants { - - - // ========== 促销活动相关 1003001000============ - ErrorCode ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "促销活动不存在"); - - // ========== banner相关 1003002000============ - ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner不存在"); -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java deleted file mode 100644 index a3f145f269..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * TODO 占位 - */ -package cn.iocoder.yudao.module.market.controller.admin.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java deleted file mode 100644 index 7874924065..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * TODO 占位 - */ -package cn.iocoder.yudao.module.market.convert.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java deleted file mode 100644 index f8de42cd9e..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * TODO 占位 - */ -package cn.iocoder.yudao.module.market.dal.mysql.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java deleted file mode 100644 index f52febd447..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * TODO 占位 - */ -package cn.iocoder.yudao.module.market.service.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java deleted file mode 100644 index 6b8663d1e6..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.market.service.price; - -import cn.iocoder.yudao.module.market.api.price.PriceApi; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; -import org.springframework.stereotype.Service; - -/** - * 价格 API 实现类 - * - * TODO 完善注释 - * - * @author TODO - */ -@Service -public class PriceApiImpl implements PriceApi { - - @Override - public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { - // TODO fixme:实现逻辑 - return new PriceCalculateRespDTO(); - } - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql deleted file mode 100644 index abad7c0694..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM "market_activity"; diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql deleted file mode 100644 index 3f3ce3c4d7..0000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql +++ /dev/null @@ -1,19 +0,0 @@ -CREATE TABLE IF NOT EXISTS "market_activity" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "title" varchar(50) NOT NULL, - "activity_type" tinyint(4) NOT NULL, - "status" tinyint(4) NOT NULL, - "start_time" datetime NOT NULL, - "end_time" datetime NOT NULL, - "invalid_time" datetime, - "delete_time" datetime, - "time_limited_discount" varchar(2000), - "full_privilege" varchar(2000), - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint(20) NOT NULL, - PRIMARY KEY ("id") - ) COMMENT '促销活动'; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-api/pom.xml b/yudao-module-mall/yudao-module-product-api/pom.xml index 7eb38008a1..123e7c3349 100644 --- a/yudao-module-mall/yudao-module-product-api/pom.xml +++ b/yudao-module-mall/yudao-module-product-api/pom.xml @@ -22,6 +22,13 @@ cn.iocoder.boot yudao-common + + + + org.springframework.boot + spring-boot-starter-validation + true + - \ No newline at end of file + diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java index b3915407fb..d5d93dc372 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java @@ -1,30 +1,40 @@ package cn.iocoder.yudao.module.product.api.sku; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import java.util.Collection; import java.util.List; /** + * 商品 SKU API 接口 + * * @author LeeYan9 * @since 2022-08-26 */ public interface ProductSkuApi { + /** + * 查询 SKU 信息 + * + * @param id SKU 编号 + * @return SKU 信息 + */ + ProductSkuRespDTO getSku(Long id); /** - * 根据skuId列表 查询sku信息 + * 批量查询 SKU 数组 * - * @param skuIds sku ID列表 - * @return sku信息列表 + * @param ids SKU 编号列表 + * @return SKU 数组 */ - List getSkusByIds(Collection skuIds); + List getSkuList(Collection ids); /** - * 批量扣减sku库存 + * 更新 SKU 库存 * - * @param batchReqDTO sku库存信息列表 + * @param updateStockReqDTO 更新请求 */ - void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO); + void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO); + } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java similarity index 95% rename from yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java rename to yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java index f9d349e481..4b324149b8 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java @@ -6,11 +6,13 @@ import lombok.Data; import java.util.List; /** + * 商品 SKU 信息 Response DTO + * * @author LeeYan9 * @since 2022-08-26 */ @Data -public class SkuInfoRespDTO { +public class ProductSkuRespDTO { /** * 商品 SKU 编号,自增 diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java new file mode 100644 index 0000000000..345c17cbd2 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.product.api.sku.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 商品 SKU 更新库存 Request DTO + * + * @author LeeYan9 + * @since 2022-08-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProductSkuUpdateStockReqDTO { + + /** + * 商品 SKU + */ + @NotNull(message = "商品 SKU 不能为空") + private List items; + + @Data + public static class Item { + + /** + * 商品 SKU 编号 + */ + @NotNull(message = "商品 SKU 编号不能为空") + private Long id; + + /** + * 库存变化数量 + * + * 正数:增加库存 + * 负数:扣减库存 + */ + @NotNull(message = "库存变化数量不能为空") + private Integer incrCount; + + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java deleted file mode 100644 index c0cee91baf..0000000000 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.product.api.sku.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * @author LeeYan9 - * @since 2022-08-26 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class SkuDecrementStockBatchReqDTO { - - - private List items; - - @Data - public static class Item { - - /** - * 商品 SPU 编号,自增 - */ - private Long productId; - - /** - * 商品 SKU 编号,自增 - */ - private Long skuId; - - /** - * 数量 - */ - private Integer count; - - } - - public static SkuDecrementStockBatchReqDTO of(List items) { - return new SkuDecrementStockBatchReqDTO(items); - } - -} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java index 5dc2bf4cf4..8ba0fba7dc 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java @@ -1,23 +1,24 @@ package cn.iocoder.yudao.module.product.api.spu; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; -import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import java.util.Collection; import java.util.List; /** + * 商品 SPU API 接口 + * * @author LeeYan9 * @since 2022-08-26 */ public interface ProductSpuApi { - /** - * 根据spuId列表 查询spu信息 + * 批量查询 SPU 数组 * - * @param spuIds spu ID列表 - * @return spu信息列表 + * @param ids SPU 编号列表 + * @return SPU 数组 */ - List getSpusByIds(Collection spuIds); + List getSpuList(Collection ids); + } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java similarity index 81% rename from yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java rename to yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java index 6d0206b7da..45d42f41be 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java @@ -1,18 +1,21 @@ package cn.iocoder.yudao.module.product.api.spu.dto; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import lombok.Data; import java.util.List; +// TODO @LeeYan9: ProductSpuRespDTO /** + * 商品 SPU 信息 Response DTO + * * @author LeeYan9 * @since 2022-08-26 */ @Data -public class SpuInfoRespDTO { +public class ProductSpuRespDTO { /** * 商品 SPU 编号,自增 @@ -30,7 +33,7 @@ public class SpuInfoRespDTO { */ private String code; /** - * 商品卖点 + * 促销语 */ private String sellPoint; /** @@ -80,25 +83,25 @@ public class SpuInfoRespDTO { /** * 最小价格,单位使用:分 *

- * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最小值 + * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最小值 */ private Integer minPrice; /** * 最大价格,单位使用:分 *

- * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最大值 + * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最大值 */ private Integer maxPrice; /** * 市场价,单位使用:分 *

- * 基于其对应的 {@link SkuInfoRespDTO#getMarketPrice()} 最大值 + * 基于其对应的 {@link ProductSkuRespDTO#getMarketPrice()} 最大值 */ private Integer marketPrice; /** * 总库存 *

- * 基于其对应的 {@link SkuInfoRespDTO#getStock()} 求和 + * 基于其对应的 {@link ProductSkuRespDTO#getStock()} 求和 */ private Integer totalStock; /** diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java index 801e2dd513..86875b119a 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java @@ -24,9 +24,11 @@ public interface ErrorCodeConstants { // ========== 商品规格名称 1008003000 ========== ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "规格名称不存在"); + ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "规格名称已存在"); // ========== 规格值 1008004000 ========== ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "规格值不存在"); + ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1008004001, "规格值已存在"); // ========== 商品 SPU 1008005000 ========== ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在"); @@ -36,5 +38,6 @@ public interface ErrorCodeConstants { ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复"); ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU,其规格数必须一致"); ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU,必须不重复"); + ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足"); } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java index 1757f1e499..6b7f03ac2a 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java @@ -19,12 +19,12 @@ public enum ProductSpuStatusEnum implements IntArrayValuable { DISABLE(0, "下架"), ENABLE(1, "上架"),; - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStyle).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStatus).toArray(); /** * 状态 */ - private final Integer style; + private final Integer status; /** * 状态名 */ diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java index 22636826bc..89913c70ed 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java @@ -1,26 +1,49 @@ package cn.iocoder.yudao.module.product.api.sku; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; +import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import javax.annotation.Resource; import java.util.Collection; +import java.util.Collections; import java.util.List; /** - * todo 注释 + * TODO LeeYan9: 类注释; + * @author LeeYan9 + * @since 2022-09-06 */ @Service +@Validated public class ProductSkuApiImpl implements ProductSkuApi { + @Resource + private ProductSkuService productSkuService; + @Override - public List getSkusByIds(Collection skuIds) { + public ProductSkuRespDTO getSku(Long id) { + // TODO TODO LeeYan9: 需要实现 return null; } @Override - public void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO) { + public List getSkuList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List skus = productSkuService.getSkuList(ids); + return ProductSkuConvert.INSTANCE.convertList04(skus); + } + @Override + public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) { + productSkuService.updateSkuStock(updateStockReqDTO); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java index 8f651f3955..4d880e662f 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java @@ -1,20 +1,38 @@ package cn.iocoder.yudao.module.product.api.spu; -import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import javax.annotation.Resource; import java.util.Collection; +import java.util.Collections; import java.util.List; /** - * todo 注释 + * TODO LeeYan9: 类注释; + * + * @author LeeYan9 + * @since 2022-09-06 */ @Service +@Validated public class ProductSpuApiImpl implements ProductSpuApi { - @Override - public List getSpusByIds(Collection spuIds) { - return null; - } + @Resource + private ProductSpuMapper productSpuMapper; + @Override + public List getSpuList(Collection spuIds) { + // TODO TODO LeeYan9: AllEmpty? + if (CollectionUtils.isAnyEmpty(spuIds)) { + return Collections.emptyList(); + } + List productSpuDOList = productSpuMapper.selectBatchIds(spuIds); + return ProductSpuConvert.INSTANCE.convertList2(productSpuDOList); + } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java index 7401ec7442..9a4c20c58c 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.product.controller.admin.property; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*; import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -17,6 +14,8 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Api(tags = "管理后台 - 规格名称") @@ -56,15 +55,29 @@ public class ProductPropertyController { @ApiOperation("获得规格名称") @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @PreAuthorize("@ss.hasPermission('product:property:query')") - public CommonResult getProperty(@RequestParam("id") Long id) { - return success(productPropertyService.getPropertyResp(id)); + public CommonResult getProperty(@RequestParam("id") Long id) { + return success(productPropertyService.getProperty(id)); + } + + @GetMapping("/list") + @ApiOperation("获得规格名称列表") + @PreAuthorize("@ss.hasPermission('product:property:query')") + public CommonResult> getPropertyList(@Valid ProductPropertyListReqVO listReqVO) { + return success(productPropertyService.getPropertyList(listReqVO)); } @GetMapping("/page") @ApiOperation("获得规格名称分页") @PreAuthorize("@ss.hasPermission('product:property:query')") - public CommonResult> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) { - return success(productPropertyService.getPropertyListPage(pageVO)); + public CommonResult> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) { + return success(productPropertyService.getPropertyPage(pageVO)); + } + + @GetMapping("/listAndValue") + @ApiOperation("获得规格名称列表") + @PreAuthorize("@ss.hasPermission('product:property:query')") + public CommonResult> getPropertyAndValueList(@Valid ProductPropertyListReqVO listReqVO) { + return success(productPropertyService.getPropertyAndValueList(listReqVO)); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyValueController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyValueController.java new file mode 100644 index 0000000000..e88c914150 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyValueController.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.product.controller.admin.property; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 规格值名称") +@RestController +@RequestMapping("/product/property/value") +@Validated +public class ProductPropertyValueController { + + @Resource + private ProductPropertyValueService productPropertyValueService; + + @PostMapping("/create") + @ApiOperation("创建规格名称") + @PreAuthorize("@ss.hasPermission('product:property:create')") + public CommonResult createProperty(@Valid @RequestBody ProductPropertyValueCreateReqVO createReqVO) { + return success(productPropertyValueService.createPropertyValue(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新规格名称") + @PreAuthorize("@ss.hasPermission('product:property:update')") + public CommonResult updateProperty(@Valid @RequestBody ProductPropertyValueUpdateReqVO updateReqVO) { + productPropertyValueService.updatePropertyValue(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除规格名称") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:property:delete')") + public CommonResult deleteProperty(@RequestParam("id") Long id) { + productPropertyValueService.deletePropertyValue(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得规格名称") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:property:query')") + public CommonResult getProperty(@RequestParam("id") Long id) { + return success(productPropertyValueService.getPropertyValue(id)); + } + + @GetMapping("/page") + @ApiOperation("获得规格名称分页") + @PreAuthorize("@ss.hasPermission('product:property:query')") + public CommonResult> getPropertyValuePage(@Valid ProductPropertyValuePageReqVO pageVO) { + return success(productPropertyValueService.getPropertyValueListPage(pageVO)); + } +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java index c900a727bc..25fc4c01ac 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java @@ -3,21 +3,25 @@ package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; /** -* 规格名称 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ + * 规格名称 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ @Data public class ProductPropertyBaseVO { @ApiModelProperty(value = "规格名称", required = true, example = "颜色") - @NotEmpty(message = "规格名称不能为空") + @NotBlank(message = "规格名称不能为空") private String name; + @ApiModelProperty(value = "备注", example = "颜色") + private String remark; + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") - @NotEmpty(message = "状态不能为空") + @NotNull(message = "状态不能为空") private Integer status; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java index c0e6b9da26..8dfd58a5d7 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java @@ -1,11 +1,9 @@ package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; -import lombok.*; -import io.swagger.annotations.*; - -import javax.validation.constraints.NotNull; -import java.util.List; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @ApiModel("管理后台 - 规格名称创建 Request VO") @Data @@ -13,9 +11,5 @@ import java.util.List; @ToString(callSuper = true) public class ProductPropertyCreateReqVO extends ProductPropertyBaseVO { - // TODO @Luowenfeng:规格值的 CRUD 可以单独;前端 + 后端,改成类似字典类型、字典数据的这种交互;在加一个 ProductPropertyValueController - @ApiModelProperty(value = "属性值") - @NotNull(message = "属性值不能为空") - List propertyValueList; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyListReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyListReqVO.java new file mode 100644 index 0000000000..314288ffb9 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyListReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +@ApiModel("管理后台 - 规格名称 List Request VO") +@Data +@ToString(callSuper = true) +public class ProductPropertyListReqVO { + + @ApiModelProperty(value = "规格名称", example = "颜色") + private String name; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyRespVO.java new file mode 100644 index 0000000000..75a9a5eea1 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@ApiModel("管理后台 - 规格 + 规格值 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyRespVO extends ProductPropertyBaseVO { + + @ApiModelProperty(value = "规格的编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java index f4b9d695a1..f4630d8747 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java @@ -16,8 +16,4 @@ public class ProductPropertyUpdateReqVO extends ProductPropertyBaseVO { @NotNull(message = "主键不能为空") private Long id; - @ApiModelProperty(value = "属性值") - @NotNull(message = "属性值不能为空") - List propertyValueList; - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java index 1e0708009c..142a8e6e22 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java @@ -22,7 +22,10 @@ public class ProductPropertyValueBaseVO { private String name; @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") - @NotEmpty(message = "状态不能为空") + @NotNull(message = "状态不能为空") private Integer status; + @ApiModelProperty(value = "备注", example = "颜色") + private String remark; + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValuePageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValuePageReqVO.java new file mode 100644 index 0000000000..ae77d822b9 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValuePageReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotEmpty; +import java.util.Date; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 规格名称值分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyValuePageReqVO extends PageParam { + + @ApiModelProperty(value = "规格id", example = "1024") + private String propertyId; + + @ApiModelProperty(value = "规格值", example = "红色") + private String name; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java index 4a1bc57787..2437600a83 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java @@ -12,6 +12,6 @@ public class ProductPropertyValueUpdateReqVO extends ProductPropertyValueBaseVO @ApiModelProperty(value = "主键", required = true, example = "1024") @NotNull(message = "主键不能为空") - private Integer id; + private Long id; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java index 2081e7da80..ac9713423d 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java @@ -1,14 +1,57 @@ package cn.iocoder.yudao.module.product.controller.admin.sku; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO; +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + @Api(tags = "管理后台 - 商品 sku") @RestController @RequestMapping("/product/sku") @Validated public class ProductSkuController { + @Resource + private ProductSkuService productSkuService; + @Resource + private ProductSpuService productSpuService; + + @GetMapping("/get-option-list") + @ApiOperation("获得商品 SKU 选项的列表") +// @PreAuthorize("@ss.hasPermission('product:sku:query')") + public CommonResult> getSkuOptionList() { + // 获得 SKU 列表 + List skus = productSkuService.getSkuList(); + if (CollUtil.isEmpty(skus)) { + return success(Collections.emptyList()); + } + + // 获得对应的 SPU 映射 + Map spuMap = productSpuService.getSpuMap(convertSet(skus, ProductSkuDO::getSpuId)); + // 转换为返回结果 + List skuVOs = ProductSkuConvert.INSTANCE.convertList05(skus); + skuVOs.forEach(sku -> MapUtils.findAndThen(spuMap, sku.getSpuId(), + spu -> sku.setSpuId(spu.getId()).setSpuName(spu.getName()))); + return success(skuVOs); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java index 43f67ee530..45cb447b80 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java @@ -33,7 +33,7 @@ public class ProductSkuBaseVO { @ApiModelProperty(value = "条形码", example = "haha") private String barCode; - @ApiModelProperty(value = "图片地址") + @ApiModelProperty(value = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png") @NotNull(message = "图片地址不能为空") private String picUrl; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java index 84d71d9b74..6d5ce024d3 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java @@ -14,8 +14,7 @@ import java.util.List; @ToString(callSuper = true) public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO { - // TODO @Luowenfeng:可以不用哈,如果基于规格匹配 - @ApiModelProperty(value = "商品 id 更新时须有", example = "1") + @ApiModelProperty(value = "商品 SKU 编号", example = "1") private Long id; /** diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java new file mode 100644 index 0000000000..0324bdc8d0 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.product.controller.admin.sku.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel(value = "管理后台 - 商品 SKU 选项 Response VO", description = "用于前端 SELECT 选项") +@Data +public class ProductSkuOptionRespVO { + + @ApiModelProperty(value = "主键", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "商品 SKU 名字", example = "红色") + private String name; + + @ApiModelProperty(value = "销售价格", required = true, example = "100", notes = "单位:分") + private String price; + + @ApiModelProperty(value = "库存", required = true, example = "100") + private Integer stock; + + // ========== 商品 SPU 信息 ========== + + @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1") + private Long spuId; + + @ApiModelProperty(value = "商品 SPU 名字", required = true, example = "iPhone 11") + private String spuName; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java index 21d59a2729..1ac29724b1 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java @@ -9,7 +9,7 @@ import lombok.ToString; import java.time.LocalDateTime; import java.util.List; -@ApiModel("管理后台 - 商品sku Response VO") +@ApiModel("管理后台 - 商品 SKU Response VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java index ddae45dc25..155fd02653 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.product.controller.admin.spu; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; @@ -56,6 +53,15 @@ public class ProductSpuController { return success(true); } + // TODO 芋艿:修改接口 + @GetMapping("/get/detail") + @ApiOperation("获得商品 SPU") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:spu:query')") + public CommonResult getSpuDetail(@RequestParam("id") Long id) { + return success(spuService.getSpuDetail(id)); + } + @GetMapping("/get") @ApiOperation("获得商品 SPU") @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @@ -64,7 +70,6 @@ public class ProductSpuController { return success(spuService.getSpu(id)); } - // TODO @luowenfeng:新增 get-detail,返回 SpuDetailRespVO @GetMapping("/list") @ApiOperation("获得商品 SPU 列表") @@ -75,6 +80,14 @@ public class ProductSpuController { return success(ProductSpuConvert.INSTANCE.convertList(list)); } + @GetMapping("/get-simple-list") + @ApiOperation("获得商品 SPU 精简列表") + @PreAuthorize("@ss.hasPermission('product:spu:query')") + public CommonResult> getSpuSimpleList() { + List list = spuService.getSpuList(); + return success(ProductSpuConvert.INSTANCE.convertList02(list)); + } + @GetMapping("/page") @ApiOperation("获得商品 SPU 分页") @PreAuthorize("@ss.hasPermission('product:spu:query')") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java index ec58bab765..54f0986dd8 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java @@ -24,7 +24,7 @@ public class ProductSpuBaseVO { @ApiModelProperty(value = "商品编码", example = "yudaoyuanma") private String code; - @ApiModelProperty(value = "商品卖点", example = "好吃!") + @ApiModelProperty(value = "促销语", example = "好吃!") private String sellPoint; @ApiModelProperty(value = "商品详情", required = true, example = "我是商品描述") @@ -67,6 +67,9 @@ public class ProductSpuBaseVO { @ApiModelProperty(value = "库存", required = true, example = "true") private Integer totalStock; + @ApiModelProperty(value = "市场价", example = "1024") + private Integer marketPrice; + @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024") private Integer minPrice; @@ -75,10 +78,14 @@ public class ProductSpuBaseVO { // ========== 统计相关字段 ========= + @ApiModelProperty(value = "商品销量", example = "1024") + private Integer salesCount; + @ApiModelProperty(value = "虚拟销量", required = true, example = "1024") @NotNull(message = "虚拟销量不能为空") private Integer virtualSalesCount; @ApiModelProperty(value = "点击量", example = "1024") private Integer clickCount; + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java index daf7d12aed..de41d14169 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -54,4 +55,11 @@ public class ProductSpuDetailRespVO extends ProductSpuBaseVO { } + @ApiModelProperty(value = "分类 id 数组,一直递归到一级父节点", example = "4") + private Long categoryId; + + // TODO @芋艿:在瞅瞅~ + @ApiModelProperty(value = "规格属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]") + private List productPropertyViews; + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java index 1ee1951377..8d1bbee5fe 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java @@ -1,13 +1,11 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; -import lombok.*; - -import java.time.LocalDateTime; -import io.swagger.annotations.*; import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @ApiModel("管理后台 - 商品 SPU 分页 Request VO") @Data @@ -15,38 +13,34 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @ToString(callSuper = true) public class ProductSpuPageReqVO extends PageParam { - @ApiModelProperty(value = "商品名称") + @ApiModelProperty(value = "商品名称", example = "yutou") private String name; - @ApiModelProperty(value = "卖点") - private String sellPoint; + @ApiModelProperty(value = "商品编码", example = "yudaoyuanma") + private String code; - @ApiModelProperty(value = "描述") - private String description; - - @ApiModelProperty(value = "分类id") + @ApiModelProperty(value = "分类id", example = "1") private Long categoryId; - @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张") - private String picUrls; + @ApiModelProperty(value = "商品品牌编号", example = "1") + private Long brandId; - @ApiModelProperty(value = "排序字段") - private Integer sort; - - @ApiModelProperty(value = "点赞初始人数") - private Integer likeCount; - - @ApiModelProperty(value = "价格 单位使用:分") - private Integer price; - - @ApiModelProperty(value = "库存数量") - private Integer quantity; - - @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") + @ApiModelProperty(value = "上下架状态", example = "1", notes = "参见 ProductSpuStatusEnum 枚举值") private Integer status; - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private LocalDateTime[] createTime; + @ApiModelProperty(value = "销量最小值", example = "1") + private Integer salesCountMin; + + @ApiModelProperty(value = "销量最大值", example = "1024") + private Integer salesCountMax; + + @ApiModelProperty(value = "市场价最小值", example = "1") + private Integer marketPriceMin; + + @ApiModelProperty(value = "市场价最大值", example = "1024") + private Integer marketPriceMax; + + @ApiModelProperty(value = "是否库存告警", example = "true") + private Boolean alarmStock; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java index abe302f657..a8dca328b6 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java @@ -1,7 +1,5 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -11,7 +9,6 @@ import lombok.ToString; import java.time.LocalDateTime; import java.util.List; -// TODO @Luowenfeng:这个类只返回 SPU 相关的信息,删除 skus、categoryIds、productPropertyViews;明细使用 SpuDetailRespVO 替代 @ApiModel("管理后台 - 商品 SPU Response VO") @Data @EqualsAndHashCode(callSuper = true) @@ -24,18 +21,4 @@ public class ProductSpuRespVO extends ProductSpuBaseVO { @ApiModelProperty(value = "创建时间") private LocalDateTime createTime; - /** - * SKU 数组 - */ - @ApiModelProperty(value = "sku 数组", example = "[{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":10}],\"price\":12,\"originalPrice\":32,\"costPrice\":22,\"barCode\":\"765670123123\",\"picUrl\":\"http://test.yudao.iocoder.cn/72938088f1ca8438837c3b51394aea43.jpg\",\"status\":0,\"id\":7,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":11}],\"price\":13,\"originalPrice\":33,\"costPrice\":23,\"barCode\":\"888788770999\",\"picUrl\":\"http://test.yudao.iocoder.cn/6b902c700e5d32e862b6fd9af2e1c0e4.jpg\",\"status\":0,\"id\":8,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":10}],\"price\":14,\"originalPrice\":34,\"costPrice\":24,\"barCode\":\"9999981212\",\"picUrl\":\"http://test.yudao.iocoder.cn/eddf3c79b1917160d94d05244e1f47da.jpg\",\"status\":0,\"id\":9,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":11}],\"price\":15,\"originalPrice\":35,\"costPrice\":25,\"barCode\":\"4444121212\",\"picUrl\":\"http://test.yudao.iocoder.cn/88ac3eb068ea9cfac4726879b2776ccf.jpg\",\"status\":0,\"id\":10,\"createTime\":1656683270000}]") - private List skus; - - @ApiModelProperty(value = "分类id数组,一直递归到一级父节点", example = "[1,2,4]") - private List categoryIds; - - // TODO @芋艿:再琢磨下 这个 VO 类,其实变成 SpuRespVO 内嵌的 VO 类会更好一点;然后把 SpuRespVO 改成 SpuDetailSpuVO - - @ApiModelProperty(value = "规格属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]") - private List productPropertyViews; - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java new file mode 100755 index 0000000000..ea2811f8b8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 商品 SPU 精简 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSpuSimpleRespVO extends ProductSpuBaseVO { + + @ApiModelProperty(value = "主键", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "商品名称", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024") + private Integer minPrice; + + @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024") + private Integer maxPrice; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java index 52b82a2ea6..91f811ad62 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java @@ -3,8 +3,11 @@ package cn.iocoder.yudao.module.product.convert.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -24,10 +27,12 @@ public interface ProductPropertyConvert { ProductPropertyDO convert(ProductPropertyUpdateReqVO bean); - ProductPropertyAndValueRespVO convert(ProductPropertyDO bean); + ProductPropertyAndValueRespVO convert(ProductPropertyRespVO bean); - List convertList(List list); + ProductPropertyRespVO convert(ProductPropertyDO bean); - PageResult convertPage(PageResult page); + List convertList(List list); + + PageResult convertPage(PageResult page); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java index 0ee468d36a..b29d612bec 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java @@ -1,16 +1,23 @@ package cn.iocoder.yudao.module.product.convert.sku; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuDetailRespVO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** - * 商品sku Convert + * 商品 SKU Convert * * @author 芋道源码 */ @@ -21,11 +28,42 @@ public interface ProductSkuConvert { ProductSkuDO convert(ProductSkuCreateOrUpdateReqVO bean); - @Mapping(source = "properties", target = "properties") ProductSkuRespVO convert(ProductSkuDO bean); List convertList(List list); List convertSkuDOList(List list); + ProductSkuRespDTO convert02(ProductSkuDO bean); + + List convertList02(List list); + + List convertList03(List list); + + List convertList04(List list); + + List convertList05(List skus); + + /** + * 获得 SPU 的库存变化 Map + * + * @param items SKU 库存变化 + * @param skus SKU 列表 + * @return SPU 的库存变化 Map + */ + default Map convertSpuStockMap(List items, + List skus) { + Map skuIdAndSpuIdMap = convertMap(skus, ProductSkuDO::getId, ProductSkuDO::getSpuId); // SKU 与 SKU 编号的 Map 关系 + Map spuIdAndStockMap = new HashMap<>(); // SPU 的库存变化 Map 关系 + items.forEach(item -> { + Long spuId = skuIdAndSpuIdMap.get(item.getId()); + if (spuId == null) { + return; + } + Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncrCount(); + spuIdAndStockMap.put(spuId, stock); + }); + return spuIdAndStockMap; + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java index 2fb6639ab4..98b7d88370 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.product.convert.spu; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; @@ -34,4 +35,8 @@ public interface ProductSpuConvert { AppSpuPageRespVO convertAppResp(ProductSpuDO list); + List convertList2(List list); + + List convertList02(List list); + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java index 6671e9b446..93ec925a97 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java @@ -64,6 +64,4 @@ public class ProductCategoryDO extends BaseDO { */ private Integer status; - // TODO 芋艿:is_recommend 是否首页推荐:1-是;0-否 - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java index 608b248fe4..b3831491ec 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java @@ -37,6 +37,10 @@ public class ProductPropertyDO extends BaseDO { * 枚举 {@link CommonStatusEnum} */ private Integer status; + /** + * 备注 + */ + private String remark; // TODO 芋艿:rule;规格属性 (发布商品时,和 SKU 关联);规格参数(搜索商品时,与 Category 关联搜索) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java index 007b95a4f8..b75f0d5927 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java @@ -44,5 +44,10 @@ public class ProductPropertyValueDO extends BaseDO { * 枚举 {@link CommonStatusEnum} */ private Integer status; + /** + * 备注 + * + */ + private String remark; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java index fc7bbe9ecd..f953534ed3 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java @@ -97,6 +97,8 @@ public class ProductSkuDO extends BaseDO { * 商品属性 */ @Data + @NoArgsConstructor + @AllArgsConstructor public static class Property { /** @@ -129,19 +131,5 @@ public class ProductSkuDO extends BaseDO { } - // TODO ========== 待定字段:yv ========= - // TODO brokerage:一级返佣 - // TODO brokerage_two:二级返佣 - // TODO pink_price:拼团价 - // TODO pink_stock:拼团库存 - // TODO seckill_price:秒杀价 - // TODO seckill_stock:秒杀库存 - // TODO integral:需要积分 - - // TODO ========== 待定字段:cf ========= - // TODO type 活动显示排序 0=默认 1=秒 2=砍价 3=拼团 - // TODO quota 活动限购数量 - // TODO quota_show 活动限购数量显示 - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java index a22b68a2c2..93c47d4aff 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java @@ -47,7 +47,7 @@ public class ProductSpuDO extends BaseDO { */ private String code; /** - * 商品卖点 + * 促销语 */ private String sellPoint; /** diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java index bd088466f6..890df34771 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.Pro import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * 规格名称 Mapper * @@ -23,4 +25,9 @@ public interface ProductPropertyMapper extends BaseMapperX { .orderByDesc(ProductPropertyDO::getId)); } + default ProductPropertyDO selectByName(String name) { + return selectOne(new LambdaQueryWrapperX() + .eqIfPresent(ProductPropertyDO::getName, name)); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java index 6c5fc7570a..ca9bafab26 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.product.dal.mysql.property; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; import org.apache.ibatis.annotations.Mapper; @@ -15,18 +17,28 @@ import java.util.List; @Mapper public interface ProductPropertyValueMapper extends BaseMapperX { - // TODO @franky:方法名,selectListByXXX。mapper 的操作都是 crud - default List getPropertyValueListByPropertyId(List propertyIds){ - // TODO @franky:调用父类的 selectList + default List selectListByPropertyId(List propertyIds) { return selectList(new LambdaQueryWrapperX() .inIfPresent(ProductPropertyValueDO::getPropertyId, propertyIds)); } - default void deletePropertyValueByPropertyId(Long propertyId){ - // TODO @franky:delete(new ) 即可 - LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); - queryWrapperX.eq(ProductPropertyValueDO::getPropertyId, propertyId) - .eq(ProductPropertyValueDO::getDeleted, false); - delete(queryWrapperX); + default ProductPropertyValueDO selectByName(Long propertyId, String name) { + return selectOne(new LambdaQueryWrapperX() + .eq(ProductPropertyValueDO::getPropertyId, propertyId) + .eq(ProductPropertyValueDO::getName, name)); } + + default void deletePropertyValueByPropertyId(Long propertyId) { + delete(new LambdaQueryWrapperX().eq(ProductPropertyValueDO::getPropertyId, propertyId) + .eq(ProductPropertyValueDO::getDeleted, false)); + } + + default PageResult selectPage(ProductPropertyValuePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ProductPropertyValueDO::getPropertyId, reqVO.getPropertyId()) + .likeIfPresent(ProductPropertyValueDO::getName, reqVO.getName()) + .eqIfPresent(ProductPropertyValueDO::getStatus, reqVO.getStatus()) + .orderByDesc(ProductPropertyValueDO::getId)); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java index cc45e5764e..3e5a9ce8fa 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.product.dal.mysql.sku; +import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -15,20 +18,46 @@ import java.util.List; @Mapper public interface ProductSkuMapper extends BaseMapperX { - // TODO @franky:方法名 selectList; 可以直接调用 selectList - default List selectListBySpuIds(List spuIds) { - return selectList(ProductSkuDO::getSpuId, spuIds); - } - default List selectListBySpuId(Long spuId) { return selectList(ProductSkuDO::getSpuId, spuId); } default void deleteBySpuId(Long spuId) { - // TODO @franky:直接 delete(new XXX) 即可,更简洁一些 - LambdaQueryWrapperX lambdaQueryWrapperX = new LambdaQueryWrapperX() - .eqIfPresent(ProductSkuDO::getSpuId, spuId); - delete(lambdaQueryWrapperX); + delete(new LambdaQueryWrapperX().eq(ProductSkuDO::getSpuId, spuId)); + } + + /** + * 更新 SKU 库存(增加) + * + * @param id 编号 + * @param incrCount 增加库存(正数) + */ + default void updateStockIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount > 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" stock = stock + " + incrCount) + .eq(ProductSkuDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新 SKU 库存(减少) + * + * @param id 编号 + * @param incrCount 减少库存(负数) + * @return 更新条数 + */ + default int updateStockDecr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() + .setSql(" stock = stock + " + incrCount) // 负数,所以使用 + 号 + .eq(ProductSkuDO::getId, id) + .ge(ProductSkuDO::getStock, -incrCount); // cas 逻辑 + return update(null, updateWrapper); + } + + default List selectListByAlarmStock(){ + return selectList(new QueryWrapper().apply("stock <= warn_stock")); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java index 1755695cba..a271b5051e 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; +import java.util.Set; + /** * 商品spu Mapper * @@ -18,12 +21,40 @@ public interface ProductSpuMapper extends BaseMapperX { default PageResult selectPage(ProductSpuPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .likeIfPresent(ProductSpuDO::getName, reqVO.getName()) - .eqIfPresent(ProductSpuDO::getSellPoint, reqVO.getSellPoint()) .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) - .eqIfPresent(ProductSpuDO::getPicUrls, reqVO.getPicUrls()) .eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime()) + .leIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMax()) + .geIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMin()) + .leIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMax()) + .geIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMin()) .orderByDesc(ProductSpuDO::getSort)); } + default PageResult selectPage(ProductSpuPageReqVO reqVO, Set alarmStockSpuIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ProductSpuDO::getName, reqVO.getName()) + .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) + .eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus()) + .leIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMax()) + .geIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMin()) + .leIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMax()) + .geIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMin()) + .inIfPresent(ProductSpuDO::getId, alarmStockSpuIds) // 库存告警 + .eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus()) + .orderByDesc(ProductSpuDO::getSort)); + } + + /** + * 更新商品 SPU 库存 + * + * @param id 商品 SPU 编号 + * @param incrCount 增加的库存数量 + */ + default void updateStock(Long id, Integer incrCount) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() + .setSql(" total_stock = total_stock +" + incrCount) // 负数,所以使用 + 号 + .eq(ProductSpuDO::getId, id); + update(null, updateWrapper); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java index ebfe599d66..ffe55e9bfc 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.product.service.category; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; @@ -93,35 +92,21 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { @Override public void validateCategoryLevel(Long id) { - Integer level = getProductCategoryLevel(id, 1); - if (level < 3){ + // TODO @芋艿:在看看,杂能优化下 + Long parentId = id; + int i = 2; + for (; i >= 0; --i) { + ProductCategoryDO category = productCategoryMapper.selectById(parentId); + parentId = category.getParentId(); + if(Objects.equals(parentId, ProductCategoryDO.PARENT_ID_NULL)){ + break; + } + } + if (!Objects.equals(parentId, ProductCategoryDO.PARENT_ID_NULL) || i != 0) { throw exception(CATEGORY_LEVEL_ERROR); } } - // TODO @Luowenfeng:建议使用 for 循环,避免递归 - /** - * 获得商品分类的级别 - * - * @param id 商品分类的编号 - * @return 级别 - */ - private Integer getProductCategoryLevel(Long id, int level){ - ProductCategoryDO category = productCategoryMapper.selectById(id); - if (category == null) { - throw exception(CATEGORY_NOT_EXISTS); - } - // TODO Luowenfeng:去掉是否开启,它不影响级别哈 - if (ObjectUtil.notEqual(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - throw exception(CATEGORY_DISABLED); - } - // TODO Luowenfeng:不使用 0 直接比较哈,使用枚举 - if (category.getParentId() == 0) { - return level; - } - return getProductCategoryLevel(category.getParentId(), ++level); - } - @Override public ProductCategoryDO getCategory(Long id) { return productCategoryMapper.selectById(id); diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java index a87e15d70b..8780c78658 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.product.service.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; @@ -39,44 +39,42 @@ public interface ProductPropertyService { void deleteProperty(Long id); /** - * 获得规格名称 + * 获得规格名称列表 + * @param listReqVO 集合查询 + * @return 规格名称集合 + */ + List getPropertyList(ProductPropertyListReqVO listReqVO); + + /** + * 获取属性名称分页 + * + * @param pageReqVO 分页条件 + * @return 规格名称分页 + */ + PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO); + + /** + * 获得指定编号的规格名称 * * @param id 编号 * @return 规格名称 */ - ProductPropertyDO getProperty(Long id); - - /** - * 获得规格名称列表 - * - * @param ids 编号 - * @return 规格名称列表 - */ - List getPropertyList(Collection ids); - - /** - * 获得规格名称分页 - * - * @param pageReqVO 分页查询 - * @return 规格名称分页 - */ - PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO); - - /** - * 获取属性及属性值列表 分页 - * @param pageReqVO - * @return - */ - PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO); - - ProductPropertyAndValueRespVO getPropertyResp(Long id); + ProductPropertyRespVO getProperty(Long id); /** * 根据规格属性编号的集合,获得对应的规格 + 规格值的集合 * * @param ids 规格编号的集合 - * @return 对应的规格 + 规格值的集合 + * @return 对应的规格 */ - List getPropertyAndValueList(Collection ids); + List getPropertyList(Collection ids); + + /** + * 获得规格名称 + 值的列表 + * + * @param listReqVO 列表查询 + * @return 规格名称 + 值的列表 + */ + List getPropertyAndValueList(ProductPropertyListReqVO listReqVO); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java index d48c8b1ff9..74e370f9c1 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.product.service.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.convert.property.ProductPropertyConvert; import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; @@ -16,13 +16,13 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_EXISTS; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_NOT_EXISTS; /** @@ -43,15 +43,13 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @Override @Transactional(rollbackFor = Exception.class) public Long createProperty(ProductPropertyCreateReqVO createReqVO) { + // 校验存在 + if (productPropertyMapper.selectByName(createReqVO.getName()) != null) { + throw exception(PROPERTY_EXISTS); + } // 插入 ProductPropertyDO property = ProductPropertyConvert.INSTANCE.convert(createReqVO); productPropertyMapper.insert(property); - - //插入属性值 - List propertyValueList = createReqVO.getPropertyValueList(); - List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); - productPropertyValueDOList.forEach(x-> x.setPropertyId(property.getId())); - productPropertyValueMapper.insertBatch(productPropertyValueDOList); // 返回 return property.getId(); } @@ -61,15 +59,13 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { public void updateProperty(ProductPropertyUpdateReqVO updateReqVO) { // 校验存在 this.validatePropertyExists(updateReqVO.getId()); + ProductPropertyDO productPropertyDO = productPropertyMapper.selectByName(updateReqVO.getName()); + if (productPropertyDO != null && !productPropertyDO.getId().equals(updateReqVO.getId())) { + throw exception(PROPERTY_EXISTS); + } // 更新 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO); productPropertyMapper.updateById(updateObj); - //更新属性值,先删后加 - productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId()); - List propertyValueList = updateReqVO.getPropertyValueList(); - List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); - productPropertyValueDOList.forEach(x-> x.setPropertyId(updateReqVO.getId())); - productPropertyValueMapper.insertBatch(productPropertyValueDOList); } @Override @@ -89,63 +85,41 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { } @Override - public ProductPropertyDO getProperty(Long id) { - return productPropertyMapper.selectById(id); + public List getPropertyList(ProductPropertyListReqVO listReqVO) { + return ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectList(new LambdaQueryWrapperX() + .likeIfPresent(ProductPropertyDO::getName, listReqVO.getName()) + .eqIfPresent(ProductPropertyDO::getStatus, listReqVO.getStatus()))); } @Override - public List getPropertyList(Collection ids) { - return productPropertyMapper.selectBatchIds(ids); - } - - @Override - public PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO) { - return productPropertyMapper.selectPage(pageReqVO); - } - - @Override - public PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO) { + public PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO) { //获取属性列表 PageResult pageResult = productPropertyMapper.selectPage(pageReqVO); - PageResult propertyRespVOPageResult = ProductPropertyConvert.INSTANCE.convertPage(pageResult); - List propertyIds = propertyRespVOPageResult.getList().stream().map(ProductPropertyAndValueRespVO::getId).collect(Collectors.toList()); + return ProductPropertyConvert.INSTANCE.convertPage(pageResult); + } - //获取属性值列表 - List productPropertyValueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); - List propertyValueRespVOList = ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueDOList); - //组装一对多 - propertyRespVOPageResult.getList().forEach(x->{ - Long propertyId = x.getId(); - List valueDOList = propertyValueRespVOList.stream().filter(v -> v.getPropertyId().equals(propertyId)).collect(Collectors.toList()); - x.setValues(valueDOList); + @Override + public ProductPropertyRespVO getProperty(Long id) { + ProductPropertyDO property = productPropertyMapper.selectById(id); + return ProductPropertyConvert.INSTANCE.convert(property); + } + + @Override + public List getPropertyList(Collection ids) { + return ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(ids)); + } + + @Override + public List getPropertyAndValueList(ProductPropertyListReqVO listReqVO) { + List propertyList = getPropertyList(listReqVO); + + // 查询属性值 + List valueDOList = productPropertyValueMapper.selectListByPropertyId(CollectionUtils.convertList(propertyList, ProductPropertyRespVO::getId)); + Map> valueDOMap = CollectionUtils.convertMultiMap(valueDOList, ProductPropertyValueDO::getPropertyId); + return CollectionUtils.convertList(propertyList, m -> { + ProductPropertyAndValueRespVO productPropertyAndValueRespVO = ProductPropertyConvert.INSTANCE.convert(m); + productPropertyAndValueRespVO.setValues(ProductPropertyValueConvert.INSTANCE.convertList(valueDOMap.get(m.getId()))); + return productPropertyAndValueRespVO; }); - return propertyRespVOPageResult; - } - - private List getPropertyValueListByPropertyId(List propertyIds) { - return productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); - } - - @Override - public ProductPropertyAndValueRespVO getPropertyResp(Long id) { - //查询规格 - ProductPropertyDO property = getProperty(id); - ProductPropertyAndValueRespVO propertyRespVO = ProductPropertyConvert.INSTANCE.convert(property); - //查询属性值 - List valueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(Arrays.asList(id)); - List propertyValueRespVOS = ProductPropertyValueConvert.INSTANCE.convertList(valueDOList); - //组装 - propertyRespVO.setValues(propertyValueRespVOS); - return propertyRespVO; - } - - @Override - public List getPropertyAndValueList(Collection ids) { - List productPropertyRespVO = ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(ids)); - //查询属性值 - List valueDOList = productPropertyValueMapper.selectBatchIds(ids); - Map> propertyValuesMap = valueDOList.stream().collect(Collectors.groupingBy(ProductPropertyValueDO::getPropertyId)); - productPropertyRespVO.forEach(p -> p.setValues(ProductPropertyValueConvert.INSTANCE.convertList(propertyValuesMap.get(p.getId())))); - return productPropertyRespVO; } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java new file mode 100644 index 0000000000..9dcbd72e7a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.product.service.property; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; + +import java.util.List; + +/** + *

+ * 规格值 Service 接口 + *

+ * + * @author LuoWenFeng + */ +public interface ProductPropertyValueService { + + /** + * 创建规格值 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO); + + /** + * 更新规格值 + * + * @param updateReqVO 更新信息 + */ + void updatePropertyValue(ProductPropertyValueUpdateReqVO updateReqVO); + + /** + * 删除规格值 + * + * @param id 编号 + */ + void deletePropertyValue(Long id); + + /** + * 获得规格值 + * + * @param id 编号 + * @return 规格名称 + */ + ProductPropertyValueRespVO getPropertyValue(Long id); + + /** + * 获得规格值 + * + * @param id 编号 + * @return 规格名称 + */ + List getPropertyValueListByPropertyId(List id); + + /** + * 获取规格值 分页 + * + * @param pageReqVO 查询条件 + * @return + */ + PageResult getPropertyValueListPage(ProductPropertyValuePageReqVO pageReqVO); +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java new file mode 100644 index 0000000000..5addb37e8a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.product.service.property; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; +import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; +import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_EXISTS; + +/** + * 规格值 Service 实现类 + * + * @author LuoWenFeng + */ +@Service +@Validated +public class ProductPropertyValueServiceImpl implements ProductPropertyValueService { + + @Resource + private ProductPropertyValueMapper productPropertyValueMapper; + + @Override + public Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO) { + if (productPropertyValueMapper.selectByName(createReqVO.getPropertyId(), createReqVO.getName()) != null) { + throw exception(PROPERTY_VALUE_EXISTS); + } + ProductPropertyValueDO convert = ProductPropertyValueConvert.INSTANCE.convert(createReqVO); + productPropertyValueMapper.insert(convert); + return convert.getId(); + } + + @Override + public void updatePropertyValue(ProductPropertyValueUpdateReqVO updateReqVO) { + ProductPropertyValueDO productPropertyValueDO = productPropertyValueMapper.selectByName(updateReqVO.getPropertyId(), updateReqVO.getName()); + if (productPropertyValueDO != null && !productPropertyValueDO.getId().equals(updateReqVO.getId())) { + throw exception(PROPERTY_VALUE_EXISTS); + } + ProductPropertyValueDO convert = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO); + productPropertyValueMapper.updateById(convert); + } + + @Override + public void deletePropertyValue(Long id) { + productPropertyValueMapper.deleteById(id); + } + + @Override + public ProductPropertyValueRespVO getPropertyValue(Long id) { + ProductPropertyValueDO productPropertyValueDO = productPropertyValueMapper.selectOne(new LambdaQueryWrapper() + .eq(ProductPropertyValueDO::getId, id)); + return ProductPropertyValueConvert.INSTANCE.convert(productPropertyValueDO); + } + + @Override + public List getPropertyValueListByPropertyId(List id) { + return ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueMapper.selectList("property_id", id)); + } + + @Override + public PageResult getPropertyValueListPage(ProductPropertyValuePageReqVO pageReqVO) { + return ProductPropertyValueConvert.INSTANCE.convertPage(productPropertyValueMapper.selectPage(pageReqVO)); + } +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java index 5316f4764d..1036d8348d 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.product.service.sku; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -15,22 +15,29 @@ import java.util.List; public interface ProductSkuService { /** - * 删除商品sku + * 删除商品 SKU * * @param id 编号 */ void deleteSku(Long id); /** - * 获得商品sku + * 获得商品 SKU 信息 * * @param id 编号 - * @return 商品sku + * @return 商品 SKU 信息 */ ProductSkuDO getSku(Long id); /** - * 获得商品sku列表 + * 获得商品 SKU 列表 + * + * @return 商品sku列表 + */ + List getSkuList(); + + /** + * 获得商品 SKU 列表 * * @param ids 编号 * @return 商品sku列表 @@ -58,7 +65,16 @@ public interface ProductSkuService { * @param spuId SPU 编码 * @param skus SKU 的集合 */ - void updateProductSkus(Long spuId, List skus); + void updateSkus(Long spuId, List skus); + + /** + * 更新 SKU 库存(增量) + * + * 如果更新的库存不足,会抛出异常 + * + * @param updateStockReqDTO 更行请求 + */ + void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO); /** * 获得商品 sku 集合 @@ -83,4 +99,12 @@ public interface ProductSkuService { */ void deleteSkuBySpuId(Long spuId); + /** + * 获得库存预警的 SKU 数组 + * + * @return SKU 数组 + */ + List getSkusByAlarmStock(); + + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java index bf8dab778f..2791ae5d3a 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.product.service.sku; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; @@ -13,6 +13,9 @@ import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -22,6 +25,7 @@ import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; /** @@ -36,13 +40,18 @@ public class ProductSkuServiceImpl implements ProductSkuService { @Resource private ProductSkuMapper productSkuMapper; + @Resource + @Lazy // 循环依赖,避免报错 + private ProductSpuService productSpuService; @Resource private ProductPropertyService productPropertyService; + @Resource + private ProductPropertyValueService productPropertyValueService; @Override public void deleteSku(Long id) { // 校验存在 - this.validateSkuExists(id); + validateSkuExists(id); // 删除 productSkuMapper.deleteById(id); } @@ -58,6 +67,11 @@ public class ProductSkuServiceImpl implements ProductSkuService { return productSkuMapper.selectById(id); } + @Override + public List getSkuList() { + return productSkuMapper.selectList(); + } + @Override public List getSkuList(Collection ids) { return productSkuMapper.selectBatchIds(ids); @@ -71,19 +85,17 @@ public class ProductSkuServiceImpl implements ProductSkuService { } // 1、校验规格属性存在 - // TODO @Luowenfeng:stream 的写法;不用改哈,就是说下可以酱紫写; Set propertyIds = skus.stream().filter(p -> p.getProperties() != null).flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性 .map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toSet()); // 将每个 Property 转换成对应的 propertyId,最后形成集合 - List propertyAndValueList = productPropertyService.getPropertyAndValueList(propertyIds); - if (propertyAndValueList.size() == propertyIds.size()) { + List propertyList = productPropertyService.getPropertyList(propertyIds); + if (propertyList.size() != propertyIds.size()) { throw exception(PROPERTY_NOT_EXISTS); } // 2. 校验,一个 SKU 下,没有重复的规格。校验方式是,遍历每个 SKU ,看看是否有重复的规格 propertyId - Map propertyValueMap = propertyAndValueList.stream().filter(p -> p.getValues() != null).flatMap(p -> p.getValues().stream()) - .collect(Collectors.toMap(ProductPropertyValueRespVO::getId, value -> value)); // KEY:规格属性值的编号 + Map propertyValueMap = CollectionUtils.convertMap(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(propertyIds)), ProductPropertyValueRespVO::getId); skus.forEach(sku -> { - Set skuPropertyIds = CollectionUtils.convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId()); + Set skuPropertyIds = convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId()); if (skuPropertyIds.size() != sku.getProperties().size()) { throw exception(SKU_PROPERTIES_DUPLICATED); } @@ -100,8 +112,7 @@ public class ProductSkuServiceImpl implements ProductSkuService { // 4. 最后校验,每个 Sku 之间不是重复的 Set> skuAttrValues = new HashSet<>(); // 每个元素,都是一个 Sku 的 attrValueId 集合。这样,通过最外层的 Set ,判断是否有重复的. for (ProductSkuCreateOrUpdateReqVO sku : skus) { - // TODO @Luowenfeng:可以使用 CollectionUtils.convertSet(),简化下面的 stream 操作 - if (!skuAttrValues.add(sku.getProperties().stream().map(ProductSkuBaseVO.Property::getValueId).collect(Collectors.toSet()))) { // 添加失败,说明重复 + if (!skuAttrValues.add(convertSet(sku.getProperties(), ProductSkuBaseVO.Property::getValueId))) { // 添加失败,说明重复 throw exception(ErrorCodeConstants.SPU_SKU_NOT_DUPLICATE); } } @@ -117,12 +128,12 @@ public class ProductSkuServiceImpl implements ProductSkuService { @Override public List getSkusBySpuId(Long spuId) { - return productSkuMapper.selectListBySpuIds(Collections.singletonList(spuId)); + return productSkuMapper.selectList(ProductSkuDO::getSpuId, spuId); } @Override public List getSkusBySpuIds(List spuIds) { - return productSkuMapper.selectListBySpuIds(spuIds); + return productSkuMapper.selectList(ProductSkuDO::getSpuId, spuIds); } @Override @@ -130,41 +141,84 @@ public class ProductSkuServiceImpl implements ProductSkuService { productSkuMapper.deleteBySpuId(spuId); } + @Override + public List getSkusByAlarmStock() { + return productSkuMapper.selectListByAlarmStock(); + } + @Override @Transactional - public void updateProductSkus(Long spuId, List skus) { + public void updateSkus(Long spuId, List skus) { // 查询 SPU 下已经存在的 SKU 的集合 List existsSkus = productSkuMapper.selectListBySpuId(spuId); - Map existsSkuMap = CollectionUtils.convertMap(existsSkus, ProductSkuDO::getId); + // 构建规格与 SKU 的映射关系; + // TODO @luowenfeng: 可以下 existsSkuMap2; 会简洁一点; 另外, 可以考虑抽一个小方法, 用于 Properties 生成一个串; 这样 177 也可以复用了 + Map existsSkuMap = existsSkus.stream() + .map(v -> { + String collect = v.getProperties() == null? "null": v.getProperties() + .stream() + .map(m -> String.valueOf(m.getValueId())) + .collect(Collectors.joining()); + return String.join("-", collect, String.valueOf(v.getId())); + }) + .collect(Collectors.toMap(v -> v.split("-")[0], v -> Long.valueOf(v.split("-")[1]))); // 拆分三个集合,新插入的、需要更新的、需要删除的 List insertSkus = new ArrayList<>(); - List updateSkus = new ArrayList<>(); // TODO Luowenfeng:使用 Long 即可 - List deleteSkus = new ArrayList<>(); + List updateSkus = new ArrayList<>(); + List deleteSkus = new ArrayList<>(); - // TODO @Luowenfeng:是不是基于规格匹配会比较好。可以参考下 onemall 的 ProductSpuServiceImpl 的 updateProductSpu 逻辑 List allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus); allUpdateSkus.forEach(p -> { - if (p.getId() != null) { - if (existsSkuMap.containsKey(p.getId())) { - updateSkus.add(p); - return; - } - deleteSkus.add(p); + String propertiesKey = p.getProperties() == null? "null": p.getProperties().stream().map(m -> String.valueOf(m.getValueId())).collect(Collectors.joining()); + // 1、找得到的,进行更新 + if (existsSkuMap.containsKey(propertiesKey)) { + updateSkus.add(p); + existsSkuMap.remove(propertiesKey); return; } + // 2、找不到,进行插入 p.setSpuId(spuId); insertSkus.add(p); }); + // 3、多余的,删除 + if(!existsSkuMap.isEmpty()){ + deleteSkus = new ArrayList<>(existsSkuMap.values()); + } - if (CollectionUtil.isNotEmpty(insertSkus)) { + // 4、执行修改 Sku + if (!insertSkus.isEmpty()) { productSkuMapper.insertBatch(insertSkus); } - if (updateSkus.size() > 0) { + if (!updateSkus.isEmpty()) { updateSkus.forEach(p -> productSkuMapper.updateById(p)); } - if (deleteSkus.size() > 0) { - productSkuMapper.deleteBatchIds(deleteSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList())); + if (!deleteSkus.isEmpty()) { + productSkuMapper.deleteBatchIds(deleteSkus); } } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) { + // 更新 SKU 库存 + updateStockReqDTO.getItems().forEach(item -> { + if (item.getIncrCount() > 0) { + productSkuMapper.updateStockIncr(item.getId(), item.getIncrCount()); + } else if (item.getIncrCount() < 0) { + int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncrCount()); + if (updateStockIncr == 0) { + throw exception(SKU_STOCK_NOT_ENOUGH); + } + } + }); + + // 更新 SPU 库存 + List skus = productSkuMapper.selectBatchIds( + convertSet(updateStockReqDTO.getItems(), ProductSkuUpdateStockReqDTO.Item::getId)); + Map spuStockIncrCounts = ProductSkuConvert.INSTANCE.convertSpuStockMap( + updateStockReqDTO.getItems(), skus); + productSpuService.updateSpuStock(spuStockIncrCounts); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java index e5affa8d2d..151edbf867 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.product.service.spu; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; @@ -12,6 +9,9 @@ import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import javax.validation.Valid; import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * 商品 SPU Service 接口 @@ -36,30 +36,55 @@ public interface ProductSpuService { void updateSpu(@Valid ProductSpuUpdateReqVO updateReqVO); /** - * 删除商品spu + * 删除商品 SPU * * @param id 编号 */ void deleteSpu(Long id); /** - * 获得商品spu + * 获得商品 SPU 详情 * * @param id 编号 - * @return 商品spu + * @return 商品 SPU + */ + ProductSpuDetailRespVO getSpuDetail(Long id); + + /** + * 获得商品 SPU + * + * @param id 编号 + * @return 商品 SPU */ ProductSpuRespVO getSpu(Long id); /** - * 获得商品spu列表 + * 获得商品 SPU 列表 * - * @param ids 编号 - * @return 商品spu列表 + * @param ids 编号数组 + * @return 商品 SPU 列表 */ List getSpuList(Collection ids); /** - * 获得商品spu分页 + * 获得商品 SPU 映射 + * + * @param ids 编号数组 + * @return 商品 SPU 映射 + */ + default Map getSpuMap(Collection ids) { + return convertMap(getSpuList(ids), ProductSpuDO::getId); + } + + /** + * 获得所有商品 SPU 列表 + * + * @return 商品 SPU 列表 + */ + List getSpuList(); + + /** + * 获得商品 SPU 分页 * * @param pageReqVO 分页查询 * @return 商品spu分页 @@ -67,12 +92,18 @@ public interface ProductSpuService { PageResult getSpuPage(ProductSpuPageReqVO pageReqVO); /** - * 获得商品spu分页 + * 获得商品 SPU 分页 * * @param pageReqVO 分页查询 * @return 商品spu分页 */ PageResult getSpuPage(AppSpuPageReqVO pageReqVO); + /** + * 更新商品 SPU 库存(增量) + * + * @param stockIncrCounts SPU 编号与库存变化(增量)的映射 + */ + void updateSpuStock(Map stockIncrCounts); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java index 847301ba80..4880d4948e 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java @@ -1,28 +1,30 @@ package cn.iocoder.yudao.module.product.service.spu; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -44,17 +46,18 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_E public class ProductSpuServiceImpl implements ProductSpuService { @Resource - private ProductSpuMapper ProductSpuMapper; + private ProductSpuMapper productSpuMapper; @Resource private ProductCategoryService categoryService; @Resource + @Lazy // 循环依赖,避免报错 private ProductSkuService productSkuService; - @Resource private ProductPropertyService productPropertyService; - + @Resource + private ProductPropertyValueService productPropertyValueService; @Resource private ProductBrandService brandService; @@ -74,7 +77,7 @@ public class ProductSpuServiceImpl implements ProductSpuService { spu.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); spu.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); spu.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); - ProductSpuMapper.insert(spu); + productSpuMapper.insert(spu); // 插入 SKU productSkuService.createSkus(spu.getId(), skuCreateReqList); // 返回 @@ -92,7 +95,6 @@ public class ProductSpuServiceImpl implements ProductSpuService { brandService.validateProductBrand(updateReqVO.getBrandId()); // 校验SKU List skuCreateReqList = updateReqVO.getSkus(); - // 多规格才需校验 productSkuService.validateSkus(skuCreateReqList, updateReqVO.getSpecType()); // 更新 SPU @@ -101,9 +103,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { updateObj.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); updateObj.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); updateObj.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); - ProductSpuMapper.updateById(updateObj); + productSpuMapper.updateById(updateObj); // 批量更新 SKU - productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus()); + productSkuService.updateSkus(updateObj.getId(), updateReqVO.getSkus()); } @Override @@ -112,41 +114,44 @@ public class ProductSpuServiceImpl implements ProductSpuService { // 校验存在 validateSpuExists(id); // 删除 SPU - ProductSpuMapper.deleteById(id); + productSpuMapper.deleteById(id); // 删除关联的 SKU productSkuService.deleteSkuBySpuId(id); } private void validateSpuExists(Long id) { - if (ProductSpuMapper.selectById(id) == null) { + if (productSpuMapper.selectById(id) == null) { throw exception(SPU_NOT_EXISTS); } } @Override // TODO @芋艿:需要再 review 下 - public ProductSpuRespVO getSpu(Long id) { - ProductSpuDO spu = ProductSpuMapper.selectById(id); - ProductSpuRespVO spuVO = ProductSpuConvert.INSTANCE.convert(spu); - if (null != spuVO) { - List skuReqs = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuId(id)); - spuVO.setSkus(skuReqs); - List properties = new ArrayList<>(); + public ProductSpuDetailRespVO getSpuDetail(Long id) { + ProductSpuDO spu = productSpuMapper.selectById(id); + ProductSpuDetailRespVO respVO = BeanUtil.copyProperties(spu, ProductSpuDetailRespVO.class); + if (null != spu) { + List skuReqs = ProductSkuConvert.INSTANCE.convertList03(productSkuService.getSkusBySpuId(id)); + respVO.setSkus(skuReqs); // 组合 sku 规格属性 - if(spu.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) { - for (ProductSkuRespVO productSkuRespVO : skuReqs) { + if (spu.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) { + List properties = new ArrayList<>(); + for (ProductSpuDetailRespVO.Sku productSkuRespVO : skuReqs) { properties.addAll(productSkuRespVO.getProperties()); } Map> propertyMaps = properties.stream().collect(Collectors.groupingBy(ProductSkuBaseVO.Property::getPropertyId)); - List propertyAndValueList = productPropertyService.getPropertyAndValueList(new ArrayList<>(propertyMaps.keySet())); + + List propertyValueList = productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(propertyMaps.keySet())); + List propertyList = productPropertyService.getPropertyList(new ArrayList<>(propertyMaps.keySet())); // 装载组装过后的属性 List productPropertyViews = new ArrayList<>(); - propertyAndValueList.forEach(p -> { + propertyList.forEach(p -> { ProductPropertyViewRespVO productPropertyViewRespVO = new ProductPropertyViewRespVO(); productPropertyViewRespVO.setPropertyId(p.getId()); productPropertyViewRespVO.setName(p.getName()); List propertyValues = new ArrayList<>(); - Map propertyValueMaps = p.getValues().stream().collect(Collectors.toMap(ProductPropertyValueRespVO::getId, pv -> pv)); + // 转换成map是为了能快速获取 + Map propertyValueMaps = CollectionUtils.convertMap(propertyValueList, ProductPropertyValueRespVO::getId); propertyMaps.get(p.getId()).forEach(pv -> { ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName()); propertyValues.add(tuple2); @@ -154,48 +159,47 @@ public class ProductSpuServiceImpl implements ProductSpuService { productPropertyViewRespVO.setPropertyValues(propertyValues.stream().distinct().collect(Collectors.toList())); productPropertyViews.add(productPropertyViewRespVO); }); - spuVO.setProductPropertyViews(productPropertyViews); - } - // 组合分类 - if (null != spuVO.getCategoryId()) { - LinkedList categoryArray = new LinkedList<>(); - Long parentId = spuVO.getCategoryId(); - categoryArray.addFirst(parentId); - while (parentId != 0) { - parentId = categoryService.getCategory(parentId).getParentId(); - if (parentId > 0) { - categoryArray.addFirst(parentId); - } - } - spuVO.setCategoryIds(categoryArray); + respVO.setProductPropertyViews(productPropertyViews); } } - return spuVO; + return respVO; + } + + @Override + public ProductSpuRespVO getSpu(Long id) { + return ProductSpuConvert.INSTANCE.convert(productSpuMapper.selectById(id)); } @Override public List getSpuList(Collection ids) { - return ProductSpuMapper.selectBatchIds(ids); + return productSpuMapper.selectBatchIds(ids); + } + + @Override + public List getSpuList() { + return productSpuMapper.selectList(); } @Override public PageResult getSpuPage(ProductSpuPageReqVO pageReqVO) { - PageResult spuVOs = ProductSpuConvert.INSTANCE.convertPage(ProductSpuMapper.selectPage(pageReqVO)); - // 查询 sku 的信息 - List spuIds = spuVOs.getList().stream().map(ProductSpuRespVO::getId).collect(Collectors.toList()); - List skus = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuIds(spuIds)); - // TODO @franky:使用 CollUtil 里的方法替代哈 - // TODO 芋艿:临时注释 -// Map> skuMap = skus.stream().collect(Collectors.groupingBy(ProductSkuRespVO::getSpuId)); -// // 将 spu 和 sku 进行组装 -// spuVOs.getList().forEach(p -> p.setSkus(skuMap.get(p.getId()))); - return spuVOs; + // 库存告警的 SPU 编号的集合 + Set alarmStockSpuIds = null; + if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) { + alarmStockSpuIds = CollectionUtils.convertSet(productSkuService.getSkusByAlarmStock(), ProductSkuDO::getSpuId); + if (CollUtil.isEmpty(alarmStockSpuIds)) { + return PageResult.empty(); + } + } + // 分页查询 + return ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds)); } @Override public PageResult getSpuPage(AppSpuPageReqVO pageReqVO) { - PageResult productSpuDOPageResult = ProductSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO)); + // TODO 芋艿:貌似实现不太合理 + PageResult productSpuDOPageResult = productSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO)); PageResult pageResult = new PageResult<>(); + // TODO @芋艿 这里用convert如何解决 List collect = productSpuDOPageResult.getList() .stream() .map(ProductSpuConvert.INSTANCE::convertAppResp) @@ -205,4 +209,10 @@ public class ProductSpuServiceImpl implements ProductSpuService { return pageResult; } + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSpuStock(Map stockIncrCounts) { + stockIncrCounts.forEach((id, incCount) -> productSpuMapper.updateStock(id, incCount)); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java new file mode 100644 index 0000000000..87ce870151 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java @@ -0,0 +1,98 @@ +package cn.iocoder.yudao.module.product.service.sku; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.framework.test.core.util.AssertUtils; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; +import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; + +/** + * {@link ProductSkuServiceImpl} 的单元测试 + * + * @author 芋道源码 + */ +@Import(ProductSkuServiceImpl.class) +public class ProductSkuServiceTest extends BaseDbUnitTest { + + @Resource + private ProductSkuService productSkuService; + + @Resource + private ProductSkuMapper productSkuMapper; + + @MockBean + private ProductSpuService productSpuService; + @MockBean + private ProductPropertyService productPropertyService; + @MockBean + private ProductPropertyValueService productPropertyValueService; + + @Test + public void testUpdateSkuStock_incrSuccess() { + // 准备参数 + ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(10))); + // mock 数据 + productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); + + // 调用 + productSkuService.updateSkuStock(updateStockReqDTO); + // 断言 + ProductSkuDO sku = productSkuMapper.selectById(1L); + assertEquals(sku.getStock(), 30); + verify(productSpuService).updateSpuStock(argThat(spuStockIncrCounts -> { + assertEquals(spuStockIncrCounts.size(), 1); + assertEquals(spuStockIncrCounts.get(10L), 10); + return true; + })); + } + + @Test + public void testUpdateSkuStock_decrSuccess() { + // 准备参数 + ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-10))); + // mock 数据 + productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); + + // 调用 + productSkuService.updateSkuStock(updateStockReqDTO); + // 断言 + ProductSkuDO sku = productSkuMapper.selectById(1L); + assertEquals(sku.getStock(), 10); + verify(productSpuService).updateSpuStock(argThat(spuStockIncrCounts -> { + assertEquals(spuStockIncrCounts.size(), 1); + assertEquals(spuStockIncrCounts.get(10L), -10); + return true; + })); + } + + @Test + public void testUpdateSkuStock_decrFail() { + // 准备参数 + ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-30))); + // mock 数据 + productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); + + // 调用并断言 + AssertUtils.assertServiceException(() -> productSkuService.updateSkuStock(updateStockReqDTO), + SKU_STOCK_NOT_ENOUGH); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java index 516e38b1a8..1f04e9d357 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java @@ -14,6 +14,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.assertNull; +// TODO 芋艿:整合到 {@link ProductSkuServiceTest} 中 /** * {@link ProductSkuServiceImpl} 的单元测试类 * diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java index 426dd926be..c6408d9656 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java @@ -1,164 +1,406 @@ package cn.iocoder.yudao.module.product.service.spu; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; +import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.product.service.brand.ProductBrandServiceImpl; +import cn.iocoder.yudao.module.product.service.category.ProductCategoryServiceImpl; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; +import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +// TODO @芋艿:review 下单元测试 /** -* {@link ProductSpuServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link ProductSpuServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(ProductSpuServiceImpl.class) @Disabled // TODO 芋艿:临时去掉 public class ProductSpuServiceImplTest extends BaseDbUnitTest { @Resource - private ProductSpuServiceImpl spuService; + private ProductSpuServiceImpl productSpuService; @Resource - private ProductSpuMapper ProductSpuMapper; + private ProductSpuMapper productSpuMapper; + + @MockBean + private ProductSkuServiceImpl productSkuService; + @MockBean + private ProductCategoryServiceImpl categoryService; + @MockBean + private ProductBrandServiceImpl brandService; + @MockBean + private ProductPropertyService productPropertyService; + @MockBean + private ProductPropertyValueService productPropertyValueService; + + public String generateNo() { + return DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999); + } + + public Long generateId() { + return RandomUtil.randomLong(100000, 999999); + } @Test public void testCreateSpu_success() { // 准备参数 - ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class); + ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class, o -> { + o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType()); + o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); + }); + + // 校验SKU + List skuCreateReqList = createReqVO.getSkus(); + + Long spu = productSpuService.createSpu(createReqVO); + ProductSpuDO productSpuDO = productSpuMapper.selectById(spu); + + createReqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice)); + createReqVO.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + createReqVO.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + createReqVO.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); + + assertPojoEquals(createReqVO, productSpuDO); - // 调用 - Long spuId = spuService.createSpu(reqVO); - // 断言 - assertNotNull(spuId); - // 校验记录的属性是否正确 - ProductSpuDO spu = ProductSpuMapper.selectById(spuId); - assertPojoEquals(reqVO, spu); } @Test public void testUpdateSpu_success() { - // mock 数据 - ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class); - ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据 + // 准备参数 + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class); + productSpuMapper.insert(createReqVO); // 准备参数 ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> { - o.setId(dbSpu.getId()); // 设置更新的 ID + o.setId(createReqVO.getId()); // 设置更新的 ID + o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType()); + o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()); }); - // 调用 - spuService.updateSpu(reqVO); + productSpuService.updateSpu(reqVO); + + List skuCreateReqList = reqVO.getSkus(); + reqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice)); + reqVO.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + reqVO.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + reqVO.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); + // 校验是否更新正确 - ProductSpuDO spu = ProductSpuMapper.selectById(reqVO.getId()); // 获取最新的 + ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, spu); } @Test - public void testUpdateSpu_notExists() { - // 准备参数 - ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS); + public void testValidateSpuExists_exception() { + ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> { + o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType()); + o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()); + }); + // 调用 + Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO)); } @Test - public void testDeleteSpu_success() { - // mock 数据 - ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class); - ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据 + void deleteSpu() { // 准备参数 - Long id = dbSpu.getId(); + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class); + productSpuMapper.insert(createReqVO); // 调用 - spuService.deleteSpu(id); - // 校验数据不存在了 - assertNull(ProductSpuMapper.selectById(id)); + productSpuService.deleteSpu(createReqVO.getId()); + + Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId())); } @Test - public void testDeleteSpu_notExists() { + void getSpuDetail() { + // 准备spu参数 + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> { + o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType()); + }); + productSpuMapper.insert(createReqVO); + + // 创建两个属性 + ArrayList productPropertyRespVOS = Lists.newArrayList( + randomPojo(ProductPropertyRespVO.class), + randomPojo(ProductPropertyRespVO.class)); + + // 所有属性值 + ArrayList productPropertyValueRespVO = new ArrayList<>(); + + // 每个属性创建属性值 + productPropertyRespVOS.forEach(v -> { + ProductPropertyValueRespVO productPropertyValueRespVO1 = randomPojo(ProductPropertyValueRespVO.class, o -> o.setPropertyId(v.getId())); + productPropertyValueRespVO.add(productPropertyValueRespVO1); + }); + + // 属性值建立笛卡尔积 + Map> collect = productPropertyValueRespVO.stream().collect(Collectors.groupingBy(ProductPropertyValueRespVO::getPropertyId)); + List> lists = cartesianProduct(Lists.newArrayList(collect.values())); + + // 准备sku参数 + ArrayList productSkuDOS = Lists.newArrayList(); + lists.forEach(pp -> { + List property = pp.stream().map(ppv -> new ProductSkuDO.Property(ppv.getPropertyId(), ppv.getId())).collect(Collectors.toList()); + ProductSkuDO productSkuDO = randomPojo(ProductSkuDO.class, o -> { + o.setProperties(property); + }); + productSkuDOS.add(productSkuDO); + + }); + + Mockito.when(productSkuService.getSkusBySpuId(createReqVO.getId())).thenReturn(productSkuDOS); + Mockito.when(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyValueRespVO); + Mockito.when(productPropertyService.getPropertyList(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyRespVOS); + + // 调用 + ProductSpuDetailRespVO spuDetail = productSpuService.getSpuDetail(createReqVO.getId()); + + assertPojoEquals(createReqVO, spuDetail); + } + + @Test + void getSpu() { // 准备参数 - Long id = 1L; + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class); + productSpuMapper.insert(createReqVO); - // 调用, 并断言异常 - assertServiceException(() -> spuService.deleteSpu(id), SPU_NOT_EXISTS); + ProductSpuRespVO spu = productSpuService.getSpu(createReqVO.getId()); + assertPojoEquals(createReqVO, spu); } @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetSpuPage() { - // mock 数据 - ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class, o -> { // 等会查询到 - o.setName(null); - o.setSellPoint(null); - o.setDescription(null); - o.setCategoryId(null); - o.setPicUrls(null); - o.setSort(null); -// o.setLikeCount(null); -// o.setPrice(null); -// o.setQuantity(null); - o.setStatus(null); - o.setCreateTime(null); - }); - ProductSpuMapper.insert(dbSpu); - // 测试 name 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setName(null))); - // 测试 sellPoint 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSellPoint(null))); - // 测试 description 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setDescription(null))); - // 测试 categoryId 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCategoryId(null))); - // 测试 picUrls 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPicUrls(null))); - // 测试 sort 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSort(null))); - // 测试 likeCount 不匹配 -// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setLikeCount(null))); - // 测试 price 不匹配 -// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPrice(null))); - // 测试 quantity 不匹配 -// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setQuantity(null))); - // 测试 status 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(null))); - // 准备参数 - ProductSpuPageReqVO reqVO = new ProductSpuPageReqVO(); - reqVO.setName(null); - reqVO.setSellPoint(null); - reqVO.setDescription(null); - reqVO.setCategoryId(null); - reqVO.setPicUrls(null); - reqVO.setSort(null); - reqVO.setLikeCount(null); - reqVO.setPrice(null); - reqVO.setQuantity(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); + void getSpuList() { + // 准备参数 + ArrayList createReqVO = Lists.newArrayList(randomPojo(ProductSpuDO.class), randomPojo(ProductSpuDO.class)); + productSpuMapper.insertBatch(createReqVO); - // 调用 - PageResult pageResult = spuService.getSpuPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSpu, pageResult.getList().get(0)); + // 调用 + List spuList = productSpuService.getSpuList(createReqVO.stream().map(ProductSpuDO::getId).collect(Collectors.toList())); + Assertions.assertIterableEquals(createReqVO, spuList); + } + + @Test + void getSpuPage_alarmStock_empty() { + // 调用 + ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); + productSpuPageReqVO.setAlarmStock(true); + + PageResult spuPage = productSpuService.getSpuPage(productSpuPageReqVO); + + PageResult result = PageResult.empty(); + Assertions.assertIterableEquals(result.getList(), spuPage.getList()); + assertEquals(spuPage.getTotal(), result.getTotal()); + } + + @Test + void getSpuPage_alarmStock() { + // mock 数据 + Long brandId = generateId(); + Long categoryId = generateId(); + String code = generateNo(); + + // 准备参数 + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o->{ + o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); + o.setTotalStock(500); + o.setMinPrice(1); + o.setMaxPrice(50); + o.setMarketPrice(25); + o.setSpecType(ProductSpuSpecTypeEnum.RECYCLE.getType()); + o.setBrandId(brandId); + o.setCategoryId(categoryId); + o.setClickCount(100); + o.setCode(code); + o.setDescription("测试商品"); + o.setPicUrls(new ArrayList<>()); + o.setName("测试"); + o.setSalesCount(100); + o.setSellPoint("超级加倍"); + o.setShowStock(true); + o.setVideoUrl(""); + }); + productSpuMapper.insert(createReqVO); + + Set alarmStockSpuIds = SetUtils.asSet(createReqVO.getId()); + + List productSpuDOS = Arrays.asList(randomPojo(ProductSkuDO.class, o -> { + o.setSpuId(createReqVO.getId()); + }), randomPojo(ProductSkuDO.class, o -> { + o.setSpuId(createReqVO.getId()); + })); + + Mockito.when(productSkuService.getSkusByAlarmStock()).thenReturn(productSpuDOS); + + // 调用 + ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); + productSpuPageReqVO.setAlarmStock(true); + PageResult spuPage = productSpuService.getSpuPage(productSpuPageReqVO); + + PageResult result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds)); + Assertions.assertIterableEquals(result.getList(), spuPage.getList()); + assertEquals(spuPage.getTotal(), result.getTotal()); + } + + @Test + void getSpuPage() { + // mock 数据 + Long brandId = generateId(); + Long categoryId = generateId(); + + // 准备参数 + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o->{ + o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); + o.setTotalStock(1); + o.setMinPrice(1); + o.setMaxPrice(1); + o.setMarketPrice(1); + o.setSpecType(ProductSpuSpecTypeEnum.RECYCLE.getType()); + o.setBrandId(brandId); + o.setCategoryId(categoryId); + o.setClickCount(1); + o.setCode(generateNo()); + o.setDescription("测试商品"); + o.setPicUrls(new ArrayList<>()); + o.setName("测试"); + o.setSalesCount(1); + o.setSellPoint("卖点"); + o.setShowStock(true); + }); + + // 准备参数 + productSpuMapper.insert(createReqVO); + // 测试 status 不匹配 + productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()))); + productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); + // 测试 SpecType 不匹配 + productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType()))); + // 测试 BrandId 不匹配 + productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setBrandId(generateId()))); + // 测试 CategoryId 不匹配 + productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setCategoryId(generateId()))); + + // 调用 + ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); + productSpuPageReqVO.setAlarmStock(false); + productSpuPageReqVO.setBrandId(brandId); + productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); + productSpuPageReqVO.setCategoryId(categoryId); + + PageResult spuPage = productSpuService.getSpuPage(productSpuPageReqVO); + + PageResult result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set) null)); + assertEquals(result, spuPage); + } + + @Test + void testGetSpuPage() { +// 准备参数 + ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> { + o.setCategoryId(2L); + }); + productSpuMapper.insert(createReqVO); + + // 调用 + AppSpuPageReqVO appSpuPageReqVO = new AppSpuPageReqVO(); + appSpuPageReqVO.setCategoryId(2L); + + PageResult spuPage = productSpuService.getSpuPage(appSpuPageReqVO); + + PageResult result = productSpuMapper.selectPage( + ProductSpuConvert.INSTANCE.convert(appSpuPageReqVO)); + + List collect = result.getList() + .stream() + .map(ProductSpuConvert.INSTANCE::convertAppResp) + .collect(Collectors.toList()); + + Assertions.assertIterableEquals(collect, spuPage.getList()); + assertEquals(spuPage.getTotal(), result.getTotal()); + } + + + /** + * 生成笛卡尔积 + * + * @param data 数据 + * @return 笛卡尔积 + */ + public static List> cartesianProduct(List> data) { + List> res = null; // 结果集(当前为第N个List,则该处存放的就为前N-1个List的笛卡尔积集合) + for (List list : data) { // 遍历数据 + List> temp = new ArrayList<>(); // 临时结果集,存放本次循环后生成的笛卡尔积集合 + if (res == null) { // 结果集为null表示第一次循环既list为第一个List + for (T t : list) { // 便利第一个List + // 利用stream生成List,第一个List的笛卡尔积集合约等于自己本身(需要创建一个List并把对象添加到当中),存放到临时结果集 + temp.add(Stream.of(t).collect(Collectors.toList())); + } + res = temp; // 将临时结果集赋值给结果集 + continue; // 跳过本次循环 + } + // 不为第一个List,计算前面的集合(笛卡尔积)和当前List的笛卡尔积集合 + for (T t : list) { // 便利 + for (List rl : res) { // 便利前面的笛卡尔积集合 + // 利用stream生成List + temp.add(Stream.concat(rl.stream(), Stream.of(t)).collect(Collectors.toList())); + } + } + res = temp; // 将临时结果集赋值给结果集 + } + // 返回结果 + return res; + } + + @Test + public void testUpdateSpuStock() { + // 准备参数 + Map stockIncrCounts = MapUtil.builder(1L, 10).put(2L, -20).build(); + // mock 方法(数据) + productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> o.setId(1L).setTotalStock(20))); + productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> o.setId(2L).setTotalStock(30))); + + // 调用 + productSpuService.updateSpuStock(stockIncrCounts); + // 断言 + assertEquals(productSpuService.getSpu(1L).getTotalStock(), 30); + assertEquals(productSpuService.getSpu(2L).getTotalStock(), 10); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql index d9a04afed4..aad1913376 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql @@ -1,3 +1,3 @@ -DELETE FROM "product_category"; +DELETE FROM "product_sku"; -DELETE FROM "product_brand"; \ No newline at end of file +DELETE FROM "product_spu"; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql index 37fc0ff211..fd5abeed04 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql @@ -1,30 +1,54 @@ -CREATE TABLE IF NOT EXISTS "product_category" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "parent_id" bigint(20) NOT NULL, - "name" varchar(255) NOT NULL, - "pic_url" varchar(255) NOT NULL, - "sort" int(11) NOT NULL, - "description" varchar(1024) NOT NULL, - "status" tinyint(4) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '商品分类'; +CREATE TABLE IF NOT EXISTS `product_sku` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `spu_id` bigint NOT NULL COMMENT 'spu编号', + `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号', + `name` varchar DEFAULT NULL COMMENT '商品 SKU 名字', + `properties` varchar DEFAULT NULL COMMENT '规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]', + `price` int NOT NULL DEFAULT '-1' COMMENT '销售价格,单位:分', + `market_price` int DEFAULT NULL COMMENT '市场价', + `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分', + `pic_url` varchar NOT NULL COMMENT '图片地址', + `stock` int DEFAULT NULL COMMENT '库存', + `warn_stock` int DEFAULT NULL COMMENT '预警库存', + `volume` double DEFAULT NULL COMMENT '商品体积', + `weight` double DEFAULT NULL COMMENT '商品重量', + `bar_code` varchar DEFAULT NULL COMMENT '条形码', + `status` tinyint DEFAULT NULL COMMENT '状态: 0-正常 1-禁用', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `creator` varchar DEFAULT NULL COMMENT '创建人', + `updater` varchar DEFAULT NULL COMMENT '更新人', + `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) +) COMMENT '商品sku'; -CREATE TABLE IF NOT EXISTS "product_brand" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(255) NOT NULL, - "pic_url" varchar(255) NOT NULL, - "sort" int(11), - "description" varchar(1024), - "status" tinyint(4) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '商品品牌'; + +CREATE TABLE IF NOT EXISTS `product_spu` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号', + `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号', + `category_id` bigint NOT NULL COMMENT '分类id', + `spec_type` int NOT NULL COMMENT '规格类型:0 单规格 1 多规格', + `code` varchar(128) DEFAULT NULL COMMENT '商品编码', + `name` varchar(128) NOT NULL COMMENT '商品名称', + `sell_point` varchar(128) DEFAULT NULL COMMENT '卖点', + `description` text COMMENT '描述', + `pic_urls` varchar(1024) DEFAULT '' COMMENT '商品轮播图地址数组,以逗号分隔最多上传15张', + `video_url` varchar(128) DEFAULT NULL COMMENT '商品视频', + `market_price` int DEFAULT NULL COMMENT '市场价,单位使用:分', + `min_price` int DEFAULT NULL COMMENT '最小价格,单位使用:分', + `max_price` int DEFAULT NULL COMMENT '最大价格,单位使用:分', + `total_stock` int NOT NULL DEFAULT '0' COMMENT '总库存', + `show_stock` int DEFAULT '0' COMMENT '是否展示库存', + `sales_count` int DEFAULT '0' COMMENT '商品销量', + `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量', + `click_count` int DEFAULT '0' COMMENT '商品点击量', + `status` bit(1) DEFAULT NULL COMMENT '上下架状态: 0 上架(开启) 1 下架(禁用)-1 回收', + `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `creator` varchar DEFAULT NULL COMMENT '创建人', + `updater` varchar DEFAULT NULL COMMENT '更新人', + `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除', +PRIMARY KEY (`id`) +) COMMENT '商品spu'; diff --git a/yudao-module-mall/yudao-module-market-api/pom.xml b/yudao-module-mall/yudao-module-promotion-api/pom.xml similarity index 94% rename from yudao-module-mall/yudao-module-market-api/pom.xml rename to yudao-module-mall/yudao-module-promotion-api/pom.xml index 9517c343ff..510b17beeb 100644 --- a/yudao-module-mall/yudao-module-market-api/pom.xml +++ b/yudao-module-mall/yudao-module-promotion-api/pom.xml @@ -8,7 +8,7 @@ ${revision} 4.0.0 - yudao-module-market-api + yudao-module-promotion-api jar ${project.artifactId} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java new file mode 100644 index 0000000000..f99ff815fd --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.promotion.api.coupon; + +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; + +import javax.validation.Valid; + +/** + * 优惠劵 API 接口 + * + * @author 芋道源码 + */ +public interface CouponApi { + + /** + * 使用优惠劵 + * + * @param useReqDTO 使用请求 + */ + void useCoupon(@Valid CouponUseReqDTO useReqDTO); + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponUseReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponUseReqDTO.java new file mode 100644 index 0000000000..9323ab5532 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponUseReqDTO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.promotion.api.coupon.dto; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 优惠劵使用 Request DTO + * + * @author 芋道源码 + */ +@Data +public class CouponUseReqDTO { + + /** + * 优惠劵编号 + */ + @NotNull(message = "优惠劵编号不能为空") + private Long id; + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + + /** + * 订单编号 + */ + @NotNull(message = "订单编号不能为空") + private Long orderId; + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/package-info.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/package-info.java new file mode 100644 index 0000000000..08e1020a69 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.promotion.api; diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApi.java similarity index 58% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApi.java index 532a66dc22..b36c938bc3 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApi.java @@ -1,7 +1,7 @@ -package cn.iocoder.yudao.module.market.api.price; +package cn.iocoder.yudao.module.promotion.api.price; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; /** * 价格 API 接口 diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/CouponMeetRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/CouponMeetRespDTO.java new file mode 100644 index 0000000000..310959e2c7 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/CouponMeetRespDTO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.promotion.api.price.dto; + +import lombok.Data; + +/** + * 优惠劵的匹配信息 Response DTO + * + * why 放在 price 包下?主要获取的时候,需要涉及到较多的价格计算逻辑,放在 price 可以更好的复用逻辑 + * + * @author 芋道源码 + */ +@Data +public class CouponMeetRespDTO { + + /** + * 优惠劵编号 + */ + private Long id; + + // ========== 非优惠劵的基本信息字段 ========== + /** + * 是否匹配 + */ + private Boolean meet; + /** + * 不匹配的提示,即 {@link #meet} = true 才有值 + * + * 例如说: + * 1. 所结算商品没有符合条件的商品 + * 2. 差 XXX 元可用优惠劵 + * 3. 优惠劵未到使用时间 + */ + private String meetTip; + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java similarity index 94% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java index 4cc019651c..01f0ac2208 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.api.price.dto; +package cn.iocoder.yudao.module.promotion.api.price.dto; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateRespDTO.java new file mode 100644 index 0000000000..cda6d99d6d --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateRespDTO.java @@ -0,0 +1,257 @@ +package cn.iocoder.yudao.module.promotion.api.price.dto; + +import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import lombok.Data; + +import java.util.List; + +/** + * 价格计算 Response DTO + * + * 整体设计,参考 taobao 的技术文档: + * 1. 订单管理 + * 2. 常用订单金额说明 + * + * 举个例子:订单图 + * 输入: + * 1. 订单实付: trade.payment = 198.00;订单邮费:5 元; + * 2. 商品级优惠 圣诞价: 省 29.00 元 和 圣诞价:省 150.00 元; 订单级优惠,圣诞 2:省 5.00 元; + * 分摊: + * 1. 商品 1:原价 108 元,优惠 29 元,子订单实付 79 元,分摊主订单优惠 1.99 元; + * 2. 商品 2:原价 269 元,优惠 150 元,子订单实付 119 元,分摊主订单优惠 3.01 元; + * + * @author 芋道源码 + */ +@Data +public class PriceCalculateRespDTO { + + /** + * 订单 + */ + private Order order; + + /** + * 营销活动数组 + * + * 只对应 {@link Order#items} 商品匹配的活动 + */ + private List promotions; + + /** + * 订单 + */ + @Data + public static class Order { + + /** + * 商品原价(总),单位:分 + * + * 基于 {@link OrderItem#getOriginalPrice()} 求和 + * + * 对应 taobao 的 trade.total_fee 字段 + */ + private Integer originalPrice; + /** + * 订单原价(总),单位:分 + * + * 基于 {@link OrderItem#getPayPrice()} 求和 + * 和 {@link #originalPrice} 的差异:去除商品级优惠 + */ + private Integer orderPrice; + /** + * 订单优惠(总),单位:分 + * + * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。 + * + * 对应 taobao 的 order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * 优惠劵减免金额(总),单位:分 + * + * 对应 taobao 的 trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 积分减免金额(总),单位:分 + * + * 对应 taobao 的 trade.point_fee 字段 + */ + private Integer pointPrice; + /** + * 运费金额,单位:分 + */ + private Integer deliveryPrice; + /** + * 最终购买金额(总),单位:分 + * + * = {@link OrderItem#getPayPrice()} 求和 + * - {@link #couponPrice} + * - {@link #pointPrice} + * + {@link #deliveryPrice} + * - {@link #discountPrice} + */ + private Integer payPrice; + /** + * 商品 SKU 数组 + */ + private List items; + + // ========== 营销基本信息 ========== + /** + * 优惠劵编号 + */ + private Long couponId; + + } + + /** + * 订单商品 SKU + */ + @Data + public static class OrderItem { + + /** + * SPU 编号 + */ + private Long spuId; + /** + * SKU 编号 + */ + private Long skuId; + /** + * 购买数量 + */ + private Integer count; + + /** + * 商品原价(总),单位:分 + * + * = {@link #originalUnitPrice} * {@link #getCount()} + */ + private Integer originalPrice; + /** + * 商品原价(单),单位:分 + * + * 对应 ProductSkuDO 的 price 字段 + * 对应 taobao 的 order.price 字段 + */ + private Integer originalUnitPrice; + /** + * 商品优惠(总),单位:分 + * + * 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元 + * + * 对应 taobao 的 order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * 子订单实付金额,不算主订单分摊金额,单位:分 + * + * = {@link #originalPrice} + * - {@link #discountPrice} + * + * 对应 taobao 的 order.payment 字段 + */ + private Integer payPrice; + + /** + * 子订单分摊金额(总),单位:分 + * 需要分摊 {@link Order#discountPrice}、{@link Order#couponPrice}、{@link Order#pointPrice} + * + * 对应 taobao 的 order.part_mjz_discount 字段 + * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。 + */ + private Integer orderPartPrice; + /** + * 分摊后子订单实付金额(总),单位:分 + * + * = {@link #payPrice} + * - {@link #orderPartPrice} + * + * 对应 taobao 的 divide_order_fee 字段 + */ + private Integer orderDividePrice; + + } + + /** + * 营销明细 + */ + @Data + public static class Promotion { + + /** + * 营销编号 + * + * 例如说:营销活动的编号、优惠劵的编号 + */ + private Long id; + /** + * 营销名字 + */ + private String name; + /** + * 营销类型 + * + * 枚举 {@link PromotionTypeEnum} + */ + private Integer type; + /** + * 营销级别 + * + * 枚举 {@link PromotionLevelEnum} + */ + private Integer level; + /** + * 计算时的原价(总),单位:分 + */ + private Integer originalPrice; + /** + * 计算时的优惠(总),单位:分 + */ + private Integer discountPrice; + /** + * 匹配的商品 SKU 数组 + */ + private List items; + + // ========== 匹配情况 ========== + + /** + * 是否满足优惠条件 + */ + private Boolean meet; + /** + * 满足条件的提示 + * + * 如果 {@link #meet} = true 满足,则提示“圣诞价:省 150.00 元” + * 如果 {@link #meet} = false 不满足,则提示“购满 85 元,可减 40 元” + */ + private String meetTip; + + } + + /** + * 营销匹配的商品 SKU + */ + @Data + public static class PromotionItem { + + /** + * 商品 SKU 编号 + */ + private Long skuId; + /** + * 计算时的原价(总),单位:分 + */ + private Integer originalPrice; + /** + * 计算时的优惠(总),单位:分 + */ + private Integer discountPrice; + + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java new file mode 100644 index 0000000000..7e8fadedea --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.promotion.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * promotion 错误码枚举类 + * + * market 系统,使用 1-003-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 促销活动相关 1003001000 ============ + ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "限时折扣活动不存在"); + ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003006001, "存在商品参加了其它限时折扣活动"); + ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003006002, "限时折扣活动已关闭,不能修改"); + ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1003006003, "限时折扣活动未关闭,不能删除"); + ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003006004, "限时折扣活动已关闭,不能重复关闭"); + ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003006004, "限时折扣活动已结束,不能关闭"); + + // ========== Banner 相关 1003002000 ============ + ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner 不存在"); + + // ========== Coupon 相关 1003003000 ============ + ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1003003000, "优惠劵没有可使用的商品!"); + ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1003003000, "所结算的商品中未满足使用的金额"); + + // ========== 优惠劵模板 1003004000 ========== + ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1003004000, "优惠劵模板不存在"); + ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1003004001, "发放数量不能小于已领取数量({})"); + + // ========== 优惠劵模板 1003005000 ========== + ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1003005000, "优惠券不存在"); + ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1003005001, "回收优惠劵失败,优惠劵已被使用"); + ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1006003003, "优惠劵不处于待使用状态"); + ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1006003004, "优惠券不在使用时间范围内"); + + // ========== 满减送活动 1003006000 ========== + ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1003006000, "满减送活动不存在"); + ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003006001, "存在商品参加了其它满减送活动"); + ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003006002, "满减送活动已关闭,不能修改"); + ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1003006003, "满减送活动未关闭,不能删除"); + ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003006004, "满减送活动已关闭,不能重复关闭"); + ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003006004, "满减送活动已结束,不能关闭"); + + // ========== Price 相关 1003007000 ============ + ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1003007000, "支付价格计算异常,原因:价格小于等于 0"); + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionActivityStatusEnum.java similarity index 93% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionActivityStatusEnum.java index 9577841aa9..db79f871b5 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionActivityStatusEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.enums.common; +package cn.iocoder.yudao.module.promotion.enums.common; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionConditionTypeEnum.java similarity index 92% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionConditionTypeEnum.java index 4df0e69574..05e62e3990 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionConditionTypeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.enums.common; +package cn.iocoder.yudao.module.promotion.enums.common; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionDiscountTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionDiscountTypeEnum.java new file mode 100644 index 0000000000..7da6b4b08a --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionDiscountTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.promotion.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 优惠类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PromotionDiscountTypeEnum implements IntArrayValuable { + + PRICE(1, "满减"), // 具体金额 + PERCENT(2, "折扣"), // 百分比 + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionDiscountTypeEnum::getType).toArray(); + + /** + * 优惠类型 + */ + private final Integer type; + /** + * 名字 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionLevelEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionLevelEnum.java new file mode 100644 index 0000000000..ed0564a70f --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionLevelEnum.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.promotion.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 营销的级别枚举 + * + * 参考有赞:营销级别 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PromotionLevelEnum implements IntArrayValuable { + + ORDER(1, "订单级"), // 多个商品,进行组合后优惠。例如说:满减送、打包一口价、第二件半价 + SKU(2, "商品级"), // 单个商品,直接优惠。例如说:限时折扣、会员折扣 + COUPON(3, "优惠劵"), // 多个商品,进行组合后优惠。例如说:优惠劵 + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionLevelEnum::getLevel).toArray(); + + /** + * 级别值 + */ + private final Integer level; + /** + * 类型名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java similarity index 92% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java index e1792ee67d..0a7a4994d1 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.enums.common; +package cn.iocoder.yudao.module.promotion.enums.common; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java similarity index 76% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java index f3f5cc8822..eea48f7dc8 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.enums.common; +package cn.iocoder.yudao.module.promotion.enums.common; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -15,8 +15,11 @@ import java.util.Arrays; @AllArgsConstructor public enum PromotionTypeEnum implements IntArrayValuable { - DISCOUNT(1, "限时折扣"), - REWARD(2, "满减送"), + DISCOUNT_ACTIVITY(1, "限时折扣"), + REWARD_ACTIVITY(2, "满减送"), + + MEMBER(3, "会员折扣"), + COUPON(4, "优惠劵") ; public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray(); diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java similarity index 56% rename from yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java rename to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java index 25e3f33c22..320345d85f 100644 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.enums.common; +package cn.iocoder.yudao.module.promotion.enums.coupon; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -7,26 +7,27 @@ import lombok.Getter; import java.util.Arrays; /** - * 营销的级别枚举 + * 优惠劵状态枚举 * * @author 芋道源码 */ -@Getter @AllArgsConstructor -public enum PromotionLevelEnum implements IntArrayValuable { +@Getter +public enum CouponStatusEnum implements IntArrayValuable { - ORDER(1, "订单级"), - SKU(2, "商品级"), + UNUSED(1, "未使用"), + USED(2, "已使用"), + EXPIRE(3, "已过期"), ; - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionLevelEnum::getLevel).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponStatusEnum::getStatus).toArray(); /** - * 级别值 + * 值 */ - private final Integer level; + private final Integer status; /** - * 类型名 + * 名字 */ private final String name; @@ -34,4 +35,5 @@ public enum PromotionLevelEnum implements IntArrayValuable { public int[] array() { return ARRAYS; } + } diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java new file mode 100644 index 0000000000..ce79741425 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.promotion.enums.coupon; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 优惠劵领取方式 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum CouponTakeTypeEnum implements IntArrayValuable { + + BY_USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取 + BY_ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵 + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTakeTypeEnum::getValue).toArray(); + + /** + * 值 + */ + private final Integer value; + /** + * 名字 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTemplateValidityTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTemplateValidityTypeEnum.java new file mode 100644 index 0000000000..391515de39 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTemplateValidityTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.promotion.enums.coupon; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 优惠劵模板的有限期类型的枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum CouponTemplateValidityTypeEnum implements IntArrayValuable { + + DATE(1, "固定日期"), + TERM(2, "领取之后"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTemplateValidityTypeEnum::getType).toArray(); + + /** + * 值 + */ + private final Integer type; + /** + * 名字 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/pom.xml b/yudao-module-mall/yudao-module-promotion-biz/pom.xml similarity index 81% rename from yudao-module-mall/yudao-module-market-biz/pom.xml rename to yudao-module-mall/yudao-module-promotion-biz/pom.xml index e3f9f06702..266cb15115 100644 --- a/yudao-module-mall/yudao-module-market-biz/pom.xml +++ b/yudao-module-mall/yudao-module-promotion-biz/pom.xml @@ -9,7 +9,7 @@ 4.0.0 jar - yudao-module-market-biz + yudao-module-promotion-biz ${project.artifactId} @@ -21,7 +21,17 @@ cn.iocoder.boot - yudao-module-market-api + yudao-module-promotion-api + ${revision} + + + cn.iocoder.boot + yudao-module-product-api + ${revision} + + + cn.iocoder.boot + yudao-module-member-api ${revision} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java new file mode 100644 index 0000000000..349eba1ff3 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.promotion.api.coupon; + + +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 优惠劵 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class CouponApiImpl implements CouponApi { + + @Resource + private CouponService couponService; + + @Override + public void useCoupon(CouponUseReqDTO useReqDTO) { + couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(), + useReqDTO.getOrderId()); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/package-info.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/package-info.java new file mode 100644 index 0000000000..4e3ce77a84 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.promotion.api.discount; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApiImpl.java new file mode 100644 index 0000000000..3c415f1c44 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/price/PriceApiImpl.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.promotion.api.price; + +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.promotion.service.price.PriceService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 价格 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class PriceApiImpl implements PriceApi { + + @Resource + private PriceService priceService; + + @Override + public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { + return priceService.calculatePrice(calculateReqDTO); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/BannerController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/BannerController.java similarity index 87% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/BannerController.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/BannerController.java index 932ead7def..0fbef12d58 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/BannerController.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/BannerController.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner; +package cn.iocoder.yudao.module.promotion.controller.admin.banner; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.*; -import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; -import cn.iocoder.yudao.module.market.service.banner.BannerService; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.*; +import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.service.banner.BannerService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerBaseVO.java similarity index 94% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerBaseVO.java index f5f37dbbc6..3083856f57 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerBaseVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner.vo; +package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerCreateReqVO.java similarity index 82% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerCreateReqVO.java index 95224e2090..c058fd8c29 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerCreateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner.vo; +package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo; import io.swagger.annotations.ApiModel; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerPageReqVO.java similarity index 93% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerPageReqVO.java index 3a903fa5da..5b7bd5f31a 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner.vo; +package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java similarity index 89% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java index 64323e777a..6b50110e84 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner.vo; +package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerUpdateReqVO.java similarity index 88% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerUpdateReqVO.java index 1f2d7ce801..971a873531 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerUpdateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.admin.banner.vo; +package cn.iocoder.yudao.module.promotion.controller.admin.banner.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java new file mode 100755 index 0000000000..0a6facbc9e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Api(tags = "管理后台 - 优惠劵") +@RestController +@RequestMapping("/promotion/coupon") +@Validated +public class CouponController { + + @Resource + private CouponService couponService; + @Resource + private MemberUserApi memberUserApi; + +// @GetMapping("/get") +// @ApiOperation("获得优惠劵") +// @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) +// @PreAuthorize("@ss.hasPermission('promotion:coupon:query')") +// public CommonResult getCoupon(@RequestParam("id") Long id) { +// CouponDO coupon = couponService.getCoupon(id); +// return success(CouponConvert.INSTANCE.convert(coupon)); +// } + + @DeleteMapping("/delete") + @ApiOperation("回收优惠劵") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:coupon:delete')") + public CommonResult deleteCoupon(@RequestParam("id") Long id) { + couponService.deleteCoupon(id); + return success(true); + } + + @GetMapping("/page") + @ApiOperation("获得优惠劵分页") + @PreAuthorize("@ss.hasPermission('promotion:coupon:query')") + public CommonResult> getCouponPage(@Valid CouponPageReqVO pageVO) { + PageResult pageResult = couponService.getCouponPage(pageVO); + PageResult pageResulVO = CouponConvert.INSTANCE.convertPage(pageResult); + if (CollUtil.isEmpty(pageResulVO.getList())) { + return success(pageResulVO); + } + // 读取用户信息,进行拼接 + Set userIds = convertSet(pageResult.getList(), CouponDO::getUserId); + Map userMap = memberUserApi.getUserMap(userIds); + pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(), + userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname()))); + return success(pageResulVO); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java new file mode 100755 index 0000000000..76efd2daaf --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.*; +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; +import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 优惠劵模板") +@RestController +@RequestMapping("/promotion/coupon-template") +@Validated +public class CouponTemplateController { + + @Resource + private CouponTemplateService couponTemplateService; + + @PostMapping("/create") + @ApiOperation("创建优惠劵模板") + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:create')") + public CommonResult createCouponTemplate(@Valid @RequestBody CouponTemplateCreateReqVO createReqVO) { + return success(couponTemplateService.createCouponTemplate(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新优惠劵模板") + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:update')") + public CommonResult updateCouponTemplate(@Valid @RequestBody CouponTemplateUpdateReqVO updateReqVO) { + couponTemplateService.updateCouponTemplate(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @ApiOperation("更新优惠劵模板状态") + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:update')") + public CommonResult updateCouponTemplateStatus(@Valid @RequestBody CouponTemplateUpdateStatusReqVO reqVO) { + couponTemplateService.updateCouponTemplateStatus(reqVO.getId(), reqVO.getStatus()); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除优惠劵模板") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:delete')") + public CommonResult deleteCouponTemplate(@RequestParam("id") Long id) { + couponTemplateService.deleteCouponTemplate(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得优惠劵模板") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:query')") + public CommonResult getCouponTemplate(@RequestParam("id") Long id) { + CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(id); + return success(CouponTemplateConvert.INSTANCE.convert(couponTemplate)); + } + + @GetMapping("/page") + @ApiOperation("获得优惠劵模板分页") + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:query')") + public CommonResult> getCouponTemplatePage(@Valid CouponTemplatePageReqVO pageVO) { + PageResult pageResult = couponTemplateService.getCouponTemplatePage(pageVO); + return success(CouponTemplateConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java new file mode 100755 index 0000000000..269b2b2caa --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; + +/** +* 优惠劵 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class CouponBaseVO { + + // ========== 基本信息 BEGIN ========== + @ApiModelProperty(value = "优惠劵模板编号", required = true, example = "1024") + @NotNull(message = "优惠劵模板编号不能为空") + private Integer templateId; + + @ApiModelProperty(value = "优惠劵名", required = true, example = "春节送送送") + @NotNull(message = "优惠劵名不能为空") + private String name; + + @ApiModelProperty(value = "优惠码状态", required = true, example = "1", notes = "参见 CouponStatusEnum 枚举") + private Integer status; + + // ========== 基本信息 END ========== + + // ========== 领取情况 BEGIN ========== + @ApiModelProperty(value = "用户编号", required = true, example = "1") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @ApiModelProperty(value = "领取方式", required = true, example = "1", notes = "参见 CouponTakeTypeEnum 枚举类") + @NotNull(message = "领取方式不能为空") + private Integer takeType; + // ========== 领取情况 END ========== + + // ========== 使用规则 BEGIN ========== + @ApiModelProperty(value = "是否设置满多少金额可用", required = true, example = "100", notes = "单位:分;0 - 不限制") + @NotNull(message = "是否设置满多少金额可用不能为空") + private Integer usePrice; + + @ApiModelProperty(value = "固定日期 - 生效开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime validStartTime; + + @ApiModelProperty(value = "固定日期 - 生效结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime validEndTime; + + @ApiModelProperty(value = "商品范围", required = true, example = "1", notes = "参见 PromotionProductScopeEnum 枚举类") + @NotNull(message = "商品范围不能为空") + @InEnum(PromotionProductScopeEnum.class) + private Integer productScope; + + @ApiModelProperty(value = "商品 SPU 编号的数组", example = "1,3") + private List productSpuIds; + // ========== 使用规则 END ========== + + // ========== 使用效果 BEGIN ========== + @ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举") + @NotNull(message = "优惠类型不能为空") + @InEnum(PromotionDiscountTypeEnum.class) + private Integer discountType; + + @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80") + private Integer discountPercent; + + @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分") + @Min(value = 0, message = "优惠金额需要大于等于 0") + private Integer discountPrice; + + @ApiModelProperty(value = "折扣上限", example = "100", notes = "单位:分,仅在 discountType 为 PERCENT 使用") + private Integer discountLimitPrice; + // ========== 使用效果 END ========== + + // ========== 使用情况 BEGIN ========== + + @ApiModelProperty(value = "使用订单号", example = "4096") + private Long useOrderId; + + @ApiModelProperty(value = "使用时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime useTime; + + // ========== 使用情况 END ========== + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageItemRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageItemRespVO.java new file mode 100755 index 0000000000..88bc364bd3 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageItemRespVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 优惠劵分页的每一项 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponPageItemRespVO extends CouponRespVO { + + @ApiModelProperty(value = "用户昵称", example = "老芋艿") + private String nickname; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java new file mode 100755 index 0000000000..0f9fbb40ee --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 优惠劵分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponPageReqVO extends PageParam { + + @ApiModelProperty(value = "优惠劵模板编号", example = "2048") + private Long templateId; + + @ApiModelProperty(value = "优惠码状态", example = "1", notes = "参见 CouponStatusEnum 枚举") + private Integer status; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + + @ApiModelProperty(value = "用户昵称", example = "芋艿", notes = "模糊匹配") + private String nickname; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponRespVO.java new file mode 100755 index 0000000000..76e075883e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@ApiModel("管理后台 - 优惠劵 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponRespVO extends CouponBaseVO { + + @ApiModelProperty(value = "优惠劵编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java new file mode 100755 index 0000000000..e54a33ce03 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java @@ -0,0 +1,154 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +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.CouponTemplateValidityTypeEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; + +/** +* 优惠劵模板 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class CouponTemplateBaseVO { + + @ApiModelProperty(value = "优惠劵名", required = true, example = "春节送送送") + @NotNull(message = "优惠劵名不能为空") + private String name; + + @ApiModelProperty(value = "发行总量", required = true, example = "1024", notes = "-1 - 则表示不限制发放数量") + @NotNull(message = "发行总量不能为空") + private Integer totalCount; + + @ApiModelProperty(value = "每人限领个数", required = true, example = "66", notes = "-1 - 则表示不限制") + @NotNull(message = "每人限领个数不能为空") + private Integer takeLimitCount; + + @ApiModelProperty(value = "领取方式", required = true, example = "1", notes = "参见 CouponTakeTypeEnum 枚举类") + @NotNull(message = "领取方式不能为空") + private Integer takeType; + + @ApiModelProperty(value = "是否设置满多少金额可用", required = true, example = "100", notes = "单位:分;0 - 不限制") + @NotNull(message = "是否设置满多少金额可用不能为空") + private Integer usePrice; + + @ApiModelProperty(value = "商品范围", required = true, example = "1", notes = "参见 PromotionProductScopeEnum 枚举类") + @NotNull(message = "商品范围不能为空") + @InEnum(PromotionProductScopeEnum.class) + private Integer productScope; + + @ApiModelProperty(value = "商品 SPU 编号的数组", example = "1,3") + private List productSpuIds; + + @ApiModelProperty(value = "生效日期类型", required = true, example = "1") + @NotNull(message = "生效日期类型不能为空") + @InEnum(CouponTemplateValidityTypeEnum.class) + private Integer validityType; + + @ApiModelProperty(value = "固定日期 - 生效开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime validStartTime; + + @ApiModelProperty(value = "固定日期 - 生效结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime validEndTime; + + @ApiModelProperty(value = "领取日期 - 开始天数") + @Min(value = 0L, message = "开始天数必须大于 0") + private Integer fixedStartTerm; + + @ApiModelProperty(value = "领取日期 - 结束天数") + @Min(value = 1L, message = "开始天数必须大于 1") + private Integer fixedEndTerm; + + @ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举") + @NotNull(message = "优惠类型不能为空") + @InEnum(PromotionDiscountTypeEnum.class) + private Integer discountType; + + @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80") + private Integer discountPercent; + + @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分") + @Min(value = 0, message = "优惠金额需要大于等于 0") + private Integer discountPrice; + + @ApiModelProperty(value = "折扣上限", example = "100", notes = "单位:分,仅在 discountType 为 PERCENT 使用") + private Integer discountLimitPrice; + + @AssertTrue(message = "商品 SPU 编号的数组不能为空") + @JsonIgnore + public boolean isProductSpuIdsValid() { + return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时,可以为空 + || CollUtil.isNotEmpty(productSpuIds); + } + + @AssertTrue(message = "生效开始时间不能为空") + @JsonIgnore + public boolean isValidStartTimeValid() { + return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType()) + || validStartTime != null; + } + + @AssertTrue(message = "生效结束时间不能为空") + @JsonIgnore + public boolean isValidEndTimeValid() { + return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType()) + || validEndTime != null; + } + + @AssertTrue(message = "开始天数不能为空") + @JsonIgnore + public boolean isFixedStartTermValid() { + return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType()) + || fixedStartTerm != null; + } + + @AssertTrue(message = "结束天数不能为空") + @JsonIgnore + public boolean isFixedEndTermValid() { + return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType()) + || fixedEndTerm != null; + } + + @AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99") + @JsonIgnore + public boolean isDiscountPercentValid() { + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType()) + || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99); + } + + @AssertTrue(message = "优惠金额不能为空") + @JsonIgnore + public boolean isDiscountPriceValid() { + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType()) + || discountPrice != null; + } + + @AssertTrue(message = "折扣上限不能为空") + @JsonIgnore + public boolean isDiscountLimitPriceValid() { + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType()) + || discountLimitPrice != null; + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateCreateReqVO.java new file mode 100755 index 0000000000..91bfb0a5fb --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 优惠劵模板创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTemplateCreateReqVO extends CouponTemplateBaseVO { + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java new file mode 100755 index 0000000000..a6c3ec6ae2 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 优惠劵模板分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTemplatePageReqVO extends PageParam { + + @ApiModelProperty(value = "优惠劵名", example = "你好") + private String name; + + @ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举类") + private Integer status; + + @ApiModelProperty(value = "优惠类型", example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举") + private Integer discountType; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateRespVO.java new file mode 100755 index 0000000000..7182733e70 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateRespVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@ApiModel("管理后台 - 优惠劵模板 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTemplateRespVO extends CouponTemplateBaseVO { + + @ApiModelProperty(value = "模板编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "状态", required = true, example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @ApiModelProperty(value = "领取优惠券的数量", required = true, example = "1024") + private Integer takeCount; + + @ApiModelProperty(value = "使用优惠券的次数", required = true, example = "2048") + private Integer useCount; + + @ApiModelProperty(value = "创建时间", required = true) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateReqVO.java new file mode 100755 index 0000000000..8e5e8723a0 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 优惠劵模板更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTemplateUpdateReqVO extends CouponTemplateBaseVO { + + @ApiModelProperty(value = "模板编号", required = true, example = "1024") + @NotNull(message = "模板编号不能为空") + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateStatusReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateStatusReqVO.java new file mode 100644 index 0000000000..32e0206378 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateStatusReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 优惠劵模板更新状态 Request VO") +@Data +public class CouponTemplateUpdateStatusReqVO { + + @ApiModelProperty(value = "优惠劵模板编号", required = true, example = "1024") + @NotNull(message = "优惠劵模板编号不能为空") + private Long id; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 CommonStatusEnum 枚举") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java new file mode 100755 index 0000000000..332a1b8225 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*; +import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert; +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.service.discount.DiscountActivityService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 限时折扣活动") +@RestController +@RequestMapping("/promotion/discount-activity") +@Validated +public class DiscountActivityController { + + @Resource + private DiscountActivityService discountActivityService; + + @PostMapping("/create") + @ApiOperation("创建限时折扣活动") + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:create')") + public CommonResult createDiscountActivity(@Valid @RequestBody DiscountActivityCreateReqVO createReqVO) { + return success(discountActivityService.createDiscountActivity(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新限时折扣活动") + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:update')") + public CommonResult updateDiscountActivity(@Valid @RequestBody DiscountActivityUpdateReqVO updateReqVO) { + discountActivityService.updateDiscountActivity(updateReqVO); + return success(true); + } + + @PutMapping("/close") + @ApiOperation("关闭限时折扣活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:close')") + public CommonResult closeRewardActivity(@RequestParam("id") Long id) { + discountActivityService.closeRewardActivity(id); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除限时折扣活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:delete')") + public CommonResult deleteDiscountActivity(@RequestParam("id") Long id) { + discountActivityService.deleteDiscountActivity(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得限时折扣活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:query')") + public CommonResult getDiscountActivity(@RequestParam("id") Long id) { + DiscountActivityDO discountActivity = discountActivityService.getDiscountActivity(id); + if (discountActivity == null) { + return success(null); + } + // 拼接结果 + List discountProducts = discountActivityService.getDiscountProductsByActivityId(id); + return success(DiscountActivityConvert.INSTANCE.convert(discountActivity, discountProducts)); + } + + @GetMapping("/page") + @ApiOperation("获得限时折扣活动分页") + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:query')") + public CommonResult> getDiscountActivityPage(@Valid DiscountActivityPageReqVO pageVO) { + PageResult pageResult = discountActivityService.getDiscountActivityPage(pageVO); + return success(DiscountActivityConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java new file mode 100755 index 0000000000..60a65ec59c --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +/** +* 限时折扣活动 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class DiscountActivityBaseVO { + + @ApiModelProperty(value = "活动标题", required = true, example = "一个标题") + @NotNull(message = "活动标题不能为空") + private String name; + + @ApiModelProperty(value = "开始时间", required = true) + @NotNull(message = "开始时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @ApiModelProperty(value = "结束时间", required = true) + @NotNull(message = "结束时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + + @ApiModelProperty(value = "备注", example = "我是备注") + private String remark; + + @ApiModel("商品") + @Data + public static class Product { + + @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1") + @NotNull(message = "商品 SPU 编号不能为空") + private Long spuId; + + @ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1") + @NotNull(message = "商品 SKU 编号不能为空") + private Long skuId; + + @ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举") + @NotNull(message = "优惠类型不能为空") + @InEnum(PromotionDiscountTypeEnum.class) + private Integer discountType; + + @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80") + private Integer discountPercent; + + @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分") + @Min(value = 0, message = "优惠金额需要大于等于 0") + private Integer discountPrice; + + @AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99") + @JsonIgnore + public boolean isDiscountPercentValid() { + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType()) + || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99); + } + + @AssertTrue(message = "优惠金额不能为空") + @JsonIgnore + public boolean isDiscountPriceValid() { + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType()) + || discountPrice != null; + } + + } +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java new file mode 100755 index 0000000000..35de6faf20 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@ApiModel("管理后台 - 限时折扣活动创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DiscountActivityCreateReqVO extends DiscountActivityBaseVO { + + /** + * 商品列表 + */ + @NotEmpty(message = "商品列表不能为空") + @Valid + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityDetailRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityDetailRespVO.java new file mode 100755 index 0000000000..1dfdeec188 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityDetailRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@ApiModel("管理后台 - 限时折扣活动的详细 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DiscountActivityDetailRespVO extends DiscountActivityRespVO { + + /** + * 商品列表 + */ + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityPageReqVO.java new file mode 100755 index 0000000000..0e3406f076 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityPageReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 限时折扣活动分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DiscountActivityPageReqVO extends PageParam { + + @ApiModelProperty(value = "活动标题", example = "一个标题") + private String name; + + @ApiModelProperty(value = "活动状态", example = "1") + private Integer status; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityRespVO.java new file mode 100755 index 0000000000..0422908b59 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@ApiModel("管理后台 - 限时折扣活动 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DiscountActivityRespVO extends DiscountActivityBaseVO { + + @ApiModelProperty(value = "活动编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "活动状态", required = true, example = "1") + @NotNull(message = "活动状态不能为空") + private Integer status; + + @ApiModelProperty(value = "创建时间", required = true) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java new file mode 100755 index 0000000000..0f3b6e435a --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 限时折扣活动更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO { + + @ApiModelProperty(value = "活动编号", required = true, example = "1024") + @NotNull(message = "活动编号不能为空") + private Long id; + + /** + * 商品列表 + */ + @NotEmpty(message = "商品列表不能为空") + @Valid + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java new file mode 100755 index 0000000000..043571e5e8 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 满减送活动") +@RestController +@RequestMapping("/promotion/reward-activity") +@Validated +public class RewardActivityController { + + @Resource + private RewardActivityService rewardActivityService; + + @PostMapping("/create") + @ApiOperation("创建满减送活动") + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:create')") + public CommonResult createRewardActivity(@Valid @RequestBody RewardActivityCreateReqVO createReqVO) { + return success(rewardActivityService.createRewardActivity(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新满减送活动") + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:update')") + public CommonResult updateRewardActivity(@Valid @RequestBody RewardActivityUpdateReqVO updateReqVO) { + rewardActivityService.updateRewardActivity(updateReqVO); + return success(true); + } + + @PutMapping("/close") + @ApiOperation("关闭满减送活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:close')") + public CommonResult closeRewardActivity(@RequestParam("id") Long id) { + rewardActivityService.closeRewardActivity(id); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除满减送活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:delete')") + public CommonResult deleteRewardActivity(@RequestParam("id") Long id) { + rewardActivityService.deleteRewardActivity(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得满减送活动") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:query')") + public CommonResult getRewardActivity(@RequestParam("id") Long id) { + RewardActivityDO rewardActivity = rewardActivityService.getRewardActivity(id); + return success(RewardActivityConvert.INSTANCE.convert(rewardActivity)); + } + + @GetMapping("/page") + @ApiOperation("获得满减送活动分页") + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:query')") + public CommonResult> getRewardActivityPage(@Valid RewardActivityPageReqVO pageVO) { + PageResult pageResult = rewardActivityService.getRewardActivityPage(pageVO); + return success(RewardActivityConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java new file mode 100755 index 0000000000..ed28021245 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.Valid; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Future; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +/** +* 满减送活动 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class RewardActivityBaseVO { + + @ApiModelProperty(value = "活动标题", required = true, example = "满啦满啦") + @NotNull(message = "活动标题不能为空") + private String name; + + @ApiModelProperty(value = "开始时间", required = true) + @NotNull(message = "开始时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @ApiModelProperty(value = "结束时间", required = true) + @NotNull(message = "结束时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Future(message = "结束时间必须大于当前时间") + private LocalDateTime endTime; + + @ApiModelProperty(value = "备注", example = "biubiubiu") + private String remark; + + @ApiModelProperty(value = "条件类型", required = true, example = "1") + @NotNull(message = "条件类型不能为空") + @InEnum(value = PromotionConditionTypeEnum.class, message = "条件类型必须是 {value}") + private Integer conditionType; + + @ApiModelProperty(value = "商品范围", required = true, example = "1") + @NotNull(message = "商品范围不能为空") + @InEnum(value = PromotionConditionTypeEnum.class, message = "商品范围必须是 {value}") + private Integer productScope; + + @ApiModelProperty(value = "商品 SPU 编号的数组", example = "1,2,3") + private List productSpuIds; + + /** + * 优惠规则的数组 + */ + @Valid // 校验下子对象 + private List rules; + + @ApiModel("优惠规则") + @Data + public static class Rule { + + @ApiModelProperty(value = "优惠门槛", required = true, example = "100", notes = "1. 满 N 元,单位:分; 2. 满 N 件") + @Min(value = 1L, message = "优惠门槛必须大于等于 1") + private Integer limit; + + @ApiModelProperty(value = "优惠价格", required = true, example = "100", notes = "单位:分") + @Min(value = 1L, message = "优惠价格必须大于等于 1") + private Integer discountPrice; + + @ApiModelProperty(value = "是否包邮", required = true, example = "true") + private Boolean freeDelivery; + + @ApiModelProperty(value = "赠送的积分", required = true, example = "100") + @Min(value = 1L, message = "赠送的积分必须大于等于 1") + private Integer point; + + @ApiModelProperty(value = "赠送的优惠劵编号的数组", example = "1,2,3") + private List couponIds; + + @ApiModelProperty(value = "赠送的优惠卷数量的数组", example = "1,2,3") + private List couponCounts; + + @AssertTrue(message = "优惠劵和数量必须一一对应") + @JsonIgnore + public boolean isCouponCountsValid() { + return CollUtil.size(couponCounts) == CollUtil.size(couponCounts); + } + + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityCreateReqVO.java new file mode 100755 index 0000000000..6a7251021e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; + +import lombok.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 满减送活动创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RewardActivityCreateReqVO extends RewardActivityBaseVO { + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityPageReqVO.java new file mode 100755 index 0000000000..5a8d2ba6db --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityPageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; + +import lombok.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@ApiModel("管理后台 - 满减送活动分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RewardActivityPageReqVO extends PageParam { + + @ApiModelProperty(value = "活动标题", example = "满啦满啦") + private String name; + + @ApiModelProperty(value = "活动状态", example = "1") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityRespVO.java new file mode 100755 index 0000000000..cb90e21cb3 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityRespVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@ApiModel("管理后台 - 满减送活动 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RewardActivityRespVO extends RewardActivityBaseVO { + + @ApiModelProperty(value = "活动编号", required = true, example = "1024") + private Integer id; + + @ApiModelProperty(value = "活动状态", required = true, example = "1") + private Integer status; + + @ApiModelProperty(value = "创建时间", required = true) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityUpdateReqVO.java new file mode 100755 index 0000000000..659deee3d6 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityUpdateReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; + +import lombok.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 满减送活动更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RewardActivityUpdateReqVO extends RewardActivityBaseVO { + + @ApiModelProperty(value = "活动编号", required = true, example = "1024") + @NotNull(message = "活动编号不能为空") + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/AppMarketTestController.java similarity index 92% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/AppMarketTestController.java index 7d45b87a96..d6d6689efc 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/AppMarketTestController.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.controller.app; +package cn.iocoder.yudao.module.promotion.controller.app; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import io.swagger.annotations.Api; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/banner/AppBannerController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java similarity index 77% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/banner/AppBannerController.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java index 9af420a611..4edd1ed04f 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/banner/AppBannerController.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.market.controller.app.banner; +package cn.iocoder.yudao.module.promotion.controller.app.banner; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerRespVO; -import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; -import cn.iocoder.yudao.module.market.service.banner.BannerService; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO; +import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.service.banner.BannerService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/banner/BannerConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/banner/BannerConvert.java similarity index 60% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/banner/BannerConvert.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/banner/BannerConvert.java index a78650f574..87d2da1878 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/banner/BannerConvert.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/banner/BannerConvert.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.market.convert.banner; +package cn.iocoder.yudao.module.promotion.convert.banner; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerRespVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java new file mode 100755 index 0000000000..281318f7d1 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.promotion.convert.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 优惠劵 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface CouponConvert { + + CouponConvert INSTANCE = Mappers.getMapper(CouponConvert.class); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java new file mode 100755 index 0000000000..22d78f46fd --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.promotion.convert.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 优惠劵模板 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface CouponTemplateConvert { + + CouponTemplateConvert INSTANCE = Mappers.getMapper(CouponTemplateConvert.class); + + CouponTemplateDO convert(CouponTemplateCreateReqVO bean); + + CouponTemplateDO convert(CouponTemplateUpdateReqVO bean); + + CouponTemplateRespVO convert(CouponTemplateDO bean); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java new file mode 100755 index 0000000000..07d2e03ab7 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.promotion.convert.discount; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*; +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.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 限时折扣活动 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface DiscountActivityConvert { + + DiscountActivityConvert INSTANCE = Mappers.getMapper(DiscountActivityConvert.class); + + DiscountActivityDO convert(DiscountActivityCreateReqVO bean); + + DiscountActivityDO convert(DiscountActivityUpdateReqVO bean); + + DiscountActivityRespVO convert(DiscountActivityDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + DiscountProductDetailBO convert(DiscountProductDO product); + + default List convertList(List products, Map activityMap) { + return CollectionUtils.convertList(products, product -> { + DiscountProductDetailBO detail = convert(product); + MapUtils.findAndThen(activityMap, product.getActivityId(), activity -> { + detail.setActivityName(activity.getName()); + }); + return detail; + }); + } + + DiscountProductDO convert(DiscountActivityBaseVO.Product bean); + + DiscountActivityDetailRespVO convert(DiscountActivityDO activity, List products); + + // =========== 比较是否相等 ========== + /** + * 比较两个限时折扣商品是否相等 + * + * @param productDO 数据库中的商品 + * @param productVO 前端传入的商品 + * @return 是否匹配 + */ + @SuppressWarnings("DuplicatedCode") + default boolean isEquals(DiscountProductDO productDO, DiscountActivityBaseVO.Product productVO) { + if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId()) + || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId()) + || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) { + return false; + } + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) { + return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice()); + } + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) { + return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent()); + } + return true; + } + + /** + * 比较两个限时折扣商品是否相等 + * 注意,比较时忽略 id 编号 + * + * @param productDO 商品 1 + * @param productVO 商品 2 + * @return 是否匹配 + */ + @SuppressWarnings("DuplicatedCode") + default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) { + if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId()) + || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId()) + || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) { + return false; + } + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) { + return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice()); + } + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) { + return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent()); + } + return true; + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/price/PriceConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/price/PriceConvert.java new file mode 100644 index 0000000000..8e4b24666f --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/price/PriceConvert.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.promotion.convert.price; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Mapper +public interface PriceConvert { + + PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class); + + default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List skuList) { + // 创建 PriceCalculateRespDTO 对象 + PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO(); + // 创建它的 Order 属性 + PriceCalculateRespDTO.Order order = new PriceCalculateRespDTO.Order().setOriginalPrice(0).setDiscountPrice(0) + .setCouponPrice(0).setPointPrice(0).setDeliveryPrice(0).setPayPrice(0) + .setItems(new ArrayList<>()).setCouponId(calculateReqDTO.getCouponId()); + priceCalculate.setOrder(order).setPromotions(new ArrayList<>()); + // 创建它的 OrderItem 属性 + Map skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(), + PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount); + skuList.forEach(sku -> { + Integer count = skuIdCountMap.get(sku.getId()); + PriceCalculateRespDTO.OrderItem orderItem = new PriceCalculateRespDTO.OrderItem() + .setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count) + .setOriginalUnitPrice(sku.getPrice()).setOriginalPrice(sku.getPrice() * count) + .setDiscountPrice(0).setOrderPartPrice(0); + orderItem.setPayPrice(orderItem.getOriginalPrice()).setOrderDividePrice(orderItem.getOriginalPrice()); + priceCalculate.getOrder().getItems().add(orderItem); + // 补充价格信息到 Order 中 + order.setOriginalPrice(order.getOriginalPrice() + orderItem.getOriginalPrice()) + .setOrderPrice(order.getOriginalPrice()).setPayPrice(order.getOriginalPrice()); + }); + return priceCalculate; + } + + CouponMeetRespDTO convert(CouponDO coupon); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java new file mode 100755 index 0000000000..5343656ed9 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.promotion.convert.reward; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 满减送活动 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface RewardActivityConvert { + + RewardActivityConvert INSTANCE = Mappers.getMapper(RewardActivityConvert.class); + + RewardActivityDO convert(RewardActivityCreateReqVO bean); + + RewardActivityDO convert(RewardActivityUpdateReqVO bean); + + RewardActivityRespVO convert(RewardActivityDO bean); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/banner/BannerDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/banner/BannerDO.java similarity index 93% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/banner/BannerDO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/banner/BannerDO.java index 1f48f7144c..585462b956 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/banner/BannerDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/banner/BannerDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.market.dal.dataobject.banner; +package cn.iocoder.yudao.module.promotion.dal.dataobject.banner; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.TableName; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java new file mode 100644 index 0000000000..7971392d43 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java @@ -0,0 +1,139 @@ +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.module.promotion.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 优惠劵 DO + * + * @author 芋道源码 + */ +@TableName(value = "promotion_coupon", autoResultMap = true) +@KeySequence("promotion_coupon_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class CouponDO extends BaseDO { + + // ========== 基本信息 BEGIN ========== + /** + * 优惠劵编号 + */ + private Long id; + /** + * 优惠劵模板编号 + * + * 关联 {@link CouponTemplateDO#getId()} + */ + private Integer templateId; + /** + * 优惠劵名 + * + * 冗余 {@link CouponTemplateDO#getName()} + */ + private String name; + /** + * 优惠码状态 + * + * 枚举 {@link CouponStatusEnum} + */ + private Integer status; + + // ========== 基本信息 END ========== + + // ========== 领取情况 BEGIN ========== + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 字段 + */ + private Long userId; + /** + * 领取类型 + * + * 枚举 {@link CouponTakeTypeEnum} + */ + private Integer takeType; + // ========== 领取情况 END ========== + + // ========== 使用规则 BEGIN ========== + /** + * 是否设置满多少金额可用,单位:分 + * + * 冗余 {@link CouponTemplateDO#getUsePrice()} + */ + private Integer usePrice; + /** + * 生效开始时间 + */ + private LocalDateTime validStartTime; + /** + * 生效结束时间 + */ + private LocalDateTime validEndTime; + /** + * 商品范围 + * + * 枚举 {@link PromotionProductScopeEnum} + */ + private Integer productScope; + /** + * 商品 SPU 编号的数组 + * + * 冗余 {@link CouponTemplateDO#getProductSpuIds()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List productSpuIds; + // ========== 使用规则 END ========== + + // ========== 使用效果 BEGIN ========== + /** + * 折扣类型 + * + * 冗余 {@link CouponTemplateDO#getDiscountType()} + */ + private Integer discountType; + /** + * 折扣百分比 + * + * 冗余 {@link CouponTemplateDO#getDiscountPercent()} + */ + private Integer discountPercent; + /** + * 优惠金额,单位:分 + * + * 冗余 {@link CouponTemplateDO#getDiscountPrice()} + */ + private Integer discountPrice; + /** + * 折扣上限,仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效 + * + * 冗余 {@link CouponTemplateDO#getDiscountLimitPrice()} + */ + private Integer discountLimitPrice; + // ========== 使用效果 END ========== + + // ========== 使用情况 BEGIN ========== + /** + * 使用订单号 + */ + private Long useOrderId; + /** + * 使用时间 + */ + private LocalDateTime useTime; + + // ========== 使用情况 END ========== + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java new file mode 100644 index 0000000000..93f9ace352 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java @@ -0,0 +1,162 @@ +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.module.promotion.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 优惠劵模板 DO + * + * 当用户领取时,会生成 {@link CouponDO} 优惠劵 + * + * @author 芋道源码 + */ +@TableName(value = "promotion_coupon_template", autoResultMap = true) +@KeySequence("promotion_coupon_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class CouponTemplateDO extends BaseDO { + + // ========== 基本信息 BEGIN ========== + /** + * 模板编号,自增唯一 + */ + @TableId + private Long id; + /** + * 优惠劵名 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + // ========== 基本信息 END ========== + + // ========== 领取规则 BEGIN ========== + /** + * 发放数量 + * + * -1 - 则表示不限制发放数量 + */ + private Integer totalCount; + /** + * 每人限领个数 + * + * -1 - 则表示不限制 + */ + private Integer takeLimitCount; + /** + * 领取方式 + * + * 枚举 {@link CouponTakeTypeEnum} + */ + private Integer takeType; + // ========== 领取规则 END ========== + + // ========== 使用规则 BEGIN ========== + /** + * 是否设置满多少金额可用,单位:分 + * + * 0 - 不限制 + * 大于 0 - 多少金额可用 + */ + private Integer usePrice; + /** + * 商品范围 + * + * 枚举 {@link PromotionProductScopeEnum} + */ + private Integer productScope; + /** + * 商品 SPU 编号的数组 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List productSpuIds; + /** + * 生效日期类型 + * + * 枚举 {@link CouponTemplateValidityTypeEnum} + */ + private Integer validityType; + /** + * 固定日期 - 生效开始时间 + * + * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#DATE} + */ + private LocalDateTime validStartTime; + /** + * 固定日期 - 生效结束时间 + * + * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#DATE} + */ + private LocalDateTime validEndTime; + /** + * 领取日期 - 开始天数 + * + * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#TERM} + */ + private Integer fixedStartTerm; + /** + * 领取日期 - 结束天数 + * + * 当 {@link #validityType} 为 {@link CouponTemplateValidityTypeEnum#TERM} + */ + private Integer fixedEndTerm; + // ========== 使用规则 END ========== + + // ========== 使用效果 BEGIN ========== + /** + * 折扣类型 + * + * 枚举 {@link PromotionDiscountTypeEnum} + */ + private Integer discountType; + /** + * 折扣百分比 + * + * 例如,80% 为 80 + */ + private Integer discountPercent; + /** + * 优惠金额,单位:分 + * + * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效 + */ + private Integer discountPrice; + /** + * 折扣上限,仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效 + * + * 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。 + */ + private Integer discountLimitPrice; + // ========== 使用效果 END ========== + + // ========== 统计信息 BEGIN ========== + /** + * 领取优惠券的数量 + */ + private Integer takeCount; + /** + * 使用优惠券的次数 + */ + private Integer useCount; + // ========== 统计信息 END ========== + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountActivityDO.java similarity index 89% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountActivityDO.java index ad13f252c0..91071f309d 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountActivityDO.java @@ -1,7 +1,7 @@ -package cn.iocoder.yudao.module.market.dal.dataobject.discount; +package cn.iocoder.yudao.module.promotion.dal.dataobject.discount; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.market.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java similarity index 69% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java index a23638757b..55c924e4f0 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java @@ -1,14 +1,13 @@ -package cn.iocoder.yudao.module.market.dal.dataobject.discount; +package cn.iocoder.yudao.module.promotion.dal.dataobject.discount; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; -import java.time.LocalDateTime; - /** * 限时折扣商品 DO * @@ -43,23 +42,24 @@ public class DiscountProductDO extends BaseDO { * 关联 ProductSkuDO 的 id 编号 */ private Long skuId; + /** - * 开始时间 - */ - private LocalDateTime startTime; - /** - * 结束时间 - */ - private LocalDateTime endTime; - /** - * 销售价格,单位:分 + * 折扣类型 * - * 冗余 ProductSkuDO 的 price 字段 + * 枚举 {@link PromotionDiscountTypeEnum} */ - private Integer originalPrice; + private Integer discountType; /** - * 优惠价格,单位:分 + * 折扣百分比 + * + * 例如,80% 为 80 */ - private Integer promotionPrice; + private Integer discountPercent; + /** + * 优惠金额,单位:分 + * + * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效 + */ + private Integer discountPrice; } diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java similarity index 64% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java index 77a0744003..e825881d1d 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java @@ -1,17 +1,20 @@ -package cn.iocoder.yudao.module.market.dal.dataobject.reward; +package cn.iocoder.yudao.module.promotion.dal.dataobject.reward; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.market.enums.common.PromotionActivityStatusEnum; -import cn.iocoder.yudao.module.market.enums.common.PromotionConditionTypeEnum; -import cn.iocoder.yudao.module.market.enums.common.PromotionProductScopeEnum; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; @@ -68,19 +71,19 @@ public class RewardActivityDO extends BaseDO { /** * 商品 SPU 编号的数组 */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List spuIds; + @TableField(typeHandler = LongListTypeHandler.class) + private List productSpuIds; /** * 优惠规则的数组 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = RuleTypeHandler.class) private List rules; /** * 优惠规则 */ @Data - public static class Rule { + public static class Rule implements Serializable { /** * 优惠门槛 @@ -92,7 +95,7 @@ public class RewardActivityDO extends BaseDO { /** * 优惠价格,单位:分 */ - private Integer promotionPrice; + private Integer discountPrice; /** * 是否包邮 */ @@ -100,7 +103,7 @@ public class RewardActivityDO extends BaseDO { /** * 赠送的积分 */ - private Integer integral; + private Integer point; /** * 赠送的优惠劵编号的数组 */ @@ -112,5 +115,19 @@ public class RewardActivityDO extends BaseDO { } + // TODO @芋艿:可以找一些新的思路 + public static class RuleTypeHandler extends AbstractJsonTypeHandler> { + + @Override + protected List parse(String json) { + return JsonUtils.parseArray(json, Rule.class); + } + + @Override + protected String toJson(List obj) { + return JsonUtils.toJsonString(obj); + } + + } } diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/banner/BannerMapper.java similarity index 78% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/banner/BannerMapper.java index 4d22f61707..d983753655 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/banner/BannerMapper.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.market.dal.mysql.banner; +package cn.iocoder.yudao.module.promotion.dal.mysql.banner; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerPageReqVO; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; import org.apache.ibatis.annotations.Mapper; /** diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java new file mode 100755 index 0000000000..e5ae5f79dc --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 优惠劵 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface CouponMapper extends BaseMapperX { + + default PageResult selectPage(CouponPageReqVO reqVO, Collection userIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId()) + .eqIfPresent(CouponDO::getStatus, reqVO.getStatus()) + .inIfPresent(CouponDO::getUserId, userIds) + .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponDO::getId)); + } + + default List selectListByUserIdAndStatus(Long userId, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(CouponDO::getUserId, userId).eq(CouponDO::getStatus, status)); + } + + default CouponDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(new LambdaQueryWrapperX() + .eq(CouponDO::getId, id).eq(CouponDO::getUserId, userId)); + } + + default int delete(Long id, Collection whereStatuses) { + return update(null, new LambdaUpdateWrapper() + .eq(CouponDO::getId, id).in(CouponDO::getStatus, whereStatuses) + .set(CouponDO::getDeleted, 1)); + } + + default int updateByIdAndStatus(Long id, Integer status, CouponDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(CouponDO::getId, id).eq(CouponDO::getStatus, status)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java new file mode 100755 index 0000000000..7cea814af6 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 优惠劵模板 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface CouponTemplateMapper extends BaseMapperX { + + default PageResult selectPage(CouponTemplatePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(CouponTemplateDO::getName, reqVO.getName()) + .eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus()) + .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType()) + .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponTemplateDO::getId)); + } + + void updateTakeCount(@Param("id") Long id, @Param("incrCount") Integer incrCount); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java new file mode 100755 index 0000000000..534ce627ab --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.discount; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 限时折扣活动 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface DiscountActivityMapper extends BaseMapperX { + + default PageResult selectPage(DiscountActivityPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DiscountActivityDO::getName, reqVO.getName()) + .eqIfPresent(DiscountActivityDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(DiscountActivityDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DiscountActivityDO::getId)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java new file mode 100755 index 0000000000..646b60707f --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.discount; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 限时折扣商城 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface DiscountProductMapper extends BaseMapperX { + + default List selectListBySkuId(Collection skuIds) { + return selectList(DiscountProductDO::getSkuId, skuIds); + } + + default List selectListByActivityId(Long activityId) { + return selectList(DiscountProductDO::getActivityId, activityId); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java new file mode 100755 index 0000000000..2ee8798233 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.reward; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 满减送活动 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface RewardActivityMapper extends BaseMapperX { + + default PageResult selectPage(RewardActivityPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(RewardActivityDO::getName, reqVO.getName()) + .eqIfPresent(RewardActivityDO::getStatus, reqVO.getStatus()) + .orderByDesc(RewardActivityDO::getId)); + } + + default List selectListByStatus(Collection statuses) { + return selectList(RewardActivityDO::getStatus, statuses); + } + + default List selectListByProductScopeAndStatus(Integer productScope, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(RewardActivityDO::getProductScope, productScope) + .eq(RewardActivityDO::getStatus, status)); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/package-info.java similarity index 86% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/package-info.java index 92b9c698dd..c022c62766 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/package-info.java @@ -5,4 +5,4 @@ * 1. Controller URL:以 /promotion/ 开头,避免和其它 Module 冲突 * 2. DataObject 表名:以 promotion_ 开头,方便在数据库中区分 */ -package cn.iocoder.yudao.module.market; +package cn.iocoder.yudao.module.promotion; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerService.java similarity index 72% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerService.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerService.java index 67debdc3d6..d541211be9 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerService.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerService.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.market.service.banner; +package cn.iocoder.yudao.module.promotion.service.banner; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerPageReqVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; import javax.validation.Valid; import java.util.List; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerServiceImpl.java similarity index 72% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java rename to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerServiceImpl.java index 04784ff66c..013ae89923 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerServiceImpl.java @@ -1,12 +1,12 @@ -package cn.iocoder.yudao.module.market.service.banner; +package cn.iocoder.yudao.module.promotion.service.banner; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerPageReqVO; -import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; -import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; -import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; -import cn.iocoder.yudao.module.market.dal.mysql.banner.BannerMapper; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.banner.BannerMapper; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -14,10 +14,10 @@ import javax.annotation.Resource; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.BANNER_NOT_EXISTS; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.BANNER_NOT_EXISTS; /** - * 首页banner 实现类 + * 首页 banner 实现类 * * @author xia */ diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java new file mode 100644 index 0000000000..96b5b5d634 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.promotion.service.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; + +import java.util.List; + +/** + * 优惠劵 Service 接口 + * + * @author 芋道源码 + */ +public interface CouponService { + + /** + * 校验优惠劵,包括状态、有限期 + * + * 1. 如果校验通过,则返回优惠劵信息 + * 2. 如果校验不通过,则直接抛出业务异常 + * + * @param id 优惠劵编号 + * @param userId 用户编号 + * @return 优惠劵信息 + */ + CouponDO validCoupon(Long id, Long userId); + + /** + * 校验优惠劵,包括状态、有限期 + * + * @see #validCoupon(Long, Long) 逻辑相同,只是入参不同 + * + * @param coupon 优惠劵 + */ + void validCoupon(CouponDO coupon); + + /** + * 获得优惠劵分页 + * + * @param pageReqVO 分页查询 + * @return 优惠劵分页 + */ + PageResult getCouponPage(CouponPageReqVO pageReqVO); + + /** + * 使用优惠劵 + * + * @param id 优惠劵编号 + * @param userId 用户编号 + * @param orderId 订单编号 + */ + void useCoupon(Long id, Long userId, Long orderId); + + /** + * 回收优惠劵 + * + * @param id 优惠劵编号 + */ + void deleteCoupon(Long id); + + /** + * 获得用户的优惠劵列表 + * + * @param userId 用户编号 + * @param status 优惠劵状态 + * @return 优惠劵列表 + */ + List getCouponList(Long userId, Integer status); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java new file mode 100644 index 0000000000..55133cdede --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -0,0 +1,123 @@ +package cn.iocoder.yudao.module.promotion.service.coupon; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; +import static java.util.Arrays.asList; + +/** + * 优惠劵 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class CouponServiceImpl implements CouponService { + + @Resource + private CouponTemplateService couponTemplateService; + + @Resource + private CouponMapper couponMapper; + + @Resource + private MemberUserApi memberUserApi; + + @Override + public CouponDO validCoupon(Long id, Long userId) { + CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId); + if (coupon == null) { + throw exception(COUPON_NOT_EXISTS); + } + validCoupon(coupon); + return coupon; + } + + @Override + public void validCoupon(CouponDO coupon) { + // 校验状态 + if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) { + throw exception(COUPON_STATUS_NOT_UNUSED); + } + // 校验有效期;为避免定时器没跑,实际优惠劵已经过期 + if (LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) { + throw exception(COUPON_VALID_TIME_NOT_NOW); + } + } + + @Override + public PageResult getCouponPage(CouponPageReqVO pageReqVO) { + // 获得用户编号 + Set userIds = null; + if (StrUtil.isNotEmpty(pageReqVO.getNickname())) { + userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()), + UserRespDTO::getId); + if (CollUtil.isEmpty(userIds)) { + return PageResult.empty(); + } + } + // 分页查询 + return couponMapper.selectPage(pageReqVO, userIds); + } + + @Override + public void useCoupon(Long id, Long userId, Long orderId) { + // 校验优惠劵 + validCoupon(id, userId); + // 更新状态 + int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(), + new CouponDO().setStatus(CouponStatusEnum.USED.getStatus()) + .setUseOrderId(orderId).setUseTime(LocalDateTime.now())); + if (updateCount == 0) { + throw exception(COUPON_STATUS_NOT_UNUSED); + } + } + + @Override + @Transactional + public void deleteCoupon(Long id) { + // 校验存在 + validateCouponExists(id); + + // 更新优惠劵 + int deleteCount = couponMapper.delete(id, + asList(CouponStatusEnum.UNUSED.getStatus(), CouponStatusEnum.EXPIRE.getStatus())); + if (deleteCount == 0) { + throw exception(COUPON_DELETE_FAIL_USED); + } + // 减少优惠劵模板的领取数量 -1 + couponTemplateService.updateCouponTemplateTakeCount(id, -1); + } + + @Override + public List getCouponList(Long userId, Integer status) { + return couponMapper.selectListByUserIdAndStatus(userId, status); + } + + private void validateCouponExists(Long id) { + if (couponMapper.selectById(id) == null) { + throw exception(COUPON_NOT_EXISTS); + } + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java new file mode 100755 index 0000000000..fdf0189748 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.promotion.service.coupon; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; + +import javax.validation.Valid; + +/** + * 优惠劵模板 Service 接口 + * + * @author 芋道源码 + */ +public interface CouponTemplateService { + + /** + * 创建优惠劵模板 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCouponTemplate(@Valid CouponTemplateCreateReqVO createReqVO); + + /** + * 更新优惠劵模板 + * + * @param updateReqVO 更新信息 + */ + void updateCouponTemplate(@Valid CouponTemplateUpdateReqVO updateReqVO); + + /** + * 更新优惠劵模板的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateCouponTemplateStatus(Long id, Integer status); + + /** + * 删除优惠劵模板 + * + * @param id 编号 + */ + void deleteCouponTemplate(Long id); + + /** + * 获得优惠劵模板 + * + * @param id 编号 + * @return 优惠劵模板 + */ + CouponTemplateDO getCouponTemplate(Long id); + + /** + * 获得优惠劵模板分页 + * + * @param pageReqVO 分页查询 + * @return 优惠劵模板分页 + */ + PageResult getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO); + + /** + * 更新优惠劵模板的领取数量 + * + * @param id 优惠劵模板编号 + * @param incrCount 增加数量 + */ + void updateCouponTemplateTakeCount(Long id, int incrCount); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java new file mode 100755 index 0000000000..1a9cc8bfb4 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.promotion.service.coupon; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO; +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; + +/** + * 优惠劵模板 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class CouponTemplateServiceImpl implements CouponTemplateService { + + @Resource + private CouponTemplateMapper couponTemplateMapper; + + @Override + public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) { + // 插入 + CouponTemplateDO couponTemplate = CouponTemplateConvert.INSTANCE.convert(createReqVO) + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + couponTemplateMapper.insert(couponTemplate); + // 返回 + return couponTemplate.getId(); + } + + @Override + public void updateCouponTemplate(CouponTemplateUpdateReqVO updateReqVO) { + // 校验存在 + CouponTemplateDO couponTemplate = validateCouponTemplateExists(updateReqVO.getId()); + // 校验发放数量不能过小 + if (updateReqVO.getTotalCount() < couponTemplate.getTakeCount()) { + throw exception(COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL, couponTemplate.getTakeCount()); + } + + // 更新 + CouponTemplateDO updateObj = CouponTemplateConvert.INSTANCE.convert(updateReqVO); + couponTemplateMapper.updateById(updateObj); + } + + @Override + public void updateCouponTemplateStatus(Long id, Integer status) { + // 校验存在 + validateCouponTemplateExists(id); + // 更新 + couponTemplateMapper.updateById(new CouponTemplateDO().setId(id).setStatus(status)); + } + + @Override + public void deleteCouponTemplate(Long id) { + // 校验存在 + validateCouponTemplateExists(id); + // 删除 + couponTemplateMapper.deleteById(id); + } + + private CouponTemplateDO validateCouponTemplateExists(Long id) { + CouponTemplateDO couponTemplate = couponTemplateMapper.selectById(id); + if (couponTemplate == null) { + throw exception(COUPON_TEMPLATE_NOT_EXISTS); + } + return couponTemplate; + } + + @Override + public CouponTemplateDO getCouponTemplate(Long id) { + return couponTemplateMapper.selectById(id); + } + + @Override + public PageResult getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO) { + return couponTemplateMapper.selectPage(pageReqVO); + } + + @Override + public void updateCouponTemplateTakeCount(Long id, int incrCount) { + couponTemplateMapper.updateTakeCount(id, incrCount); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java new file mode 100644 index 0000000000..8b6e5895b7 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.promotion.service.discount; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO; +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.service.discount.bo.DiscountProductDetailBO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 限时折扣 Service 接口 + * + * @author 芋道源码 + */ +public interface DiscountActivityService { + + /** + * 基于指定 SKU 编号数组,获得匹配的限时折扣商品 + * + * 注意,匹配的条件,仅仅是日期符合,并且处于开启状态 + * + * @param skuIds SKU 编号数组 + * @return 匹配的限时折扣商品 + */ + Map getMatchDiscountProducts(Collection skuIds); + + /** + * 创建限时折扣活动 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDiscountActivity(@Valid DiscountActivityCreateReqVO createReqVO); + + /** + * 更新限时折扣活动 + * + * @param updateReqVO 更新信息 + */ + void updateDiscountActivity(@Valid DiscountActivityUpdateReqVO updateReqVO); + + /** + * 关闭限时折扣活动 + * + * @param id 编号 + */ + void closeRewardActivity(Long id); + + /** + * 删除限时折扣活动 + * + * @param id 编号 + */ + void deleteDiscountActivity(Long id); + + /** + * 获得限时折扣活动 + * + * @param id 编号 + * @return 限时折扣活动 + */ + DiscountActivityDO getDiscountActivity(Long id); + + /** + * 获得限时折扣活动分页 + * + * @param pageReqVO 分页查询 + * @return 限时折扣活动分页 + */ + PageResult getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO); + + /** + * 获得活动编号,对应对应的商品列表 + * + * @param activityId 活动编号 + * @return 活动的商品列表 + */ + List getDiscountProductsByActivityId(Long activityId); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java new file mode 100644 index 0000000000..df54d44f2c --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java @@ -0,0 +1,196 @@ +package cn.iocoder.yudao.module.promotion.service.discount; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert; +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.mysql.discount.DiscountActivityMapper; +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO; +import cn.iocoder.yudao.module.promotion.util.PromotionUtils; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; +import static java.util.Arrays.asList; + +/** + * 限时折扣 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class DiscountActivityServiceImpl implements DiscountActivityService { + + @Resource + private DiscountActivityMapper discountActivityMapper; + @Resource + private DiscountProductMapper discountProductMapper; + + @Override + public Map getMatchDiscountProducts(Collection skuIds) { + List discountProducts = getRewardProductListBySkuIds(skuIds, singleton(PromotionActivityStatusEnum.RUN.getStatus())); + return convertMap(discountProducts, DiscountProductDetailBO::getSkuId); + } + + @Override + public Long createDiscountActivity(DiscountActivityCreateReqVO createReqVO) { + // 校验商品是否冲突 + validateDiscountActivityProductConflicts(null, createReqVO.getProducts()); + + // 插入活动 + DiscountActivityDO discountActivity = DiscountActivityConvert.INSTANCE.convert(createReqVO) + .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getStartTime(), createReqVO.getEndTime())); + discountActivityMapper.insert(discountActivity); + // 插入商品 + List discountProducts = convertList(createReqVO.getProducts(), + product -> DiscountActivityConvert.INSTANCE.convert(product).setActivityId(discountActivity.getId())); + discountProductMapper.insertBatch(discountProducts); + // 返回 + return discountActivity.getId(); + } + + @Override + public void updateDiscountActivity(DiscountActivityUpdateReqVO updateReqVO) { + // 校验存在 + DiscountActivityDO discountActivity = validateDiscountActivityExists(updateReqVO.getId()); + if (discountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能修改噢 + throw exception(DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED); + } + // 校验商品是否冲突 + validateDiscountActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts()); + + // 更新活动 + DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO) + .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getStartTime(), updateReqVO.getEndTime())); + discountActivityMapper.updateById(updateObj); + // 更新商品 + updateDiscountProduct(updateReqVO); + } + + private void updateDiscountProduct(DiscountActivityUpdateReqVO updateReqVO) { + List dbDiscountProducts = discountProductMapper.selectListByActivityId(updateReqVO.getId()); + // 计算要删除的记录 + List deleteIds = convertList(dbDiscountProducts, DiscountProductDO::getId, + discountProductDO -> updateReqVO.getProducts().stream() + .noneMatch(product -> DiscountActivityConvert.INSTANCE.isEquals(discountProductDO, product))); + if (CollUtil.isNotEmpty(deleteIds)) { + discountProductMapper.deleteBatchIds(deleteIds); + } + // 计算新增的记录 + List newDiscountProducts = convertList(updateReqVO.getProducts(), + product -> DiscountActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId())); + newDiscountProducts.removeIf(product -> dbDiscountProducts.stream().anyMatch( + dbProduct -> DiscountActivityConvert.INSTANCE.isEquals(dbProduct, product))); // 如果匹配到,说明是更新的 + if (CollectionUtil.isNotEmpty(newDiscountProducts)) { + discountProductMapper.insertBatch(newDiscountProducts); + } + } + + /** + * 校验商品是否冲突 + * + * @param id 编号 + * @param products 商品列表 + */ + private void validateDiscountActivityProductConflicts(Long id, List products) { + if (CollUtil.isEmpty(products)) { + return; + } + // 查询商品参加的活动 + List discountActivityProductList = getRewardProductListBySkuIds( + convertSet(products, DiscountActivityBaseVO.Product::getSkuId), + asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus())); + if (id != null) { // 排除自己这个活动 + discountActivityProductList.removeIf(product -> id.equals(product.getActivityId())); + } + // 如果非空,则说明冲突 + if (CollUtil.isNotEmpty(discountActivityProductList)) { + throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS); + } + } + + private List getRewardProductListBySkuIds(Collection skuIds, + Collection statuses) { + // 查询商品 + List products = discountProductMapper.selectListBySkuId(skuIds); + if (CollUtil.isEmpty(products)) { + return new ArrayList<>(0); + } + + // 查询活动 + List activities = discountActivityMapper.selectBatchIds(skuIds); + activities.removeIf(activity -> !statuses.contains(activity.getStatus())); // 移除不满足 statuses 状态的 + Map activityMap = CollectionUtils.convertMap(activities, DiscountActivityDO::getId); + + // 移除不满足活动的商品 + products.removeIf(product -> !activityMap.containsKey(product.getActivityId())); + return DiscountActivityConvert.INSTANCE.convertList(products, activityMap); + } + + @Override + public void closeRewardActivity(Long id) { + // 校验存在 + DiscountActivityDO dbDiscountActivity = validateDiscountActivityExists(id); + if (dbDiscountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能关闭噢 + throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); + } + if (dbDiscountActivity.getStatus().equals(PromotionActivityStatusEnum.END.getStatus())) { // 已关闭的活动,不能关闭噢 + throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END); + } + + // 更新 + DiscountActivityDO updateObj = new DiscountActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()); + discountActivityMapper.updateById(updateObj); + } + + @Override + public void deleteDiscountActivity(Long id) { + // 校验存在 + DiscountActivityDO discountActivity = validateDiscountActivityExists(id); + if (!discountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 未关闭的活动,不能删除噢 + throw exception(DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED); + } + + // 删除 + discountActivityMapper.deleteById(id); + } + + private DiscountActivityDO validateDiscountActivityExists(Long id) { + DiscountActivityDO discountActivity = discountActivityMapper.selectById(id); + if (discountActivity == null) { + throw exception(DISCOUNT_ACTIVITY_NOT_EXISTS); + } + return discountActivity; + } + + @Override + public DiscountActivityDO getDiscountActivity(Long id) { + return discountActivityMapper.selectById(id); + } + + @Override + public PageResult getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO) { + return discountActivityMapper.selectPage(pageReqVO); + } + + @Override + public List getDiscountProductsByActivityId(Long activityId) { + return discountProductMapper.selectListByActivityId(activityId); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/bo/DiscountProductDetailBO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/bo/DiscountProductDetailBO.java new file mode 100644 index 0000000000..7b8f4a20fb --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/bo/DiscountProductDetailBO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.promotion.service.discount.bo; + +import lombok.Data; + +/** + * 限时折扣活动商品 BO + * + * @author 芋道源码 + */ +@Data +public class DiscountProductDetailBO { + + // ========== DiscountProductDO 字段 ========== + + /** + * 编号,主键自增 + */ + private Long id; + /** + * 限时折扣活动的编号 + */ + private Long activityId; + /** + * 商品 SPU 编号 + */ + private Long spuId; + /** + * 商品 SKU 编号 + */ + private Long skuId; + /** + * 折扣类型 + */ + private Integer discountType; + /** + * 折扣百分比 + */ + private Integer discountPercent; + /** + * 优惠金额,单位:分 + */ + private Integer discountPrice; + + // ========== DiscountActivityDO 字段 ========== + /** + * 活动标题 + */ + private String activityName; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceService.java new file mode 100644 index 0000000000..a7420e119f --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceService.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.promotion.service.price; + +import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; + +import java.util.List; + +/** + * 价格计算 Service 接口 + * + * @author 芋道源码 + */ +public interface PriceService { + + /** + * 计算商品的价格 + * + * @param calculateReqDTO 价格请求 + * @return 价格响应 + */ + PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO); + + /** + * 获得优惠劵的匹配信息列表 + * + * @param calculateReqDTO 价格请求 + * @return 价格响应 + */ + List getMeetCouponList(PriceCalculateReqDTO calculateReqDTO); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java new file mode 100644 index 0000000000..e384d1f812 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java @@ -0,0 +1,547 @@ +package cn.iocoder.yudao.module.promotion.service.price; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.promotion.convert.price.PriceConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import cn.iocoder.yudao.module.promotion.enums.common.*; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService; +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO; +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; +import com.google.common.base.Suppliers; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.*; +import java.util.function.Supplier; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; + +/** + * 价格计算 Service 实现类 + * + * 优惠计算顺序:min(限时折扣, 会员折扣) > 满减送 > 优惠券。 + * 参考文档: + * 1. 有赞文档:限时折扣、满减送、优惠券哪个优先计算? + * + * TODO 芋艿:进一步完善 + * 1. 限时折扣:指定金额、减免金额、折扣 + * 2. 满减送:循环、折扣 + * 3. 优惠劵:待定 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class PriceServiceImpl implements PriceService { + + @Resource + private DiscountActivityService discountService; + @Resource + private RewardActivityService rewardActivityService; + @Resource + private CouponService couponService; + + @Resource + private ProductSkuApi productSkuApi; + + @Override + public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { + // 获得商品 SKU 数组 + List skuList = checkSkus(calculateReqDTO); + // 初始化 PriceCalculateRespDTO 对象 + PriceCalculateRespDTO priceCalculate = PriceConvert.INSTANCE.convert(calculateReqDTO, skuList); + + // 计算商品级别的价格 + calculatePriceForSkuLevel(calculateReqDTO.getUserId(), priceCalculate); + // 计算订单级别的价格 + calculatePriceForOrderLevel(calculateReqDTO.getUserId(), priceCalculate); + // 计算优惠劵级别的价格 + calculatePriceForCouponLevel(calculateReqDTO.getUserId(), calculateReqDTO.getCouponId(), priceCalculate); + + // 如果最终支付金额小于等于 0,则抛出业务异常 + if (priceCalculate.getOrder().getPayPrice() <= 0) { + log.error("[calculatePrice][价格计算不正确,请求 calculateReqDTO({}),结果 priceCalculate({})]", + calculateReqDTO, priceCalculate); + throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL); + } + return priceCalculate; + } + + @Override + public List getMeetCouponList(PriceCalculateReqDTO calculateReqDTO) { + // 先计算一轮价格 + PriceCalculateRespDTO priceCalculate = calculatePrice(calculateReqDTO); + + // 获得用户的待使用优惠劵 + List couponList = couponService.getCouponList(calculateReqDTO.getUserId(), CouponStatusEnum.UNUSED.getStatus()); + if (CollUtil.isEmpty(couponList)) { + return Collections.emptyList(); + } + + // 获得优惠劵的匹配信息 + return CollectionUtils.convertList(couponList, coupon -> { + CouponMeetRespDTO couponMeetRespDTO = PriceConvert.INSTANCE.convert(coupon); + try { + // 校验优惠劵 + couponService.validCoupon(coupon); + + // 获得匹配的商品 SKU 数组 + List orderItems = getMatchCouponOrderItems(priceCalculate, coupon); + if (CollUtil.isEmpty(orderItems)) { + return couponMeetRespDTO.setMeet(false).setMeetTip("所结算商品没有符合条件的商品"); + } + + // 计算是否满足优惠劵的使用金额 + Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); + assert originPrice != null; + if (originPrice < coupon.getUsePrice()) { + return couponMeetRespDTO.setMeet(false) +// .setMeetTip(String.format("差 %s 元可用优惠劵", formatPrice(coupon.getUsePrice() - originPrice))); + .setMeetTip("所结算的商品中未满足使用的金额"); + } + } catch (ServiceException serviceException) { + couponMeetRespDTO.setMeet(false); + if (serviceException.getCode().equals(COUPON_VALID_TIME_NOT_NOW.getCode())) { + couponMeetRespDTO.setMeetTip("优惠劵未到使用时间"); + } else { + log.error("[getMeetCouponList][calculateReqDTO({}) 获得优惠劵匹配信息异常]", calculateReqDTO, serviceException); + couponMeetRespDTO.setMeetTip("优惠劵不满足使用条件"); + } + return couponMeetRespDTO; + } + // 满足 + return couponMeetRespDTO.setMeet(true); + }); + } + + private List checkSkus(PriceCalculateReqDTO calculateReqDTO) { + // 获得商品 SKU 数组 + Map skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(), + PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount); + List skus = productSkuApi.getSkuList(skuIdCountMap.keySet()); + + // 校验商品 SKU + skus.forEach(sku -> { + Integer count = skuIdCountMap.get(sku.getId()); + if (count == null) { + throw exception(SKU_NOT_EXISTS); + } + // 不校验库存不足,避免购物车场景,商品无货的情况 + }); + return skus; + } + + // ========== 计算商品级别的价格 ========== + + /** + * 计算商品级别的价格,例如说: + * 1. 会员折扣 + * 2. 限时折扣 {@link cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO} + * + * 其中,会员折扣、限时折扣取最低价 + * + * @param userId 用户编号 + * @param priceCalculate 价格计算的结果 + */ + private void calculatePriceForSkuLevel(Long userId, PriceCalculateRespDTO priceCalculate) { + // 获取 SKU 级别的所有优惠信息 + Supplier memberDiscountPercentSupplier = getMemberDiscountPercentSupplier(userId); + Map discountProducts = discountService.getMatchDiscountProducts( + convertSet(priceCalculate.getOrder().getItems(), PriceCalculateRespDTO.OrderItem::getSkuId)); + + // 处理每个 SKU 的优惠 + priceCalculate.getOrder().getItems().forEach(orderItem -> { + // 获取该 SKU 的优惠信息 + Double memberDiscountPercent = memberDiscountPercentSupplier.get(); + DiscountProductDetailBO discountProduct = discountProducts.get(orderItem.getSkuId()); + if (memberDiscountPercent == null && discountProduct == null) { + return; + } + // 计算价格,判断选择哪个折扣 + Integer memberPrice = memberDiscountPercent != null ? (int) (orderItem.getPayPrice() * memberDiscountPercent / 100) : null; + Integer promotionPrice = discountProduct != null ? getDiscountProductPrice(discountProduct, orderItem) : null; + if (memberPrice == null) { + calculatePriceByDiscountActivity(priceCalculate, orderItem, discountProduct, promotionPrice); + } else if (promotionPrice == null) { + calculatePriceByMemberDiscount(priceCalculate, orderItem, memberPrice); + } else if (memberPrice < promotionPrice) { + calculatePriceByDiscountActivity(priceCalculate, orderItem, discountProduct, promotionPrice); + } else { + calculatePriceByMemberDiscount(priceCalculate, orderItem, memberPrice); + } + }); + } + + private Integer getDiscountProductPrice(DiscountProductDetailBO discountProduct, + PriceCalculateRespDTO.OrderItem orderItem) { + Integer price = orderItem.getPayPrice(); + if (PromotionDiscountTypeEnum.PRICE.getType().equals(discountProduct.getDiscountType())) { // 减价 + price -= discountProduct.getDiscountPrice() * orderItem.getCount(); + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discountProduct.getDiscountType())) { // 打折 + price = price * discountProduct.getDiscountPercent() / 100; + } else { + throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discountProduct)); + } + return price; + } + + private void calculatePriceByMemberDiscount(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem, + Integer memberPrice) { + // 记录优惠明细 + addPromotion(priceCalculate, orderItem, null, PromotionTypeEnum.MEMBER.getName(), + PromotionTypeEnum.MEMBER.getType(), PromotionLevelEnum.SKU.getLevel(), memberPrice, + true, StrUtil.format("会员折扣:省 {} 元", formatPrice(orderItem.getPayPrice() - memberPrice))); + // 修改 SKU 的优惠 + modifyOrderItemPayPrice(orderItem, memberPrice, priceCalculate); + } + + private void calculatePriceByDiscountActivity(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem, + DiscountProductDetailBO discountProduct, Integer promotionPrice) { + // 记录优惠明细 + addPromotion(priceCalculate, orderItem, discountProduct.getActivityId(), discountProduct.getActivityName(), + PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(), PromotionLevelEnum.SKU.getLevel(), promotionPrice, + true, StrUtil.format("限时折扣:省 {} 元", formatPrice(orderItem.getPayPrice() - promotionPrice))); + // 修改 SKU 的优惠 + modifyOrderItemPayPrice(orderItem, promotionPrice, priceCalculate); + } + + // TODO 芋艿:提前实现 + private Supplier getMemberDiscountPercentSupplier(Long userId) { + return Suppliers.memoize(() -> { + if (userId == 1) { + return 90d; + } + if (userId == 2) { + return 80d; + } + return null; // 无优惠 + }); + } + + // ========== 计算商品级别的价格 ========== + + /** + * 计算订单级别的价格,例如说: + * 1. 满减送 {@link cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO} + * + * @param userId 用户编号 + * @param priceCalculate 价格计算的结果 + */ + @SuppressWarnings("unused") + private void calculatePriceForOrderLevel(Long userId, PriceCalculateRespDTO priceCalculate) { + // 获取 SKU 级别的所有优惠信息 + Set spuIds = convertSet(priceCalculate.getOrder().getItems(), PriceCalculateRespDTO.OrderItem::getSpuId); + Map> rewardActivities = rewardActivityService.getMatchRewardActivities(spuIds); + + // 处理满减送活动 + if (CollUtil.isNotEmpty(rewardActivities)) { + rewardActivities.forEach((rewardActivity, activitySpuIds) -> { + List orderItems = CollectionUtils.filterList(priceCalculate.getOrder().getItems(), + orderItem -> CollUtil.contains(activitySpuIds, orderItem.getSpuId())); + calculatePriceByRewardActivity(priceCalculate, orderItems, rewardActivity); + }); + } + } + + private void calculatePriceByRewardActivity(PriceCalculateRespDTO priceCalculate, List orderItems, + RewardActivityDO rewardActivity) { + // 获得最大匹配的满减送活动的规格 + RewardActivityDO.Rule rule = getLastMatchRewardActivityRule(rewardActivity, orderItems); + if (rule == null) { + // 获取不到的情况下,记录不满足的优惠明细 + addNotMeetPromotion(priceCalculate, orderItems, rewardActivity.getId(), rewardActivity.getName(), + PromotionTypeEnum.REWARD_ACTIVITY.getType(), PromotionLevelEnum.ORDER.getLevel(), + getRewardActivityNotMeetTip(rewardActivity)); + return; + } + + // 分摊金额 + List discountPartPrices = dividePrice(orderItems, rule.getDiscountPrice()); + // 记录优惠明细 + addPromotion(priceCalculate, orderItems, rewardActivity.getId(), rewardActivity.getName(), + PromotionTypeEnum.REWARD_ACTIVITY.getType(), PromotionLevelEnum.ORDER.getLevel(), discountPartPrices, + true, StrUtil.format("满减送:省 {} 元", formatPrice(rule.getDiscountPrice()))); + // 修改 SKU 的分摊 + for (int i = 0; i < orderItems.size(); i++) { + modifyOrderItemOrderPartPriceFromDiscountPrice(orderItems.get(i), discountPartPrices.get(i), priceCalculate); + } + } + + /** + * 获得最大匹配的满减送活动的规格 + * + * @param rewardActivity 满减送活动 + * @param orderItems 商品项 + * @return 匹配的活动规格 + */ + private RewardActivityDO.Rule getLastMatchRewardActivityRule(RewardActivityDO rewardActivity, + List orderItems) { + Integer count = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getCount, Integer::sum); + // price 的计算逻辑,使用 orderDividePrice 的原因,主要考虑分摊后,这个才是该 SKU 当前真实的支付总价 + Integer price = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); + assert count != null && price != null; + for (int i = rewardActivity.getRules().size() - 1; i >= 0; i--) { + RewardActivityDO.Rule rule = rewardActivity.getRules().get(i); + if (PromotionConditionTypeEnum.PRICE.getType().equals(rewardActivity.getConditionType()) + && price >= rule.getLimit()) { + return rule; + } + if (PromotionConditionTypeEnum.COUNT.getType().equals(rewardActivity.getConditionType()) + && count >= rule.getLimit()) { + return rule; + } + } + return null; + } + + /** + * 获得满减送活动部匹配时的提示 + * + * @param rewardActivity 满减送活动 + * @return 提示 + */ + private String getRewardActivityNotMeetTip(RewardActivityDO rewardActivity) { + return "TODO"; // TODO 芋艿:后面再想想 + } + + // ========== 计算优惠劵级别的价格 ========== + + private void calculatePriceForCouponLevel(Long userId, Long couponId, PriceCalculateRespDTO priceCalculate) { + // 校验优惠劵 + if (couponId == null) { + return; + } + CouponDO coupon = couponService.validCoupon(couponId, userId); + + // 获得匹配的商品 SKU 数组 + List orderItems = getMatchCouponOrderItems(priceCalculate, coupon); + if (CollUtil.isEmpty(orderItems)) { + throw exception(COUPON_NO_MATCH_SPU); + } + + // 计算是否满足优惠劵的使用金额 + Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); + assert originPrice != null; + if (originPrice < coupon.getUsePrice()) { + throw exception(COUPON_NO_MATCH_MIN_PRICE); + } + + // 计算可以优惠的金额 + priceCalculate.getOrder().setCouponId(couponId); + Integer couponPrice = getCouponPrice(coupon, originPrice); + // 分摊金额 + List couponPartPrices = dividePrice(orderItems, couponPrice); + // 记录优惠明细 + addPromotion(priceCalculate, orderItems, coupon.getId(), coupon.getName(), + PromotionTypeEnum.COUPON.getType(), PromotionLevelEnum.COUPON.getLevel(), couponPartPrices, + true, StrUtil.format("优惠劵:省 {} 元", formatPrice(couponPrice))); + // 修改 SKU 的分摊 + for (int i = 0; i < orderItems.size(); i++) { + modifyOrderItemOrderPartPriceFromCouponPrice(orderItems.get(i), couponPartPrices.get(i), priceCalculate); + } + } + + private List getMatchCouponOrderItems(PriceCalculateRespDTO priceCalculate, + CouponDO coupon) { + if (PromotionProductScopeEnum.ALL.getScope().equals(coupon.getProductScope())) { + return priceCalculate.getOrder().getItems(); + } + return CollectionUtils.filterList(priceCalculate.getOrder().getItems(), + orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId())); + } + + private Integer getCouponPrice(CouponDO coupon, Integer originPrice) { + if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价 + return coupon.getDiscountPrice(); + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(coupon.getDiscountType())) { // 打折 + int couponPrice = originPrice * coupon.getDiscountPercent() / 100; + return coupon.getDiscountLimitPrice() == null ? couponPrice + : Math.min(couponPrice, coupon.getDiscountLimitPrice()); // 优惠上限 + } + throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", coupon)); + } + + // ========== 其它相对通用的方法 ========== + + /** + * 添加单个 OrderItem 的营销明细 + * + * @param priceCalculate 价格计算结果 + * @param orderItem 单个订单商品 SKU + * @param id 营销编号 + * @param name 营销名字 + * @param type 营销类型 + * @param level 营销级别 + * @param newPayPrice 新的单实付金额(总) + * @param meet 是否满足优惠条件 + * @param meetTip 满足条件的提示 + */ + private void addPromotion(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem, + Long id, String name, Integer type, Integer level, + Integer newPayPrice, Boolean meet, String meetTip) { + // 创建营销明细 Item + // TODO 芋艿:orderItem.getPayPrice() 要不要改成 orderDividePrice;同时,newPayPrice 要不要改成直接传递 discountPrice + PriceCalculateRespDTO.PromotionItem promotionItem = new PriceCalculateRespDTO.PromotionItem().setSkuId(orderItem.getSkuId()) + .setOriginalPrice(orderItem.getPayPrice()).setDiscountPrice(orderItem.getPayPrice() - newPayPrice); + // 创建营销明细 + PriceCalculateRespDTO.Promotion promotion = new PriceCalculateRespDTO.Promotion() + .setId(id).setName(name).setType(type).setLevel(level) + .setOriginalPrice(promotionItem.getOriginalPrice()).setDiscountPrice(promotionItem.getDiscountPrice()) + .setItems(singletonList(promotionItem)).setMeet(meet).setMeetTip(meetTip); + priceCalculate.getPromotions().add(promotion); + } + + /** + * 添加多个 OrderItem 的营销明细 + * + * @param priceCalculate 价格计算结果 + * @param orderItems 多个订单商品 SKU + * @param id 营销编号 + * @param name 营销名字 + * @param type 营销类型 + * @param level 营销级别 + * @param discountPrices 多个订单商品 SKU 的优惠价格(总),和 orderItems 一一对应 + * @param meet 是否满足优惠条件 + * @param meetTip 满足条件的提示 + */ + private void addPromotion(PriceCalculateRespDTO priceCalculate, List orderItems, + Long id, String name, Integer type, Integer level, + List discountPrices, Boolean meet, String meetTip) { + // 创建营销明细 Item + List promotionItems = new ArrayList<>(discountPrices.size()); + for (int i = 0; i < orderItems.size(); i++) { + PriceCalculateRespDTO.OrderItem orderItem = orderItems.get(i); + promotionItems.add(new PriceCalculateRespDTO.PromotionItem().setSkuId(orderItem.getSkuId()) + .setOriginalPrice(orderItem.getPayPrice()).setDiscountPrice(discountPrices.get(i))); + } + // 创建营销明细 + PriceCalculateRespDTO.Promotion promotion = new PriceCalculateRespDTO.Promotion() + .setId(id).setName(name).setType(type).setLevel(level) + .setOriginalPrice(getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum)) + .setDiscountPrice(getSumValue(discountPrices, value -> value, Integer::sum)) + .setItems(promotionItems).setMeet(meet).setMeetTip(meetTip); + priceCalculate.getPromotions().add(promotion); + } + + private void addNotMeetPromotion(PriceCalculateRespDTO priceCalculate, List orderItems, + Long id, String name, Integer type, Integer level, String meetTip) { + // 创建营销明细 Item + List promotionItems = CollectionUtils.convertList(orderItems, + orderItem -> new PriceCalculateRespDTO.PromotionItem().setSkuId(orderItem.getSkuId()) + .setOriginalPrice(orderItem.getOrderDividePrice()).setDiscountPrice(0)); + // 创建营销明细 + Integer originalPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); + PriceCalculateRespDTO.Promotion promotion = new PriceCalculateRespDTO.Promotion() + .setId(id).setName(name).setType(type).setLevel(level) + .setOriginalPrice(originalPrice).setDiscountPrice(0) + .setItems(promotionItems).setMeet(false).setMeetTip(meetTip); + priceCalculate.getPromotions().add(promotion); + } + + /** + * 修改 OrderItem 的 payPrice 价格,同时会修改 Order 的 payPrice 价格 + * + * @param orderItem 订单商品 SKU + * @param newPayPrice 新的 payPrice 价格 + * @param priceCalculate 价格计算结果 + */ + private void modifyOrderItemPayPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer newPayPrice, + PriceCalculateRespDTO priceCalculate) { + // diffPayPrice 等于额外增加的商品级的优惠 + int diffPayPrice = orderItem.getPayPrice() - newPayPrice; + // 设置 OrderItem 价格相关字段 + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + diffPayPrice); + orderItem.setPayPrice(newPayPrice); + orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice()); + // 设置 Order 相关相关字段 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + order.setPayPrice(order.getPayPrice() - diffPayPrice); + order.setOrderPrice(order.getOrderPrice() - diffPayPrice); + } + + /** + * 修改 OrderItem 的 orderPartPrice 价格,同时会修改 Order 的 discountPrice 价格 + * + * 本质:分摊 Order 的 discountPrice 价格,到对应的 OrderItem 的 orderPartPrice 价格中 + * + * @param orderItem 订单商品 SKU + * @param addOrderPartPrice 新增的 discountPrice 价格 + * @param priceCalculate 价格计算结果 + */ + private void modifyOrderItemOrderPartPriceFromDiscountPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer addOrderPartPrice, + PriceCalculateRespDTO priceCalculate) { + // 设置 OrderItem 价格相关字段 + orderItem.setOrderPartPrice(orderItem.getOrderPartPrice() + addOrderPartPrice); + orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice()); + // 设置 Order 相关相关字段 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + order.setDiscountPrice(order.getDiscountPrice() + addOrderPartPrice); + order.setPayPrice(order.getPayPrice() - addOrderPartPrice); + } + + /** + * 修改 OrderItem 的 orderPartPrice 价格,同时会修改 Order 的 couponPrice 价格 + * + * 本质:分摊 Order 的 couponPrice 价格,到对应的 OrderItem 的 orderPartPrice 价格中 + * + * @param orderItem 订单商品 SKU + * @param addOrderPartPrice 新增的 couponPrice 价格 + * @param priceCalculate 价格计算结果 + */ + private void modifyOrderItemOrderPartPriceFromCouponPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer addOrderPartPrice, + PriceCalculateRespDTO priceCalculate) { + // 设置 OrderItem 价格相关字段 + orderItem.setOrderPartPrice(orderItem.getOrderPartPrice() + addOrderPartPrice); + orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice()); + // 设置 Order 相关相关字段 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + order.setCouponPrice(order.getCouponPrice() + addOrderPartPrice); + order.setPayPrice(order.getPayPrice() - addOrderPartPrice); + } + + private List dividePrice(List orderItems, Integer price) { + List prices = new ArrayList<>(orderItems.size()); + Integer total = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); + assert total != null; + int remainPrice = price; + // 遍历每一个,进行分摊 + for (int i = 0; i < orderItems.size(); i++) { + PriceCalculateRespDTO.OrderItem orderItem = orderItems.get(i); + int partPrice; + if (i < orderItems.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减 + partPrice = (int) (price * (1.0D * orderItem.getOrderDividePrice() / total)); + remainPrice -= partPrice; + } else { + partPrice = remainPrice; + } + Assert.isTrue(partPrice > 0, "分摊金额必须大于 0"); + prices.add(partPrice); + } + return prices; + } + + private String formatPrice(Integer price) { + return String.format("%.2f", price / 100d); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java new file mode 100755 index 0000000000..40bcc28364 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.promotion.service.reward; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; + +import javax.validation.Valid; +import java.util.Map; +import java.util.Set; + +/** + * 满减送活动 Service 接口 + * + * @author 芋道源码 + */ +public interface RewardActivityService { + + /** + * 创建满减送活动 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createRewardActivity(@Valid RewardActivityCreateReqVO createReqVO); + + /** + * 更新满减送活动 + * + * @param updateReqVO 更新信息 + */ + void updateRewardActivity(@Valid RewardActivityUpdateReqVO updateReqVO); + + /** + * 关闭满减送活动 + * + * @param id 活动编号 + */ + void closeRewardActivity(Long id); + + /** + * 删除满减送活动 + * + * @param id 编号 + */ + void deleteRewardActivity(Long id); + + /** + * 获得满减送活动 + * + * @param id 编号 + * @return 满减送活动 + */ + RewardActivityDO getRewardActivity(Long id); + + /** + * 获得满减送活动分页 + * + * @param pageReqVO 分页查询 + * @return 满减送活动分页 + */ + PageResult getRewardActivityPage(RewardActivityPageReqVO pageReqVO); + + /** + * 基于指定的 SPU 编号数组,获得它们匹配的满减送活动 + * + * @param spuIds SPU 编号数组 + * @return 满减送活动,与对应的 SPU 编号的映射。即,value 就是 SPU 编号的集合 + */ + Map> getMatchRewardActivities(Set spuIds); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java new file mode 100755 index 0000000000..51d0ce6269 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java @@ -0,0 +1,169 @@ +package cn.iocoder.yudao.module.promotion.service.reward; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import cn.iocoder.yudao.module.promotion.util.PromotionUtils; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.hutool.core.collection.CollUtil.intersectionDistinct; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; + +/** + * 满减送活动 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class RewardActivityServiceImpl implements RewardActivityService { + + @Resource + private RewardActivityMapper rewardActivityMapper; + + @Override + public Long createRewardActivity(RewardActivityCreateReqVO createReqVO) { + // 校验商品是否冲突 + validateRewardActivitySpuConflicts(null, createReqVO.getProductSpuIds()); + + // 插入 + RewardActivityDO rewardActivity = RewardActivityConvert.INSTANCE.convert(createReqVO) + .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getStartTime(), createReqVO.getEndTime())); + rewardActivityMapper.insert(rewardActivity); + // 返回 + return rewardActivity.getId(); + } + + @Override + public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) { + // 校验存在 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId()); + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能修改噢 + throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED); + } + // 校验商品是否冲突 + validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO.getProductSpuIds()); + + // 更新 + RewardActivityDO updateObj = RewardActivityConvert.INSTANCE.convert(updateReqVO) + .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getStartTime(), updateReqVO.getEndTime())); + rewardActivityMapper.updateById(updateObj); + } + + @Override + public void closeRewardActivity(Long id) { + // 校验存在 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(id); + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能关闭噢 + throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); + } + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.END.getStatus())) { // 已关闭的活动,不能关闭噢 + throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END); + } + + // 更新 + RewardActivityDO updateObj = new RewardActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()); + rewardActivityMapper.updateById(updateObj); + } + + @Override + public void deleteRewardActivity(Long id) { + // 校验存在 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(id); + if (!dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 未关闭的活动,不能删除噢 + throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED); + } + + // 删除 + rewardActivityMapper.deleteById(id); + } + + private RewardActivityDO validateRewardActivityExists(Long id) { + RewardActivityDO activity = rewardActivityMapper.selectById(id); + if (activity == null) { + throw exception(REWARD_ACTIVITY_NOT_EXISTS); + } + return activity; + } + + /** + * 校验商品参加的活动是否冲突 + * + * @param id 活动编号 + * @param spuIds 商品 SPU 编号数组 + */ + private void validateRewardActivitySpuConflicts(Long id, Collection spuIds) { + if (CollUtil.isEmpty(spuIds)) { + return; + } + // 查询商品参加的活动 + List rewardActivityList = getRewardActivityListBySpuIds(spuIds, + asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus())); + if (id != null) { // 排除自己这个活动 + rewardActivityList.removeIf(activity -> id.equals(activity.getId())); + } + // 如果非空,则说明冲突 + if (CollUtil.isNotEmpty(rewardActivityList)) { + throw exception(REWARD_ACTIVITY_SPU_CONFLICTS); + } + } + + /** + * 获得商品参加的满减送活动的数组 + * + * @param spuIds 商品 SPU 编号数组 + * @param statuses 活动状态数组 + * @return 商品参加的满减送活动的数组 + */ + private List getRewardActivityListBySpuIds(Collection spuIds, + Collection statuses) { + List list = rewardActivityMapper.selectListByStatus(statuses); + return CollUtil.filter(list, activity -> CollUtil.containsAny(activity.getProductSpuIds(), spuIds)); + } + + @Override + public RewardActivityDO getRewardActivity(Long id) { + return rewardActivityMapper.selectById(id); + } + + @Override + public PageResult getRewardActivityPage(RewardActivityPageReqVO pageReqVO) { + return rewardActivityMapper.selectPage(pageReqVO); + } + + @Override + public Map> getMatchRewardActivities(Set spuIds) { + // 如果有全局活动,则直接选择它 + List allActivities = rewardActivityMapper.selectListByProductScopeAndStatus( + PromotionProductScopeEnum.ALL.getScope(), PromotionActivityStatusEnum.RUN.getStatus()); + if (CollUtil.isNotEmpty(allActivities)) { + return MapUtil.builder(allActivities.get(0), spuIds).build(); + } + + // 查询某个活动参加的活动 + List productActivityList = getRewardActivityListBySpuIds(spuIds, + singleton(PromotionActivityStatusEnum.RUN.getStatus())); + return convertMap(productActivityList, activity -> activity, + rewardActivityDO -> intersectionDistinct(rewardActivityDO.getProductSpuIds(), spuIds)); // 求交集返回 + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java new file mode 100644 index 0000000000..fc8ca16cfa --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.promotion.util; + +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; + +import java.time.LocalDateTime; + +/** + * 活动工具类 + * + * @author 芋道源码 + */ +public class PromotionUtils { + + /** + * 根据时间,计算活动状态 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 活动状态 + */ + public static Integer calculateActivityStatus(LocalDateTime startTime, LocalDateTime endTime) { + if (LocalDateTimeUtils.beforeNow(endTime)) { + return PromotionActivityStatusEnum.END.getStatus(); + } + if (LocalDateTimeUtils.afterNow(startTime)) { + return PromotionActivityStatusEnum.WAIT.getStatus(); + } + return PromotionActivityStatusEnum.RUN.getStatus(); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/coupon/CouponTemplateMapper.xml b/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/coupon/CouponTemplateMapper.xml new file mode 100644 index 0000000000..9871435347 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/coupon/CouponTemplateMapper.xml @@ -0,0 +1,11 @@ + + + + + + UPDATE promotion_coupon_template + SET take_count = take_count + #{incrCount} + WHERE id = #{id} + + + diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java new file mode 100755 index 0000000000..d77e29deed --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.promotion.service.coupon; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper; +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.CouponTemplateValidityTypeEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.util.Date; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildLocalDateTime; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** +* {@link CouponTemplateServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(CouponTemplateServiceImpl.class) +public class CouponTemplateServiceImplTest extends BaseDbUnitTest { + + @Resource + private CouponTemplateServiceImpl couponTemplateService; + + @Resource + private CouponTemplateMapper couponTemplateMapper; + + @Test + public void testCreateCouponTemplate_success() { + // 准备参数 + CouponTemplateCreateReqVO reqVO = randomPojo(CouponTemplateCreateReqVO.class, + o -> o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope()) + .setValidityType(randomEle(CouponTemplateValidityTypeEnum.values()).getType()) + .setDiscountType(randomEle(PromotionDiscountTypeEnum.values()).getType())); + + // 调用 + Long couponTemplateId = couponTemplateService.createCouponTemplate(reqVO); + // 断言 + assertNotNull(couponTemplateId); + // 校验记录的属性是否正确 + CouponTemplateDO couponTemplate = couponTemplateMapper.selectById(couponTemplateId); + assertPojoEquals(reqVO, couponTemplate); + } + + @Test + public void testUpdateCouponTemplate_success() { + // mock 数据 + CouponTemplateDO dbCouponTemplate = randomPojo(CouponTemplateDO.class); + couponTemplateMapper.insert(dbCouponTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + CouponTemplateUpdateReqVO reqVO = randomPojo(CouponTemplateUpdateReqVO.class, o -> { + o.setId(dbCouponTemplate.getId()); // 设置更新的 ID + // 其它通用字段 + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope()) + .setValidityType(randomEle(CouponTemplateValidityTypeEnum.values()).getType()) + .setDiscountType(randomEle(PromotionDiscountTypeEnum.values()).getType()); + }); + + // 调用 + couponTemplateService.updateCouponTemplate(reqVO); + // 校验是否更新正确 + CouponTemplateDO couponTemplate = couponTemplateMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, couponTemplate); + } + + @Test + public void testUpdateCouponTemplate_notExists() { + // 准备参数 + CouponTemplateUpdateReqVO reqVO = randomPojo(CouponTemplateUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> couponTemplateService.updateCouponTemplate(reqVO), COUPON_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testDeleteCouponTemplate_success() { + // mock 数据 + CouponTemplateDO dbCouponTemplate = randomPojo(CouponTemplateDO.class); + couponTemplateMapper.insert(dbCouponTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCouponTemplate.getId(); + + // 调用 + couponTemplateService.deleteCouponTemplate(id); + // 校验数据不存在了 + assertNull(couponTemplateMapper.selectById(id)); + } + + @Test + public void testDeleteCouponTemplate_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> couponTemplateService.deleteCouponTemplate(id), COUPON_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testGetCouponTemplatePage() { + // mock 数据 + CouponTemplateDO dbCouponTemplate = randomPojo(CouponTemplateDO.class, o -> { // 等会查询到 + o.setName("芋艿"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()); + o.setCreateTime(buildLocalDateTime(2022, 2, 2)); + }); + couponTemplateMapper.insert(dbCouponTemplate); + // 测试 name 不匹配 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setName("土豆"))); + // 测试 status 不匹配 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 type 不匹配 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()))); + // 测试 createTime 不匹配 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setCreateTime(buildLocalDateTime(2022, 1, 1)))); + // 准备参数 + CouponTemplatePageReqVO reqVO = new CouponTemplatePageReqVO(); + reqVO.setName("芋艿"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()); + reqVO.setCreateTime((new Date[]{buildTime(2022, 2, 1), buildTime(2022, 2, 3)})); + + // 调用 + PageResult pageResult = couponTemplateService.getCouponTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbCouponTemplate, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java new file mode 100755 index 0000000000..5ad517463d --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java @@ -0,0 +1,210 @@ +package cn.iocoder.yudao.module.promotion.service.discount; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO; +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.mysql.discount.DiscountActivityMapper; +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DISCOUNT_ACTIVITY_NOT_EXISTS; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; + +/** +* {@link DiscountActivityServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(DiscountActivityServiceImpl.class) +public class DiscountActivityServiceImplTest extends BaseDbUnitTest { + + @Resource + private DiscountActivityServiceImpl discountActivityService; + + @Resource + private DiscountActivityMapper discountActivityMapper; + @Resource + private DiscountProductMapper discountProductMapper; + + @Test + public void testCreateDiscountActivity_success() { + // 准备参数 + DiscountActivityCreateReqVO reqVO = randomPojo(DiscountActivityCreateReqVO.class, o -> { + // 用于触发进行中的状态 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2))); + // 设置商品 + o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L) + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3), + new DiscountActivityBaseVO.Product().setSpuId(10L).setSkuId(20L) + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30))); + }); + + // 调用 + Long discountActivityId = discountActivityService.createDiscountActivity(reqVO); + // 断言 + assertNotNull(discountActivityId); + // 校验活动 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(discountActivityId); + assertPojoEquals(reqVO, discountActivity); + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus()); + // 校验商品 + List discountProducts = discountProductMapper.selectList(DiscountProductDO::getActivityId, discountActivity.getId()); + assertEquals(discountProducts.size(), reqVO.getProducts().size()); + for (int i = 0; i < reqVO.getProducts().size(); i++) { + DiscountActivityBaseVO.Product product = reqVO.getProducts().get(i); + DiscountProductDO discountProduct = discountProducts.get(i); + assertEquals(discountProduct.getActivityId(), discountActivity.getId()); + assertEquals(discountProduct.getSpuId(), product.getSpuId()); + assertEquals(discountProduct.getSkuId(), product.getSkuId()); + assertEquals(discountProduct.getDiscountType(), product.getDiscountType()); + assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice()); + assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent()); + } + } + + @Test + public void testUpdateDiscountActivity_success() { + // mock 数据(商品) + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class); + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据 + // mock 数据(活动) + DiscountProductDO dbDiscountProduct01 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId()) + .setSpuId(1L).setSkuId(2L).setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null)); + DiscountProductDO dbDiscountProduct02 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId()) + .setSpuId(10L).setSkuId(20L).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null)); + discountProductMapper.insert(dbDiscountProduct01); + discountProductMapper.insert(dbDiscountProduct02); + // 准备参数 + DiscountActivityUpdateReqVO reqVO = randomPojo(DiscountActivityUpdateReqVO.class, o -> { + o.setId(dbDiscountActivity.getId()); // 设置更新的 ID + // 用于触发进行中的状态 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2))); + // 设置商品 + o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L) + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null), + new DiscountActivityBaseVO.Product().setSpuId(100L).setSkuId(200L) + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null))); + }); + + // 调用 + discountActivityService.updateDiscountActivity(reqVO); + // 校验活动 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, discountActivity); + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus()); + // 校验商品 + List discountProducts = discountProductMapper.selectList(DiscountProductDO::getActivityId, discountActivity.getId()); + assertEquals(discountProducts.size(), reqVO.getProducts().size()); + for (int i = 0; i < reqVO.getProducts().size(); i++) { + DiscountActivityBaseVO.Product product = reqVO.getProducts().get(i); + DiscountProductDO discountProduct = discountProducts.get(i); + assertEquals(discountProduct.getActivityId(), discountActivity.getId()); + assertEquals(discountProduct.getSpuId(), product.getSpuId()); + assertEquals(discountProduct.getSkuId(), product.getSkuId()); + assertEquals(discountProduct.getDiscountType(), product.getDiscountType()); + assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice()); + assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent()); + } + } + + @Test + public void testCloseDiscountActivity() { + // mock 数据 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class, + o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus())); + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDiscountActivity.getId(); + + // 调用 + discountActivityService.closeRewardActivity(id); + // 校验状态 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(id); + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.CLOSE.getStatus()); + } + + @Test + public void testUpdateDiscountActivity_notExists() { + // 准备参数 + DiscountActivityUpdateReqVO reqVO = randomPojo(DiscountActivityUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> discountActivityService.updateDiscountActivity(reqVO), DISCOUNT_ACTIVITY_NOT_EXISTS); + } + + @Test + public void testDeleteDiscountActivity_success() { + // mock 数据 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class, + o -> o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus())); + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDiscountActivity.getId(); + + // 调用 + discountActivityService.deleteDiscountActivity(id); + // 校验数据不存在了 + assertNull(discountActivityMapper.selectById(id)); + } + + @Test + public void testDeleteDiscountActivity_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> discountActivityService.deleteDiscountActivity(id), DISCOUNT_ACTIVITY_NOT_EXISTS); + } + + @Test + public void testGetDiscountActivityPage() { + // mock 数据 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class, o -> { // 等会查询到 + o.setName("芋艿"); + o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()); + o.setCreateTime(buildTime(2021, 1, 15)); + }); + discountActivityMapper.insert(dbDiscountActivity); + // 测试 name 不匹配 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setName("土豆"))); + // 测试 status 不匹配 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setStatus(PromotionActivityStatusEnum.END.getStatus()))); + // 测试 createTime 不匹配 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setCreateTime(buildTime(2021, 2, 10)))); + // 准备参数 + DiscountActivityPageReqVO reqVO = new DiscountActivityPageReqVO(); + reqVO.setName("芋艿"); + reqVO.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()); + reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 1, 1), buildTime(2021, 1, 31)})); + + // 调用 + PageResult pageResult = discountActivityService.getDiscountActivityPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbDiscountActivity, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java new file mode 100644 index 0000000000..0c7b2d6ec1 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java @@ -0,0 +1,506 @@ +package cn.iocoder.yudao.module.promotion.service.price; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import cn.iocoder.yudao.module.promotion.enums.common.*; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService; +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO; +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +/** + * {@link PriceServiceImpl} 的单元测试 + * + * @author 芋道源码 + */ +public class PriceServiceTest extends BaseMockitoUnitTest { + + @InjectMocks + private PriceServiceImpl priceService; + + @Mock + private DiscountActivityService discountService; + @Mock + private RewardActivityService rewardActivityService; + @Mock + private CouponService couponService; + @Mock + private ProductSkuApi productSkuApi; + + @Test + public void testCalculatePrice_memberDiscount() { + // 准备参数 + // TODO 芋艿:userId = 1,实现 9 折;后续改成 mock + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1L) + .setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2))); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100)); + when(productSkuApi.getSkuList(eq(asSet(10L)))).thenReturn(singletonList(productSku)); + + // 调用 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); + // 断言 Order 部分 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + assertEquals(order.getOriginalPrice(), 200); + assertEquals(order.getOrderPrice(), 180); + assertEquals(order.getDiscountPrice(), 0); + assertEquals(order.getPointPrice(), 0); + assertEquals(order.getDeliveryPrice(), 0); + assertEquals(order.getPayPrice(), 180); + assertNull(order.getCouponId()); + // 断言 OrderItem 部分 + assertEquals(order.getItems().size(), 1); + PriceCalculateRespDTO.OrderItem orderItem = order.getItems().get(0); + assertEquals(orderItem.getSkuId(), 10L); + assertEquals(orderItem.getCount(), 2); + assertEquals(orderItem.getOriginalPrice(), 200); + assertEquals(orderItem.getOriginalUnitPrice(), 100); + assertEquals(orderItem.getDiscountPrice(), 20); + assertEquals(orderItem.getPayPrice(), 180); + assertEquals(orderItem.getOrderPartPrice(), 0); + assertEquals(orderItem.getOrderDividePrice(), 180); + // 断言 Promotion 部分 + assertEquals(priceCalculate.getPromotions().size(), 1); + PriceCalculateRespDTO.Promotion promotion = priceCalculate.getPromotions().get(0); + assertNull(promotion.getId()); + assertEquals(promotion.getName(), "会员折扣"); + assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType()); + assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel()); + assertEquals(promotion.getOriginalPrice(), 200); + assertEquals(promotion.getDiscountPrice(), 20); + assertTrue(promotion.getMeet()); + assertEquals(promotion.getMeetTip(), "会员折扣:省 0.20 元"); + PriceCalculateRespDTO.PromotionItem promotionItem = promotion.getItems().get(0); + assertEquals(promotion.getItems().size(), 1); + assertEquals(promotionItem.getSkuId(), 10L); + assertEquals(promotionItem.getOriginalPrice(), 200); + assertEquals(promotionItem.getDiscountPrice(), 20); + } + + @Test + public void testCalculatePrice_discountActivity() { + // 准备参数 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId()) + .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2), + new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3))); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100)); + ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50)); + when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02)); + // mock 方法(限时折扣 DiscountActivity 信息) + DiscountProductDetailBO discountProduct01 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(1000L) + .setActivityName("活动 1000 号").setSkuId(10L) + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40)); + DiscountProductDetailBO discountProduct02 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(2000L) + .setActivityName("活动 2000 号").setSkuId(20L) + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60)); + when(discountService.getMatchDiscountProducts(eq(asSet(10L, 20L)))).thenReturn( + MapUtil.builder(10L, discountProduct01).put(20L, discountProduct02).map()); + + // 10L: 100 * 2 - 40 * 2 = 120 + // 20L:50 * 3 - 50 * 3 * 0.4 = 90 + + // 调用 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); + // 断言 Order 部分 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + assertEquals(order.getOriginalPrice(), 350); + assertEquals(order.getOrderPrice(), 210); + assertEquals(order.getDiscountPrice(), 0); + assertEquals(order.getPointPrice(), 0); + assertEquals(order.getDeliveryPrice(), 0); + assertEquals(order.getPayPrice(), 210); + assertNull(order.getCouponId()); + // 断言 OrderItem 部分 + assertEquals(order.getItems().size(), 2); + PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0); + assertEquals(orderItem01.getSkuId(), 10L); + assertEquals(orderItem01.getCount(), 2); + assertEquals(orderItem01.getOriginalPrice(), 200); + assertEquals(orderItem01.getOriginalUnitPrice(), 100); + assertEquals(orderItem01.getDiscountPrice(), 80); + assertEquals(orderItem01.getPayPrice(), 120); + assertEquals(orderItem01.getOrderPartPrice(), 0); + assertEquals(orderItem01.getOrderDividePrice(), 120); + PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1); + assertEquals(orderItem02.getSkuId(), 20L); + assertEquals(orderItem02.getCount(), 3); + assertEquals(orderItem02.getOriginalPrice(), 150); + assertEquals(orderItem02.getOriginalUnitPrice(), 50); + assertEquals(orderItem02.getDiscountPrice(), 60); + assertEquals(orderItem02.getPayPrice(), 90); + assertEquals(orderItem02.getOrderPartPrice(), 0); + assertEquals(orderItem02.getOrderDividePrice(), 90); + // 断言 Promotion 部分 + assertEquals(priceCalculate.getPromotions().size(), 2); + PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0); + assertEquals(promotion01.getId(), 1000L); + assertEquals(promotion01.getName(), "活动 1000 号"); + assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()); + assertEquals(promotion01.getLevel(), PromotionLevelEnum.SKU.getLevel()); + assertEquals(promotion01.getOriginalPrice(), 200); + assertEquals(promotion01.getDiscountPrice(), 80); + assertTrue(promotion01.getMeet()); + assertEquals(promotion01.getMeetTip(), "限时折扣:省 0.80 元"); + PriceCalculateRespDTO.PromotionItem promotionItem01 = promotion01.getItems().get(0); + assertEquals(promotion01.getItems().size(), 1); + assertEquals(promotionItem01.getSkuId(), 10L); + assertEquals(promotionItem01.getOriginalPrice(), 200); + assertEquals(promotionItem01.getDiscountPrice(), 80); + PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1); + assertEquals(promotion02.getId(), 2000L); + assertEquals(promotion02.getName(), "活动 2000 号"); + assertEquals(promotion02.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()); + assertEquals(promotion02.getLevel(), PromotionLevelEnum.SKU.getLevel()); + assertEquals(promotion02.getOriginalPrice(), 150); + assertEquals(promotion02.getDiscountPrice(), 60); + assertTrue(promotion02.getMeet()); + assertEquals(promotion02.getMeetTip(), "限时折扣:省 0.60 元"); + PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0); + assertEquals(promotion02.getItems().size(), 1); + assertEquals(promotionItem02.getSkuId(), 20L); + assertEquals(promotionItem02.getOriginalPrice(), 150); + assertEquals(promotionItem02.getDiscountPrice(), 60); + } + + /** + * 测试满减送活动,匹配的情况 + */ + @Test + public void testCalculatePrice_rewardActivity() { + // 准备参数 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId()) + .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2), + new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3), + new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4))); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L)); + ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L)); + ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L)); + when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03)); + // mock 方法(限时折扣 DiscountActivity 信息) + RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号") + .setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType()) + .setRules(singletonList(new RewardActivityDO.Rule().setLimit(200).setDiscountPrice(70)))); + RewardActivityDO rewardActivity02 = randomPojo(RewardActivityDO.class, o -> o.setId(2000L).setName("活动 2000 号") + .setProductSpuIds(singletonList(30L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType()) + .setRules(asList(new RewardActivityDO.Rule().setLimit(1).setDiscountPrice(10), + new RewardActivityDO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个 + new RewardActivityDO.Rule().setLimit(10).setDiscountPrice(100)))); + Map> matchRewardActivities = new LinkedHashMap<>(); + matchRewardActivities.put(rewardActivity01, asSet(1L, 2L)); + matchRewardActivities.put(rewardActivity02, asSet(3L)); + when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities); + + // 调用 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); + // 断言 Order 部分 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + assertEquals(order.getOriginalPrice(), 470); + assertEquals(order.getOrderPrice(), 470); + assertEquals(order.getDiscountPrice(), 130); + assertEquals(order.getPointPrice(), 0); + assertEquals(order.getDeliveryPrice(), 0); + assertEquals(order.getPayPrice(), 340); + assertNull(order.getCouponId()); + // 断言 OrderItem 部分 + assertEquals(order.getItems().size(), 3); + PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0); + assertEquals(orderItem01.getSkuId(), 10L); + assertEquals(orderItem01.getCount(), 2); + assertEquals(orderItem01.getOriginalPrice(), 200); + assertEquals(orderItem01.getOriginalUnitPrice(), 100); + assertEquals(orderItem01.getDiscountPrice(), 0); + assertEquals(orderItem01.getPayPrice(), 200); + assertEquals(orderItem01.getOrderPartPrice(), 40); + assertEquals(orderItem01.getOrderDividePrice(), 160); + PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1); + assertEquals(orderItem02.getSkuId(), 20L); + assertEquals(orderItem02.getCount(), 3); + assertEquals(orderItem02.getOriginalPrice(), 150); + assertEquals(orderItem02.getOriginalUnitPrice(), 50); + assertEquals(orderItem02.getDiscountPrice(), 0); + assertEquals(orderItem02.getPayPrice(), 150); + assertEquals(orderItem02.getOrderPartPrice(), 30); + assertEquals(orderItem02.getOrderDividePrice(), 120); + PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2); + assertEquals(orderItem03.getSkuId(), 30L); + assertEquals(orderItem03.getCount(), 4); + assertEquals(orderItem03.getOriginalPrice(), 120); + assertEquals(orderItem03.getOriginalUnitPrice(), 30); + assertEquals(orderItem03.getDiscountPrice(), 0); + assertEquals(orderItem03.getPayPrice(), 120); + assertEquals(orderItem03.getOrderPartPrice(), 60); + assertEquals(orderItem03.getOrderDividePrice(), 60); + // 断言 Promotion 部分(第一个) + assertEquals(priceCalculate.getPromotions().size(), 2); + PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0); + assertEquals(promotion01.getId(), 1000L); + assertEquals(promotion01.getName(), "活动 1000 号"); + assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); + assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); + assertEquals(promotion01.getOriginalPrice(), 350); + assertEquals(promotion01.getDiscountPrice(), 70); + assertTrue(promotion01.getMeet()); + assertEquals(promotion01.getMeetTip(), "满减送:省 0.70 元"); + assertEquals(promotion01.getItems().size(), 2); + PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0); + assertEquals(promotionItem011.getSkuId(), 10L); + assertEquals(promotionItem011.getOriginalPrice(), 200); + assertEquals(promotionItem011.getDiscountPrice(), 40); + PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1); + assertEquals(promotionItem012.getSkuId(), 20L); + assertEquals(promotionItem012.getOriginalPrice(), 150); + assertEquals(promotionItem012.getDiscountPrice(), 30); + // 断言 Promotion 部分(第二个) + PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1); + assertEquals(promotion02.getId(), 2000L); + assertEquals(promotion02.getName(), "活动 2000 号"); + assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); + assertEquals(promotion02.getLevel(), PromotionLevelEnum.ORDER.getLevel()); + assertEquals(promotion02.getOriginalPrice(), 120); + assertEquals(promotion02.getDiscountPrice(), 60); + assertTrue(promotion02.getMeet()); + assertEquals(promotion02.getMeetTip(), "满减送:省 0.60 元"); + PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0); + assertEquals(promotion02.getItems().size(), 1); + assertEquals(promotionItem02.getSkuId(), 30L); + assertEquals(promotionItem02.getOriginalPrice(), 120); + assertEquals(promotionItem02.getDiscountPrice(), 60); + } + + /** + * 测试满减送活动,不匹配的情况 + */ + @Test + public void testCalculatePrice_rewardActivityNotMeet() { + // 准备参数 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId()) + .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2), + new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3))); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L)); + ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L)); + when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02)); + // mock 方法(限时折扣 DiscountActivity 信息) + RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号") + .setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType()) + .setRules(singletonList(new RewardActivityDO.Rule().setLimit(351).setDiscountPrice(70)))); + Map> matchRewardActivities = new LinkedHashMap<>(); + matchRewardActivities.put(rewardActivity01, asSet(1L, 2L)); + when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities); + + // 调用 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); + // 断言 Order 部分 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + assertEquals(order.getOriginalPrice(), 350); + assertEquals(order.getOrderPrice(), 350); + assertEquals(order.getDiscountPrice(), 0); + assertEquals(order.getPointPrice(), 0); + assertEquals(order.getDeliveryPrice(), 0); + assertEquals(order.getPayPrice(), 350); + assertNull(order.getCouponId()); + // 断言 OrderItem 部分 + assertEquals(order.getItems().size(), 2); + PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0); + assertEquals(orderItem01.getSkuId(), 10L); + assertEquals(orderItem01.getCount(), 2); + assertEquals(orderItem01.getOriginalPrice(), 200); + assertEquals(orderItem01.getOriginalUnitPrice(), 100); + assertEquals(orderItem01.getDiscountPrice(), 0); + assertEquals(orderItem01.getPayPrice(), 200); + assertEquals(orderItem01.getOrderPartPrice(), 0); + assertEquals(orderItem01.getOrderDividePrice(), 200); + PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1); + assertEquals(orderItem02.getSkuId(), 20L); + assertEquals(orderItem02.getCount(), 3); + assertEquals(orderItem02.getOriginalPrice(), 150); + assertEquals(orderItem02.getOriginalUnitPrice(), 50); + assertEquals(orderItem02.getDiscountPrice(), 0); + assertEquals(orderItem02.getPayPrice(), 150); + assertEquals(orderItem02.getOrderPartPrice(), 0); + assertEquals(orderItem02.getOrderDividePrice(), 150); + // 断言 Promotion 部分 + assertEquals(priceCalculate.getPromotions().size(), 1); + PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0); + assertEquals(promotion01.getId(), 1000L); + assertEquals(promotion01.getName(), "活动 1000 号"); + assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); + assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); + assertEquals(promotion01.getOriginalPrice(), 350); + assertEquals(promotion01.getDiscountPrice(), 0); + assertFalse(promotion01.getMeet()); + assertEquals(promotion01.getMeetTip(), "TODO"); // TODO 芋艿:后面再想想 + assertEquals(promotion01.getItems().size(), 2); + PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0); + assertEquals(promotionItem011.getSkuId(), 10L); + assertEquals(promotionItem011.getOriginalPrice(), 200); + assertEquals(promotionItem011.getDiscountPrice(), 0); + PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1); + assertEquals(promotionItem012.getSkuId(), 20L); + assertEquals(promotionItem012.getOriginalPrice(), 150); + assertEquals(promotionItem012.getDiscountPrice(), 0); + } + + @Test + public void testCalculatePrice_coupon() { + // 准备参数 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId()) + .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2), + new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3), + new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4))) + .setCouponId(1024L); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L)); + ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L)); + ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L)); + when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03)); + // mock 方法(优惠劵 Coupon 信息) + CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setName("程序员节") + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L)) + .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()) + .setDiscountPercent(50).setDiscountLimitPrice(70)); + when(couponService.validCoupon(eq(1024L), eq(calculateReqDTO.getUserId()))).thenReturn(coupon); + + // 调用 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); + // 断言 Order 部分 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder(); + assertEquals(order.getOriginalPrice(), 470); + assertEquals(order.getOrderPrice(), 470); + assertEquals(order.getDiscountPrice(), 0); + assertEquals(order.getPointPrice(), 0); + assertEquals(order.getDeliveryPrice(), 0); + assertEquals(order.getPayPrice(), 400); + assertEquals(order.getCouponId(), 1024L); + assertEquals(order.getCouponPrice(), 70); + // 断言 OrderItem 部分 + assertEquals(order.getItems().size(), 3); + PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0); + assertEquals(orderItem01.getSkuId(), 10L); + assertEquals(orderItem01.getCount(), 2); + assertEquals(orderItem01.getOriginalPrice(), 200); + assertEquals(orderItem01.getOriginalUnitPrice(), 100); + assertEquals(orderItem01.getDiscountPrice(), 0); + assertEquals(orderItem01.getPayPrice(), 200); + assertEquals(orderItem01.getOrderPartPrice(), 40); + assertEquals(orderItem01.getOrderDividePrice(), 160); + PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1); + assertEquals(orderItem02.getSkuId(), 20L); + assertEquals(orderItem02.getCount(), 3); + assertEquals(orderItem02.getOriginalPrice(), 150); + assertEquals(orderItem02.getOriginalUnitPrice(), 50); + assertEquals(orderItem02.getDiscountPrice(), 0); + assertEquals(orderItem02.getPayPrice(), 150); + assertEquals(orderItem02.getOrderPartPrice(), 30); + assertEquals(orderItem02.getOrderDividePrice(), 120); + PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2); + assertEquals(orderItem03.getSkuId(), 30L); + assertEquals(orderItem03.getCount(), 4); + assertEquals(orderItem03.getOriginalPrice(), 120); + assertEquals(orderItem03.getOriginalUnitPrice(), 30); + assertEquals(orderItem03.getDiscountPrice(), 0); + assertEquals(orderItem03.getPayPrice(), 120); + assertEquals(orderItem03.getOrderPartPrice(), 0); + assertEquals(orderItem03.getOrderDividePrice(), 120); + // 断言 Promotion 部分 + assertEquals(priceCalculate.getPromotions().size(), 1); + PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0); + assertEquals(promotion01.getId(), 1024L); + assertEquals(promotion01.getName(), "程序员节"); + assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType()); + assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel()); + assertEquals(promotion01.getOriginalPrice(), 350); + assertEquals(promotion01.getDiscountPrice(), 70); + assertTrue(promotion01.getMeet()); + assertEquals(promotion01.getMeetTip(), "优惠劵:省 0.70 元"); + assertEquals(promotion01.getItems().size(), 2); + PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0); + assertEquals(promotionItem011.getSkuId(), 10L); + assertEquals(promotionItem011.getOriginalPrice(), 200); + assertEquals(promotionItem011.getDiscountPrice(), 40); + PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1); + assertEquals(promotionItem012.getSkuId(), 20L); + assertEquals(promotionItem012.getOriginalPrice(), 150); + assertEquals(promotionItem012.getDiscountPrice(), 30); + } + + @Test + public void testGetMeetCouponList() { + // 准备参数 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1024L) + .setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2))); + // mock 方法(商品 SKU 信息) + ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100)); + when(productSkuApi.getSkuList(eq(asSet(10L)))).thenReturn(singletonList(productSku)); + // mock 方法(情况一:优惠劵未到使用时间) + CouponDO coupon01 = randomPojo(CouponDO.class); + doThrow(new ServiceException(COUPON_VALID_TIME_NOT_NOW)).when(couponService).validCoupon(coupon01); + // mock 方法(情况二:所结算商品没有符合条件的商品) + CouponDO coupon02 = randomPojo(CouponDO.class); + // mock 方法(情况三:使用金额不足) + CouponDO coupon03 = randomPojo(CouponDO.class, o -> o.setProductScope(PromotionProductScopeEnum.ALL.getScope()) + .setUsePrice(300)); + // mock 方法(情况五:满足条件) + CouponDO coupon04 = randomPojo(CouponDO.class, o -> o.setProductScope(PromotionProductScopeEnum.ALL.getScope()) + .setUsePrice(190)); + // mock 方法(获得用户的待使用优惠劵) + when(couponService.getCouponList(eq(1024L), eq(CouponStatusEnum.UNUSED.getStatus()))) + .thenReturn(asList(coupon01, coupon02, coupon03, coupon04)); + // 调用 + List list = priceService.getMeetCouponList(calculateReqDTO); + // 断言 + assertEquals(list.size(), 4); + // 断言情况一:优惠劵未到使用时间 + CouponMeetRespDTO couponMeetRespDTO01 = list.get(0); + assertPojoEquals(couponMeetRespDTO01, coupon01); + assertFalse(couponMeetRespDTO01.getMeet()); + assertEquals(couponMeetRespDTO01.getMeetTip(), "优惠劵未到使用时间"); + // 断言情况二:所结算商品没有符合条件的商品 + CouponMeetRespDTO couponMeetRespDTO02 = list.get(1); + assertPojoEquals(couponMeetRespDTO02, coupon02); + assertFalse(couponMeetRespDTO02.getMeet()); + assertEquals(couponMeetRespDTO02.getMeetTip(), "所结算商品没有符合条件的商品"); + // 断言情况三:差 %s 元可用优惠劵 + CouponMeetRespDTO couponMeetRespDTO03 = list.get(2); + assertPojoEquals(couponMeetRespDTO03, coupon03); + assertFalse(couponMeetRespDTO03.getMeet()); + assertEquals(couponMeetRespDTO03.getMeetTip(), "所结算的商品中未满足使用的金额"); + // 断言情况四:满足条件 + CouponMeetRespDTO couponMeetRespDTO04 = list.get(3); + assertPojoEquals(couponMeetRespDTO04, coupon04); + assertTrue(couponMeetRespDTO04.getMeet()); + assertNull(couponMeetRespDTO04.getMeetTip()); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImplTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImplTest.java new file mode 100755 index 0000000000..9f9e28c078 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImplTest.java @@ -0,0 +1,218 @@ +package cn.iocoder.yudao.module.promotion.service.reward; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.time.Duration; +import java.util.Map; +import java.util.Set; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_NOT_EXISTS; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; + +/** +* {@link RewardActivityServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(RewardActivityServiceImpl.class) +public class RewardActivityServiceImplTest extends BaseDbUnitTest { + + @Resource + private RewardActivityServiceImpl rewardActivityService; + + @Resource + private RewardActivityMapper rewardActivityMapper; + + @Test + public void testCreateRewardActivity_success() { + // 准备参数 + RewardActivityCreateReqVO reqVO = randomPojo(RewardActivityCreateReqVO.class, o -> { + o.setConditionType(randomEle(PromotionConditionTypeEnum.values()).getType()); + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope()); + // 用于触发进行中的状态 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2))); + }); + + // 调用 + Long rewardActivityId = rewardActivityService.createRewardActivity(reqVO); + // 断言 + assertNotNull(rewardActivityId); + // 校验记录的属性是否正确 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(rewardActivityId); + assertPojoEquals(reqVO, rewardActivity, "rules"); + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus()); + for (int i = 0; i < reqVO.getRules().size(); i++) { + assertPojoEquals(reqVO.getRules().get(i), rewardActivity.getRules().get(i)); + } + } + + @Test + public void testUpdateRewardActivity_success() { + // mock 数据 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus())); + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + RewardActivityUpdateReqVO reqVO = randomPojo(RewardActivityUpdateReqVO.class, o -> { + o.setId(dbRewardActivity.getId()); // 设置更新的 ID + o.setConditionType(randomEle(PromotionConditionTypeEnum.values()).getType()); + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope()); + // 用于触发进行中的状态 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2))); + }); + + // 调用 + rewardActivityService.updateRewardActivity(reqVO); + // 校验是否更新正确 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, rewardActivity, "rules"); + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus()); + for (int i = 0; i < reqVO.getRules().size(); i++) { + assertPojoEquals(reqVO.getRules().get(i), rewardActivity.getRules().get(i)); + } + } + + @Test + public void testCloseRewardActivity() { + // mock 数据 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus())); + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbRewardActivity.getId(); + + // 调用 + rewardActivityService.closeRewardActivity(id); + // 校验状态 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(id); + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.CLOSE.getStatus()); + } + + @Test + public void testUpdateRewardActivity_notExists() { + // 准备参数 + RewardActivityUpdateReqVO reqVO = randomPojo(RewardActivityUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> rewardActivityService.updateRewardActivity(reqVO), REWARD_ACTIVITY_NOT_EXISTS); + } + + @Test + public void testDeleteRewardActivity_success() { + // mock 数据 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus())); + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbRewardActivity.getId(); + + // 调用 + rewardActivityService.deleteRewardActivity(id); + // 校验数据不存在了 + assertNull(rewardActivityMapper.selectById(id)); + } + + @Test + public void testDeleteRewardActivity_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> rewardActivityService.deleteRewardActivity(id), REWARD_ACTIVITY_NOT_EXISTS); + } + + @Test + public void testGetRewardActivityPage() { + // mock 数据 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> { // 等会查询到 + o.setName("芋艿"); + o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()); + }); + rewardActivityMapper.insert(dbRewardActivity); + // 测试 name 不匹配 + rewardActivityMapper.insert(cloneIgnoreId(dbRewardActivity, o -> o.setName("土豆"))); + // 测试 status 不匹配 + rewardActivityMapper.insert(cloneIgnoreId(dbRewardActivity, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus()))); + // 准备参数 + RewardActivityPageReqVO reqVO = new RewardActivityPageReqVO(); + reqVO.setName("芋艿"); + reqVO.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()); + + // 调用 + PageResult pageResult = rewardActivityService.getRewardActivityPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbRewardActivity, pageResult.getList().get(0), "rules"); + } + + @Test + public void testGetRewardActivities_all() { + // mock 数据 + RewardActivityDO allActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus()) + .setProductScope(PromotionProductScopeEnum.ALL.getScope())); + rewardActivityMapper.insert(allActivity); + RewardActivityDO productActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus()) + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))); + rewardActivityMapper.insert(productActivity); + // 准备参数 + Set spuIds = asSet(1L, 2L); + + // 调用 + Map> matchRewardActivities = rewardActivityService.getMatchRewardActivities(spuIds); + // 断言 + assertEquals(matchRewardActivities.size(), 1); + Map.Entry> next = matchRewardActivities.entrySet().iterator().next(); + assertPojoEquals(next.getKey(), allActivity); + assertEquals(next.getValue(), spuIds); + } + + @Test + public void testGetRewardActivities_product() { + // mock 数据 + RewardActivityDO productActivity01 = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus()) + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))); + rewardActivityMapper.insert(productActivity01); + RewardActivityDO productActivity02 = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus()) + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(singletonList(3L))); + rewardActivityMapper.insert(productActivity02); + // 准备参数 + Set spuIds = asSet(1L, 2L, 3L); + + // 调用 + Map> matchRewardActivities = rewardActivityService.getMatchRewardActivities(spuIds); + // 断言 + assertEquals(matchRewardActivities.size(), 2); + matchRewardActivities.forEach((activity, activitySpuIds) -> { + if (activity.getId().equals(productActivity01.getId())) { + assertPojoEquals(activity, productActivity01); + assertEquals(activitySpuIds, asSet(1L, 2L)); + } else if (activity.getId().equals(productActivity02.getId())) { + assertPojoEquals(activity, productActivity02); + assertEquals(activitySpuIds, asSet(3L)); + } else { + fail(); + } + }); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/application-unit-test.yaml b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/application-unit-test.yaml similarity index 100% rename from yudao-module-mall/yudao-module-market-biz/src/test/resources/application-unit-test.yaml rename to yudao-module-mall/yudao-module-promotion-biz/src/test/resources/application-unit-test.yaml diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/logback.xml b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/logback.xml similarity index 100% rename from yudao-module-mall/yudao-module-market-biz/src/test/resources/logback.xml rename to yudao-module-mall/yudao-module-promotion-biz/src/test/resources/logback.xml diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql new file mode 100644 index 0000000000..d3f8e37188 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql @@ -0,0 +1,6 @@ +DELETE FROM "market_activity"; +DELETE FROM "promotion_coupon_template"; +DELETE FROM "promotion_coupon"; +DELETE FROM "promotion_reward_activity"; +DELETE FROM "promotion_discount_activity"; +DELETE FROM "promotion_discount_product"; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000000..7ff1a72399 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,124 @@ +CREATE TABLE IF NOT EXISTS "market_activity" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "title" varchar(50) NOT NULL, + "activity_type" tinyint(4) NOT NULL, + "status" tinyint(4) NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "invalid_time" datetime, + "delete_time" datetime, + "time_limited_discount" varchar(2000), + "full_privilege" varchar(2000), + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint(20) NOT NULL, + PRIMARY KEY ("id") + ) COMMENT '促销活动'; + +CREATE TABLE IF NOT EXISTS "promotion_coupon_template" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "total_count" int NOT NULL, + "take_limit_count" int NOT NULL, + "take_type" int NOT NULL, + "use_price" int NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "validity_type" int NOT NULL, + "valid_start_time" datetime, + "valid_end_time" datetime, + "fixed_start_term" int, + "fixed_end_term" int, + "discount_type" int NOT NULL, + "discount_percent" int, + "discount_price" int, + "discount_limit_price" int, + "take_count" int NOT NULL DEFAULT 0, + "use_count" int NOT NULL DEFAULT 0, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '优惠劵模板'; + +CREATE TABLE IF NOT EXISTS "promotion_coupon" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "template_id" bigint NOT NULL, + "name" varchar NOT NULL, + "status" int NOT NULL, + "user_id" bigint NOT NULL, + "take_type" int NOT NULL, + "useprice" int NOT NULL, + "valid_start_time" datetime NOT NULL, + "valid_end_time" datetime NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "discount_type" int NOT NULL, + "discount_percent" int, + "discount_price" int, + "discount_limit_price" int, + "use_order_id" bigint, + "use_time" datetime, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '优惠劵'; + +CREATE TABLE IF NOT EXISTS "promotion_reward_activity" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "remark" varchar, + "condition_type" int NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "rules" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '满减送活动'; + +CREATE TABLE IF NOT EXISTS "promotion_discount_activity" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '限时折扣活动'; + +CREATE TABLE IF NOT EXISTS "promotion_discount_product" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "activity_id" bigint NOT NULL, + "spu_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "discount_type" int NOT NULL, + "discount_percent" int, + "discount_price" int, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '限时折扣活动'; diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java new file mode 100644 index 0000000000..8725ac58f1 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.trade.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * 交易 错误码枚举类 + * 交易系统,使用 1-011-000-000 段 + * + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface ErrorCodeConstants { + + // ========== Order 模块 1-011-000-000 ========== + ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在"); + ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖"); + ErrorCode ORDER_CREATE_SKU_NOT_SALE = new ErrorCode(1011000003, "商品 SKU 不可售卖"); + ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足"); + ErrorCode ORDER_CREATE_SPU_NOT_FOUND = new ErrorCode(1011000005, "商品 SPU 不可售卖"); + ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在"); + + // ========== Cart 模块 1-011-001-000 ========== + ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1001001000, "购物车项不存在"); + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java deleted file mode 100644 index 5bdc0ff827..0000000000 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.trade.enums.enums; - -import cn.iocoder.yudao.framework.common.exception.ErrorCode; - -/** - * Trade-Order 错误码 Core 枚举类 - *

- * Trade-Order 系统,使用 1-011-000-000 段 - * - * @author LeeYan9 - * @since 2022-08-26 - */ -public interface ErrorCodeConstants { - - /** - * ========== Order 模块 1-011-000-000 ========== - */ - ErrorCode ORDER_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品不存在"); - ErrorCode ORDER_SPU_NOT_SALE = new ErrorCode(1011000002, "商品不可售卖"); - ErrorCode ORDER_SKU_NOT_SALE = new ErrorCode(1011000003, "商品Sku不可售卖"); - ErrorCode ORDER_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品库存不足"); - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/pom.xml b/yudao-module-mall/yudao-module-trade-biz/pom.xml index 5a8512232c..d92ec23cd1 100644 --- a/yudao-module-mall/yudao-module-trade-biz/pom.xml +++ b/yudao-module-mall/yudao-module-trade-biz/pom.xml @@ -29,16 +29,19 @@ yudao-module-product-api ${revision} - cn.iocoder.boot yudao-module-pay-api ${revision} - cn.iocoder.boot - yudao-module-market-api + yudao-module-promotion-api + ${revision} + + + cn.iocoder.boot + yudao-module-member-api ${revision} @@ -53,11 +56,18 @@ cn.iocoder.boot yudao-spring-boot-starter-web + cn.iocoder.boot yudao-spring-boot-starter-security + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-pay + + cn.iocoder.boot diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/package-info.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/package-info.java new file mode 100644 index 0000000000..08acfdbca5 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 基础包,放一些通用的 VO 类 + */ +package cn.iocoder.yudao.module.trade.controller.app.base; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java new file mode 100644 index 0000000000..e116f5888f --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.controller.app.base.property; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("用户 App - 规格 + 规格值 Response VO") +@Data +public class AppProductPropertyValueDetailRespVO { + + @ApiModelProperty(value = "属性的编号", required = true, example = "1") + private Long propertyId; + + @ApiModelProperty(value = "属性的名称", required = true, example = "颜色") + private String propertyName; + + @ApiModelProperty(value = "属性值的编号", required = true, example = "1024") + private Long valueId; + + @ApiModelProperty(value = "属性值的名称", required = true, example = "红色") + private String valueName; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java new file mode 100644 index 0000000000..ed666a9c89 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.trade.controller.app.base.sku; + +import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 商品 SKU 基础 Response VO + * + * @author 芋道源码 + */ +@Data +public class AppProductSkuBaseRespVO { + + @ApiModelProperty(value = "主键", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "商品 SKU 名字", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "图片地址", example = "https://www.iocoder.cn/xx.png") + private String picUrl; + + @ApiModelProperty(value = "库存", required = true, example = "1") + private Integer stock; + + /** + * 规格数组 + */ + private List properties; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java new file mode 100644 index 0000000000..c7d9f236ff --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.trade.controller.app.base.spu; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 商品 SPU 基础 Response VO + * + * @author 芋道源码 + */ +@Data +public class AppProductSpuBaseRespVO { + + @ApiModelProperty(value = "主键", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "商品 SPU 名字", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "商品主图地址", example = "https://www.iocoder.cn/xx.png") + private List picUrls; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java deleted file mode 100644 index 91e51dfd51..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.trade.controller.app.cart; - -import io.swagger.annotations.Api; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Api(tags = "购物车 API") -@RestController -@RequestMapping("/cart") -@Validated -public class CartController { - -// @Autowired -// private CartManager cartManager; -// -// @PostMapping("add") -// @ApiOperation("添加商品到购物车") -// @ApiImplicitParams({ -// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"), -// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024") -// }) -// @RequiresAuthenticate -// public CommonResult addCartItem(@RequestParam("skuId") Integer skuId, -// @RequestParam("quantity") Integer quantity) { -// cartManager.addCartItem(UserSecurityContextHolder.getUserId(), skuId, quantity); -// return success(true); -// } -// -// @GetMapping("sum-quantity") -// @ApiOperation("查询用户在购物车中的商品数量") -// @RequiresAuthenticate -// public CommonResult sumCartItemQuantity() { -// return success(cartManager.sumCartItemQuantity(UserSecurityContextHolder.getUserId())); -// } -// -// @GetMapping("/get-detail") -// @ApiOperation("查询用户的购物车的商品列表") -// @RequiresAuthenticate -// public CommonResult getCartDetail() { -// return success(cartManager.getCartDetail(UserSecurityContextHolder.getUserId())); -// } -// -// @PostMapping("update-quantity") -// @ApiOperation("更新购物车商品数量") -// @ApiImplicitParams({ -// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"), -// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024") -// }) -// @RequiresAuthenticate -// public CommonResult updateCartItemQuantity(@RequestParam("skuId") Integer skuId, -// @RequestParam("quantity") Integer quantity) { -// cartManager.updateCartItemQuantity(UserSecurityContextHolder.getUserId(), skuId, quantity); -// return success(true); -// } -// -// @PostMapping("update-selected") -// @ApiOperation("更新购物车商品是否选中") -// @ApiImplicitParams({ -// @ApiImplicitParam(name = "skuIds", value = "商品 SKU 编号数组", required = true, example = "1,3"), -// @ApiImplicitParam(name = "selected", value = "是否选中", required = true, example = "true") -// }) -// @RequiresAuthenticate -// public CommonResult updateCartItemSelected(@RequestParam("skuIds") Set skuIds, -// @RequestParam("selected") Boolean selected) { -// cartManager.updateCartItemSelected(UserSecurityContextHolder.getUserId(), skuIds, selected); -// // 获得目前购物车明细 -// return success(true); -// } - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http new file mode 100644 index 0000000000..3ce8797fce --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http @@ -0,0 +1,47 @@ +### 请求 /trade/cart/add-count 接口 => 成功 +POST {{appApi}}/trade/cart/add-count +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} +Content-Type: application/json + +{ + "skuId": 1, + "count": 1 +} + +### 请求 /trade/cart/update-count 接口 => 成功 +PUT {{appApi}}/trade/cart/update-count +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} +Content-Type: application/json + +{ + "skuId": 1, + "count": 5 +} + +### 请求 /trade/cart/update-selected 接口 => 成功 +PUT {{appApi}}/trade/cart/update-selected +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} +Content-Type: application/json + +{ + "skuIds": [1], + "selected": false +} + +### 请求 /trade/cart/delete 接口 => 成功 +DELETE {{appApi}}/trade/cart/delete?skuIds=1 +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +### 请求 /trade/cart/get-count 接口 => 成功 +GET {{appApi}}/trade/cart/get-count +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +### 请求 /trade/cart/get-detail 接口 => 成功 +GET {{appApi}}/trade/cart/get-detail +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java new file mode 100644 index 0000000000..fa12e2f09f --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO; +import cn.iocoder.yudao.module.trade.service.cart.TradeCartService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Api(tags = "用户 App - 购物车") +@RestController +@RequestMapping("/trade/cart") +@RequiredArgsConstructor +@Validated +@Slf4j +public class TradeCartController { + + @Resource + private TradeCartService cartService; + + @PostMapping("/add-count") + @ApiOperation("添加商品到购物车") + @PreAuthenticated + public CommonResult addCartItemCount(@Valid @RequestBody AppTradeCartItemAddCountReqVO addCountReqVO) { + cartService.addCartItemCount(getLoginUserId(), addCountReqVO); + return success(true); + } + + @PutMapping("update-count") + @ApiOperation("更新购物车商品数量") + @PreAuthenticated + public CommonResult updateCartItemQuantity(@Valid @RequestBody AppTradeCartItemUpdateCountReqVO updateCountReqVO) { + cartService.updateCartItemCount(getLoginUserId(), updateCountReqVO); + return success(true); + } + + @PutMapping("update-selected") + @ApiOperation("更新购物车商品是否选中") + @PreAuthenticated + public CommonResult updateCartItemSelected(@Valid @RequestBody AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) { + cartService.updateCartItemSelected(getLoginUserId(), updateSelectedReqVO); + // 获得目前购物车明细 + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除购物车商品") + @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号的数组", required = true, example = "1024,2048", dataTypeClass = List.class) + @PreAuthenticated + public CommonResult deleteCartItem(@RequestParam("skuIds") List skuIds) { + cartService.deleteCartItems(getLoginUserId(), skuIds); + return success(true); + } + + @GetMapping("get-count") + @ApiOperation("查询用户在购物车中的商品数量") + @PreAuthenticated + public CommonResult getCartCount() { + return success(cartService.getCartCount(getLoginUserId())); + } + + @GetMapping("/get-detail") + @ApiOperation("查询用户的购物车的详情") + @PreAuthenticated + public CommonResult getCartDetail() { + return success(cartService.getCartDetail(getLoginUserId())); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartDetailRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartDetailRespVO.java new file mode 100644 index 0000000000..5656c3d69d --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartDetailRespVO.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart.vo; + +import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@ApiModel(value = "用户 App - 用户的购物车明细 Response VO") +@Data +public class AppTradeCartDetailRespVO { + + /** + * 商品分组数组 + */ + private List itemGroups; + + /** + * 费用 + */ + private Order order; + + @ApiModel(value = "商品分组", description = "多个商品,参加同一个活动,从而形成分组") + @Data + public static class ItemGroup { + + /** + * 商品数组 + */ + private List items; + /** + * 营销活动,订单级别 + */ + private Promotion promotion; + + } + + @ApiModel(value = "商品 SKU") + @Data + public static class Sku extends AppProductSkuBaseRespVO { + + /** + * SPU 信息 + */ + private AppProductSkuBaseRespVO spu; + + // ========== 购物车相关的字段 ========== + + @ApiModelProperty(value = "商品数量", required = true, example = "1") + private Integer count; + @ApiModelProperty(value = "是否选中", required = true, example = "true") + private Boolean selected; + + // ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ========== + + // TODO 芋艿:后续可以去除一些无用的字段 + + @ApiModelProperty(value = "商品原价(单)", required = true, example = "100") + private Integer originalPrice; + @ApiModelProperty(value = "商品原价(总)", required = true, example = "200") + private Integer totalOriginalPrice; + @ApiModelProperty(value = "商品级优惠(总)", required = true, example = "300") + private Integer totalPromotionPrice; + @ApiModelProperty(value = "最终购买金额(总)", required = true, example = "400") + private Integer totalPresentPrice; + @ApiModelProperty(value = "最终购买金额(单)", required = true, example = "500") + private Integer presentPrice; + @ApiModelProperty(value = "应付金额(总)", required = true, example = "600") + private Integer totalPayPrice; + + // ========== 营销相关的字段 ========== + /** + * 营销活动,商品级别 + */ + private Promotion promotion; + + } + + @ApiModel(value = "订单", description = "对应 PriceCalculateRespDTO.Order 类,用于费用(合计)") + @Data + public static class Order { + + // TODO 芋艿:后续可以去除一些无用的字段 + + @ApiModelProperty(value = "商品原价(总)", required = true, example = "100") + private Integer skuOriginalPrice; + @ApiModelProperty(value = "商品优惠(总)", required = true, example = "200") + private Integer skuPromotionPrice; + @ApiModelProperty(value = "订单优惠(总)", required = true, example = "300") + private Integer orderPromotionPrice; + @ApiModelProperty(value = "运费金额", required = true, example = "400") + private Integer deliveryPrice; + @ApiModelProperty(value = "应付金额(总)", required = true, example = "500") + private Integer payPrice; + + } + + @ApiModel(value = "营销活动", description = "对应 PriceCalculateRespDTO.Promotion 类的属性") + @Data + public static class Promotion { + + @ApiModelProperty(value = "营销编号", required = true, example = "1024", notes = "营销活动的编号、优惠劵的编号") + private Long id; + @ApiModelProperty(value = "营销名字", required = true, example = "xx 活动") + private String name; + @ApiModelProperty(value = "营销类型", required = true, example = "1", notes = "参见 PromotionTypeEnum 枚举类") + private Integer type; + + // ========== 匹配情况 ========== + @ApiModelProperty(value = "是否满足优惠条件", required = true, example = "true") + private Boolean meet; + @ApiModelProperty(value = "满足条件的提示", required = true, example = "圣诞价:省 150.00 元") + private String meetTip; + + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemAddCountReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemAddCountReqVO.java new file mode 100644 index 0000000000..c3103dcce5 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemAddCountReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +@ApiModel(value = "用户 App - 购物车添加购物项 Request VO") +@Data +public class AppTradeCartItemAddCountReqVO { + + @ApiModelProperty(value = "商品 SKU 编号", required = true,example = "1024") + @NotNull(message = "商品 SKU 编号不能为空") + private Long skuId; + + @ApiModelProperty(value = "商品数量", required = true, example = "1", notes = "注意,这是新增数量") + @NotNull(message = "数量不能为空") + @Min(message = "数量必须大于 0", value = 1L) + private Integer count; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateCountReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateCountReqVO.java new file mode 100644 index 0000000000..6beb093799 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateCountReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +@ApiModel(value = "用户 App - 购物车更新数量 Request VO") +@Data +public class AppTradeCartItemUpdateCountReqVO { + + @ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1024") + @NotNull(message = "商品 SKU 编号不能为空") + private Long skuId; + + @ApiModelProperty(value = "商品数量", required = true, example = "1") + @NotNull(message = "数量不能为空") + @Min(message = "数量必须大于 0", value = 1L) + private Integer count; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java new file mode 100644 index 0000000000..cc7bf6f030 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Collection; + +@ApiModel(value = "用户 App - 购物车更新是否选中 Request VO") +@Data +public class AppTradeCartItemUpdateSelectedReqVO { + + @ApiModelProperty(value = "商品 SKU 编号列表", required = true, example = "1024,2048") + @NotNull(message = "商品 SKU 编号列表不能为空") + private Collection skuIds; + + @ApiModelProperty(value = "是否选中", required = true, example = "true") + @NotNull(message = "是否选中不能为空") + private Boolean selected; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java deleted file mode 100644 index 403efbebef..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java +++ /dev/null @@ -1,211 +0,0 @@ -package cn.iocoder.yudao.module.trade.controller.app.cart.vo; - -import io.swagger.annotations.ApiModel; -import lombok.Data; -import lombok.experimental.Accessors; - -import java.util.List; - -@ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿:swagger 文档完善 -@Data -@Accessors(chain = true) -public class CartDetailVO { - - /** - * 商品分组数组 - */ - private List itemGroups; - /** - * 费用 - */ - private Fee fee; - - /** - * 商品分组 - * - * 多个商品,参加同一个活动,从而形成分组。 - */ - @Data - @Accessors(chain = true) - public static class ItemGroup { - -// /** -// * 优惠活动 -// */ -// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 - /** - * 促销减少的金额 - * - * 1. 若未参与促销活动,或不满足促销条件,返回 null - * 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。 - */ - private Integer activityDiscountTotal; - /** - * 商品数组 - */ - private List items; - - } - - @Data - @Accessors(chain = true) - public static class Sku { - - // SKU 自带信息 - /** - * sku 编号 - */ - private Integer id; - /** - * SPU 信息 - */ - private Spu spu; - /** - * 图片地址 - */ - private String picURL; -// /** -// * 规格值数组 -// */ -// private List attrs; // TODO 后面改下 - /** - * 价格,单位:分 - */ - private Integer price; - /** - * 库存数量 - */ - private Integer quantity; - - // 非 SKU 自带信息 - - /** - * 购买数量 - */ - private Integer buyQuantity; - /** - * 是否选中 - */ - private Boolean selected; -// /** -// * 优惠活动 -// */ -// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 - /** - * 原始单价,单位:分。 - */ - private Integer originPrice; - /** - * 购买单价,单位:分 - */ - private Integer buyPrice; - /** - * 最终价格,单位:分。 - */ - private Integer presentPrice; - /** - * 购买总金额,单位:分 - * - * 用途类似 {@link #presentTotal} - */ - private Integer buyTotal; - /** - * 优惠总金额,单位:分。 - */ - private Integer discountTotal; - /** - * 最终总金额,单位:分。 - * - * 注意,presentPrice * quantity 不一定等于 presentTotal 。 - * 因为,存在无法整除的情况。 - * 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。 - * 所以,需要存储一个该字段。 - */ - private Integer presentTotal; - - } - - @Data - @Accessors(chain = true) - public static class Spu { - - /** - * SPU 编号 - */ - private Integer id; - - // ========== 基本信息 ========= - /** - * SPU 名字 - */ - private String name; - /** - * 分类编号 - */ - private Integer cid; - /** - * 商品主图地址 - * - * 数组,以逗号分隔 - * - * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张 - */ - private List picUrls; - - } - - /** - * 费用(合计) - */ - @Data - @Accessors(chain = true) - public static class Fee { - - /** - * 购买总价 - */ - private Integer buyTotal; - /** - * 优惠总价 - * - * 注意,满多少元包邮,不算在优惠中。 - */ - private Integer discountTotal; - /** - * 邮费 - */ - private Integer postageTotal; - /** - * 最终价格 - * - * 计算公式 = 总价 - 优惠总价 + 邮费 - */ - private Integer presentTotal; - - public Fee() { - } - - public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) { - this.buyTotal = buyTotal; - this.discountTotal = discountTotal; - this.postageTotal = postageTotal; - this.presentTotal = presentTotal; - } - - } - - /** - * 邮费信息 TODO 芋艿,未完成 - */ - @Data - @Accessors(chain = true) - public static class Postage { - - /** - * 需要满足多少钱,可以包邮。单位:分 - */ - private Integer threshold; - - } - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 3b1766bd3e..4233867e13 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -23,16 +23,13 @@ import javax.servlet.http.HttpServletRequest; @Api(tags = "用户 App - 交易订单") @RestController @RequestMapping("/trade/order") -@RequiredArgsConstructor +@RequiredArgsConstructor // TODO @LeeYan9: 先统一使用 @Resource 注入哈; 项目只有三层, 依赖注入会存在, 所以使用 @Resource; 也因此, 最好全局保持一致 @Validated @Slf4j public class AppTradeOrderController { - // TODO 在思考下; - private final TradeOrderService tradeOrderService; - @GetMapping("/get-create-info") @ApiOperation("基于商品,确认创建订单") @PreAuthenticated @@ -46,16 +43,12 @@ public class AppTradeOrderController { @PreAuthenticated public CommonResult createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO, HttpServletRequest servletRequest) { -// return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(), -// HttpUtil.getIp(servletRequest), createReqVO)); - // 获取登录用户 + // 获取登录用户、用户 IP 地址 Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); - // 获取用户ip地址 String clientIp = ServletUtil.getClientIP(servletRequest); // 创建交易订单,预支付记录 - Long result = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO); - - return CommonResult.success(result); + Long orderId = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO); + return CommonResult.success(orderId); } @GetMapping("/get") diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java index 54e856a6bf..5126de4a51 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java @@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.List; @@ -14,12 +15,12 @@ public class AppTradeOrderCreateReqVO { @ApiModelProperty(name = "收件地址编号", required = true, example = "1") @NotNull(message = "收件地址不能为空") - private Integer addressId; + private Long addressId; @ApiModelProperty(name = "优惠劵编号", example = "1024") private Long couponId; - @ApiModelProperty(name = "备注", example = "1024") + @ApiModelProperty(name = "备注", example = "这个是我的订单哟") private String remark; @ApiModelProperty(name = "是否来自购物车", required = true, example = "true", notes = "true - 来自购物车;false - 立即购买") @@ -29,7 +30,7 @@ public class AppTradeOrderCreateReqVO { /** * 订单商品项列表 */ - @NotNull(message = "必须选择购买的商品") + @NotEmpty(message = "必须选择购买的商品") private List items; @ApiModel(value = "订单商品项") diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java new file mode 100644 index 0000000000..eb696ae300 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.trade.convert.cart; + +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO; +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Mapper +public interface TradeCartConvert { + + TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class); + + default AppTradeCartDetailRespVO buildEmptyAppTradeCartDetailRespVO() { + return new AppTradeCartDetailRespVO().setItemGroups(Collections.emptyList()) + .setOrder(new AppTradeCartDetailRespVO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0) + .setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0)); + } + + default PriceCalculateReqDTO convert(Long userId, List cartItems) { + return new PriceCalculateReqDTO().setUserId(userId) + .setItems(convertList(cartItems, cartItem -> new PriceCalculateReqDTO.Item().setSkuId(cartItem.getSkuId()) + .setCount(cartItem.getSelected() ? cartItem.getCount() : 0))); + } + + // ========== AppTradeCartDetailRespVO 相关 ========== + + AppTradeCartDetailRespVO.Promotion convert(PriceCalculateRespDTO.Promotion bean); + + @Mappings({ + @Mapping(source = "cartItem.count", target = "count") + }) + AppTradeCartDetailRespVO.Sku convert(PriceCalculateRespDTO.OrderItem orderItem, TradeCartItemDO cartItem); + + AppTradeCartDetailRespVO.Order convert(PriceCalculateRespDTO.Order bean); + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 1b81212b8d..54abc96984 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -1,21 +1,90 @@ package cn.iocoder.yudao.module.trade.convert.order; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; 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.order.TradeOrderItemRefundStatusEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; -/** - * @author LeeYan9 - * @since 2022-08-26 - */ +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; + @Mapper public interface TradeOrderConvert { TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); - @Mapping(source = "order.couponId", target = "couponId") - TradeOrderDO convert(AppTradeOrderCreateReqVO createReqVO, PriceCalculateRespDTO.Order order); + @Mappings({ + @Mapping(source = "createReqVO.couponId", target = "couponId"), + @Mapping(target = "remark", ignore = true), + @Mapping(source = "createReqVO.remark", target = "userRemark"), + @Mapping(source = "address.name", target = "receiverName"), + @Mapping(source = "address.mobile", target = "receiverMobile"), + @Mapping(source = "address.areaId", target = "receiverAreaId"), + @Mapping(source = "address.postCode", target = "receiverPostCode"), + @Mapping(source = "address.detailAddress", target = "receiverDetailAddress"), + }) + TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, + PriceCalculateRespDTO.Order order, AddressRespDTO address); + + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(source = "sku.spuId", target = "spuId"), + }) + TradeOrderItemDO convert(PriceCalculateRespDTO.OrderItem orderItem, ProductSkuRespDTO sku); + + default List convertList(TradeOrderDO tradeOrderDO, + List orderItems, List skus) { + Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); + return CollectionUtils.convertList(orderItems, orderItem -> { + TradeOrderItemDO tradeOrderItemDO = convert(orderItem, skuMap.get(orderItem.getSkuId())); + tradeOrderItemDO.setOrderId(tradeOrderDO.getId()); + tradeOrderItemDO.setUserId(tradeOrderDO.getUserId()); + tradeOrderItemDO.setRefundStatus(TradeOrderItemRefundStatusEnum.NONE.getStatus()).setRefundTotal(0); // 退款信息 +// tradeOrderItemDO.setCommented(false); + return tradeOrderItemDO; + }); + } + + @Mapping(source = "userId" , target = "userId") + PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId); + + @Mappings({ + @Mapping(source = "skuId", target = "id"), + @Mapping(source = "count", target = "incrCount"), + }) + ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean); + List convertList(List list); + + default PayOrderInfoCreateReqDTO convert(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus, TradeOrderProperties tradeOrderProperties) { + PayOrderInfoCreateReqDTO createReqDTO = new PayOrderInfoCreateReqDTO() + .setAppId(tradeOrderProperties.getAppId()).setUserIp(tradeOrderDO.getUserIp()); + // 商户相关字段 + createReqDTO.setMerchantOrderId(String.valueOf(tradeOrderDO.getId())); + String subject = spus.get(0).getName(); + if (spus.size() > 1) { + subject += " 等多件"; + } + createReqDTO.setSubject(subject); + // 订单相关字段 + createReqDTO.setAmount(tradeOrderDO.getPayPrice()).setExpireTime(addTime(tradeOrderProperties.getExpireTime())); + return createReqDTO; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java deleted file mode 100644 index 23113f9f33..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.order; - -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -/** - * @author LeeYan9 - * @since 2022-08-26 - */ -@Mapper -public interface TradeOrderItemConvert { - - TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class); - - /** - * - * @param tradeOrder 交易订单 - * @param items sku列表价格 - * @return 订单项 - */ - @Mappings({ - @Mapping(source = "tradeOrder.userId", target = "userId"), - @Mapping(source = "tradeOrder.orderId", target = "orderId") - }) - default List convertList(TradeOrderDO tradeOrder, List items) { - // TODO @Com: Mapstruct 生成会报错 - throw new UnsupportedOperationException("无法实现"); - } -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java deleted file mode 100644 index 22dd11fe96..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.pay; - -import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; -import org.mapstruct.factory.Mappers; - -/** - * @author LeeYan9 - * @since 2022-08-26 - */ -public interface PayOrderConvert { - - PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class); - - - PayOrderDataCreateReqDTO convert(TradeOrderDO tradeOrderDO); -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java deleted file mode 100644 index 3b2d173c3e..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.price; - -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; -import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.factory.Mappers; - -/** - * @author LeeYan9 - * @since 2022-08-26 - */ -@Mapper -public interface PriceConvert { - - PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class); - - @Mappings( - @Mapping(source = "userId" , target = "userId") - ) - PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO , Long userId); -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java deleted file mode 100644 index fffc6fa427..0000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.sku; - -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -/** - * @author LeeYan9 - * @since 2022-08-26 - */ -@Mapper -public interface ProductSkuConvert { - - ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class); - - List convert(List tradeOrderItems); -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartItemDO.java similarity index 91% rename from yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java rename to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartItemDO.java index 60cba5991d..05fbb801db 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartItemDO.java @@ -6,16 +6,16 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; -import java.time.LocalDateTime; - /** * 购物车的商品信息 DO + * + * @author 芋道源码 */ @TableName("trade_cart_item") @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) -public class CartItemDO extends BaseDO { +public class TradeCartItemDO extends BaseDO { // ========= 基础字段 BEGIN ========= @@ -27,10 +27,6 @@ public class CartItemDO extends BaseDO { * 是否选中 */ private Boolean selected; - /** - * 购物时间 - */ - private LocalDateTime buyTime; // basket_date // ========= 基础字段 END ========= @@ -62,7 +58,7 @@ public class CartItemDO extends BaseDO { /** * 商品购买数量 */ - private Integer stock; + private Integer count; // ========= 商品信息 END ========= diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java index ed64714717..4d68799b99 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java @@ -2,7 +2,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.module.product.enums.delivery.DeliveryTypeEnum; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; @@ -38,7 +38,7 @@ public class TradeOrderDO extends BaseDO { * * 例如说,1146347329394184195 */ - private String sn; + private String no; /** * 订单类型 * @@ -75,7 +75,7 @@ public class TradeOrderDO extends BaseDO { /** * 购买的商品数量 */ - private Integer productCount; // total_num + private Integer productCount; /** * 订单完成时间 */ @@ -113,44 +113,49 @@ public class TradeOrderDO extends BaseDO { // 价格文档 - 京东到家:https://openo2o.jddj.com/api/getApiDetail/182/4d1494c5e7ac4679bfdaaed950c5bc7f.htm // 价格文档 - 有赞:https://doc.youzanyun.com/detail/API/0/906 -// TODO promotion_details(订单优惠信息明细,商品和订单级优惠一般都在里面) - /** * 商品原价(总),单位:分 * - * 基于 {@link TradeOrderItemDO#getTotalOriginalPrice()} 求和 - */ - // niu - goods_money; - private Integer skuOriginalPrice; - /** - * 商品优惠(总),单位:分 + * 基于 {@link TradeOrderItemDO#getOriginalPrice()} 求和 * - * 基于 {@link TradeOrderItemDO#getTotalPromotionPrice()} 求和 + * 对应 taobao 的 trade.total_fee 字段 */ - private Integer skuPromotionPrice; + private Integer originalPrice; + /** + * 订单原价(总),单位:分 + * + * 基于 {@link OrderItem#getPayPrice()} 求和 + * 和 {@link #originalPrice} 的差异:去除商品级优惠 + */ + private Integer orderPrice; /** * 订单优惠(总),单位:分 * - * 例如说:满减折扣;不包括优惠劵、商品优惠 + * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。 + * + * 对应 taobao 的 order.discount_fee 字段 */ - // niu - promotion_money;taobao - discount_fee(主订单优惠) - private Integer orderPromotionPrice; + private Integer discountPrice; /** * 运费金额,单位:分 */ - // niu - delivery_money;taobao - post_fee(订单邮费) private Integer deliveryPrice; - // TODO 芋艿:taobao 的:trade.adjust_fee/order.adjust_fee(调整金额,如:卖家手动修改订单价格,官方数据修复等等) + /** + * 订单调价(总),单位:分 + * + * 正数,加价;负数,减价 + */ + private Integer adjustPrice; /** * 应付金额(总),单位:分 * - * = {@link #skuOriginalPrice} + * = {@link OrderItem#getPayPrice()} 求和 + * - {@link #couponPrice} + * - {@link #pointPrice} * + {@link #deliveryPrice} - * - {@link #skuPromotionPrice} - * - {@link #orderPromotionPrice} + * - {@link #discountPrice} + * + {@link #adjustPrice} */ - // niu - pay_money;taobao - payment(主订单实付金额) | trade.total_fee(主订单应付金额,参考使用); -// * - {@link #couponPrice} // TODO 芋艿:靠营销表记录 private Integer payPrice; /** * 支付订单编号 @@ -166,31 +171,16 @@ public class TradeOrderDO extends BaseDO { private Integer payChannel; // ========== 收件 + 物流基本信息 ========== - /** - * 配送方式 - * 会员用户下单时,选择的配送方式 - * - * 枚举 {@link DeliveryTypeEnum} - */ - private Integer deliveryType; - /** - * 实际的配送方式 - * 管理后台发货时,选择的配送方式 - * - * 0 - 无需物流 - * 枚举 {@link DeliveryTypeEnum} - */ - private Integer actualDeliveryType; // like - shipping_status; /** * 配置模板的编号 * * 关联 DeliveryTemplateDO 的 id 编号 */ - private Long deliveryTemplateId; // dvy_id + private Long deliveryTemplateId; /** * 物流公司单号 */ - private String expressNo; // dvy_flow_id + private String expressNo; /** * 发货状态 * @@ -217,7 +207,7 @@ public class TradeOrderDO extends BaseDO { /** * 收件人地区编号 */ - private Integer receiverAreaId; + private Long receiverAreaId; /** * 收件人邮编 */ @@ -247,58 +237,17 @@ public class TradeOrderDO extends BaseDO { * 优惠劵编号 */ private Long couponId; -// /** -// * 优惠劵减免金额,单位:分 // TODO 芋艿:靠营销表记录 -// */ -// // niu - coupon_money; -// private Integer couponPrice; -// /** -// * 积分抵扣的金额,单位:分 -// */ -// private Integer integralPrice; -// /** -// * 使用的积分 -// */ -// private Integer useIntegral; - - // TODO ========== 待定字段:yv ========= - // TODO cart_id:购物车 id - // TODO refund_reason_wap_img:退款图片 - // TODO refund_reason_wap_explain:退款用户说明 - // TODO refund_reason_time:退款时间 - // TODO refund_reason_wap:前台退款原因 - // TODO refund_reason:不退款的理由 - - // TODO gain_integral:消费赚取积分 - // TODO back_integral:给用户退了多少积分 - - // TODO combination_id:拼团产品id - // TODO pink_id:拼团id - // TODO seckill_id:秒杀产品ID - // TODO bargain_id:砍价id - - // TODO cost:成本价 - // TODO verify_code:核销码 - // TODO store_id:门店id - - // TODO ========== 待定字段:cf ========= - // TODO before_pay_price:改价前支付金额 - // TODO is_alter_price:是否改价 - // TODO out_trade_no:商户系统内部的订单号 String - - // TODO ========== 待定字段:lf ========= - // TODO settle_id:未结算 - // TODO settle_amount:结算金额 - // TODO team_found_id: 拼团id - // TODO team_id: 拼团活动id - // TODO delivery_id: 发货单ID - // TODO attach_values: 附带的值(赠送时机,赠送积分成长值什么的)Json格式 - - // TODO ========== 待定字段:nf ========= - // TODO delivery_code:整体提货编码 - - // TODO ========== 待定字段:niu ========= - // TODO adjust_money '订单调整金额' - // TODO balance_money ''余额支付金额'' + /** + * 优惠劵减免金额,单位:分 + * + * 对应 taobao 的 trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 积分抵扣的金额,单位:分 + * + * 对应 taobao 的 trade.point_fee 字段 + */ + private Integer pointPrice; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java index 14e4de0f43..5dfcf4c6a3 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -1,10 +1,11 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -16,7 +17,7 @@ import java.util.List; * * @author 芋道源码 */ -@TableName(value = "trade_order_item") +@TableName(value = "trade_order_item", autoResultMap = true) @Data @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) @@ -56,7 +57,7 @@ public class TradeOrderItemDO extends BaseDO { /** * 规格值数组,JSON 格式 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = PropertyTypeHandler.class) private List properties; /** * 商品名称 @@ -70,70 +71,70 @@ public class TradeOrderItemDO extends BaseDO { * 购买数量 */ private Integer count; - /** - * 是否评论 - * - * false - 未评论 - * true - 已评论 - */ - private Boolean commented; +// /** +// * 是否评论 TODO +// * +// * false - 未评论 +// * true - 已评论 +// */ +// private Boolean commented; // ========== 价格 + 支付基本信息 ========== + + /** + * 商品原价(总),单位:分 + * + * = {@link #originalUnitPrice} * {@link #getCount()} + */ + private Integer originalPrice; /** * 商品原价(单),单位:分 * * 对应 ProductSkuDO 的 price 字段 + * 对应 taobao 的 order.price 字段 */ - // like - original_price;niu - costPrice - private Integer originalPrice; + private Integer originalUnitPrice; /** - * 商品原价(总),单位:分 + * 商品优惠(总),单位:分 * - * = {@link #originalPrice} * {@link #count} - */ - // like - total_price;niu - 暂无 - private Integer totalOriginalPrice; - /** - * 商品级优惠(总),单位:分 + * 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元 * - * 例如说“限时折扣”:商品原价的 8 折;商品原价的减 50 元 + * 对应 taobao 的 order.discount_fee 字段 */ - // taobao - order.discount_fee(子订单商品优惠) - private Integer totalPromotionPrice; + private Integer discountPrice; /** - * 最终购买金额(单),单位:分。 + * 子订单实付金额,不算主订单分摊金额,单位:分 * - * = {@link #totalPresentPrice} / {@link #count} - */ - private Integer presentPrice; - /** - * 最终购买金额(总),单位:分。 + * = {@link #originalPrice} + * - {@link #discountPrice} * - * = {@link #totalOriginalPrice} - * - {@link #totalPromotionPrice} + * 对应 taobao 的 order.payment 字段 */ - // like - total_pay_price;niu - goods_money; taobao - order.payment(子订单实付金额,不算主订单分摊金额) | order.total_fee(子订单应付金额,参考使用) - private Integer totalPresentPrice; - // TODO 芋艿:part_mjz_discount(子订单分摊金额);本质上,totalOriginalPrice - totalPayPrice + private Integer payPrice; + /** - * 应付金额(总),单位:分 + * 子订单分摊金额(总),单位:分 + * 需要分摊 {@link TradeOrderDO#getDiscountPrice()}、{@link TradeOrderDO#getCouponPrice()}、{@link TradeOrderDO#getPointPrice()} + * + * 对应 taobao 的 order.part_mjz_discount 字段 + * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。 */ - // taobao - divide_order_fee (分摊后子订单实付金额); - private Integer totalPayPrice; + private Integer orderPartPrice; + /** + * 分摊后子订单实付金额(总),单位:分 + * + * = {@link #payPrice} + * - {@link #orderPartPrice} + * + * 对应 taobao 的 divide_order_fee 字段 + */ + private Integer orderDividePrice; // ========== 营销基本信息 ========== -// /** -// * 积分抵扣的金额,单位:分 -// */ -// private Integer integralTotal; // like - integral_price;niu - point_money -// /** -// * 使用的积分 -// */ -// private Integer useIntegral; // niu - use_point // ========== 退款基本信息 ========== /** - * 退款状态 + * 退款状态 TODO * * 枚举 {@link TradeOrderItemRefundStatusEnum} */ @@ -148,7 +149,7 @@ public class TradeOrderItemDO extends BaseDO { // presentTotal = buyTotal - discountTotal = 24 - 11 = 13 // 最终 presentPrice = presentTotal / stock = 13 / 3 = 4.33 /** - * 退款总金额,单位:分 + * 退款总金额,单位:分 TODO */ private Integer refundTotal; @@ -173,42 +174,20 @@ public class TradeOrderItemDO extends BaseDO { } - // TODO 芋艿:basket_date 加入购物车时间; - // TODO 芋艿:distribution_card_no 推广员使用的推销卡号 + // TODO @芋艿:可以找一些新的思路 + public static class PropertyTypeHandler extends AbstractJsonTypeHandler> { - // TODO 待确定:mf - // TODO give_integral:赠送积分 - // TODO is_reply:是否评价,0-未评价,1-已评价 - // TODO is_sub:是否单独分佣,0-否,1-是 - // TODO vip_price:会员价 - // TODO product_type:商品类型:0-普通,1-秒杀,2-砍价,3-拼团,4-视频号 + @Override + protected List parse(String json) { + return JsonUtils.parseArray(json, Property.class); + } - // TODO 待确定:lf - // TODO integral_price:积分抵扣的金额 - // TODO member_price:会员价格 - // TODO is_member:是否为会员折扣;0-不是;1-是 - // TODO member_discount:会员折扣(百分比) + @Override + protected String toJson(List obj) { + return JsonUtils.toJsonString(obj); + } - // TODO goods_info 商品信息 - - // TODO integral_price:积分抵扣的金额 - - // TODO 待确定:niu - // TODO is_virtual '是否是虚拟商品' - // TODO goods_class '商品种类(1.实物 2.虚拟3.卡券)' - // TODO adjust_money ''调整金额'' - - // TODO is_fenxiao 是否分销, - // TODO adjust_money 是否分销, - - // TODO delivery_status '配送状态' - // TODO delivery_no ''配送单号'' - // TODO gift_flag '赠品标识' - // TODO gift_flag '赠品标识' - - // TODO refund_status '退款状态' - // TODO refund_type '退款状态' - // TODO 一堆退款字段 + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartItemMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartItemMapper.java new file mode 100644 index 0000000000..fa6adbf414 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartItemMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.trade.dal.mysql.cart; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@Mapper +public interface TradeCartItemMapper extends BaseMapperX { + + default TradeCartItemDO selectByUserIdAndSkuId(Long userId, Long skuId) { + return selectOne(TradeCartItemDO::getUserId, userId, + TradeCartItemDO::getSkuId, skuId); + } + + default List selectListByUserIdAndSkuIds(Long userId, Collection skuIds) { + return selectList(new LambdaQueryWrapper().eq(TradeCartItemDO::getUserId, userId) + .in(TradeCartItemDO::getSkuId, skuIds)); + } + + default void updateByIds(Collection ids, TradeCartItemDO updateObject) { + update(updateObject, new LambdaQueryWrapper().in(TradeCartItemDO::getId, ids)); + } + + default Integer selectSumByUserId(Long userId) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(count) AS sumCount") + .eq("user_id", userId)); + // 获得数量 + return CollUtil.isNotEmpty(result) ? MapUtil.getInt(result.get(0), "sumCount") : 0; + } + + default List selectListByUserId(Long userId, Boolean selected) { + return selectList(new LambdaQueryWrapperX().eq(TradeCartItemDO::getUserId, userId) + .eqIfPresent(TradeCartItemDO::getSelected, selected)); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java index 3b963540b4..a62746b1b9 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java @@ -4,10 +4,6 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import org.apache.ibatis.annotations.Mapper; -/** - * @author LeeYan9 - * @since 2022-08-26 - */ @Mapper public interface TradeOrderMapper extends BaseMapperX { } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java index 987d5a3f5d..3a85e0cbb1 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java @@ -4,10 +4,6 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import org.apache.ibatis.annotations.Mapper; -/** - * @author LeeYan9 - * @since 2022-08-26 - */ @Mapper public interface TradeOrderItemMapper extends BaseMapperX { } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderConfig.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderConfig.java new file mode 100644 index 0000000000..715169275f --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderConfig.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.trade.framework.order.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +// TODO @LeeYan9: 可以直接给 TradeOrderProperties 一个 @Component生效哈 +/** + * @author LeeYan9 + * @since 2022-09-15 + */ +@Configuration +@EnableConfigurationProperties(TradeOrderProperties.class) +public class TradeOrderConfig { +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java new file mode 100644 index 0000000000..4d584f4c91 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.trade.framework.order.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import java.time.Duration; + +/** + * 交易订单的配置项 + * + * @author LeeYan9 + * @since 2022-09-15 + */ +@ConfigurationProperties(prefix = "yudao.trade.order") +@Data +@Validated +public class TradeOrderProperties { + + /** + * 应用编号 + */ + @NotNull(message = "应用编号不能为空") + private Long appId; + + /** + * 支付超时时间 + */ + @NotNull(message = "支付超时时间不能为空") + private Duration expireTime; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java new file mode 100644 index 0000000000..3f46f5102a --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.trade.service.cart; + +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO; + +import javax.validation.Valid; +import java.util.Collection; + +/** + * 购物车 Service 接口 + * + * @author 芋道源码 + */ +public interface TradeCartService { + + /** + * 添加商品到购物车 + * + * @param userId 用户编号 + * @param addCountReqVO 添加信息 + */ + void addCartItemCount(Long userId, @Valid AppTradeCartItemAddCountReqVO addCountReqVO); + + /** + * 更新购物车商品数量 + * + * @param userId 用户编号 + * @param updateCountReqVO 更新信息 + */ + void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO); + + /** + * 更新购物车商品是否选中 + * + * @param userId 用户编号 + * @param updateSelectedReqVO 更新信息 + */ + void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO); + + /** + * 删除购物车商品 + * + * @param userId 用户编号 + * @param skuIds SKU 编号的数组 + */ + void deleteCartItems(Long userId, Collection skuIds); + + /** + * 查询用户在购物车中的商品数量 + * + * @param userId 用户编号 + * @return 商品数量 + */ + Integer getCartCount(Long userId); + + /** + * 查询用户的购物车详情 + * + * @param userId 用户编号 + * @return 购物车详情 + */ + AppTradeCartDetailRespVO getCartDetail(Long userId); + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java new file mode 100644 index 0000000000..ae0301e832 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java @@ -0,0 +1,184 @@ +package cn.iocoder.yudao.module.trade.service.cart; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.promotion.api.price.PriceApi; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO; +import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert; +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO; +import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND; + +/** + * 购物车 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class TradeCartServiceImpl implements TradeCartService { + + @Resource + private TradeCartItemMapper cartItemMapper; + + @Resource + private ProductSkuApi productSkuApi; + @Resource + private PriceApi priceApi; + + @Override + public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) { + Long skuId = addCountReqVO.getSkuId(); + Integer count = addCountReqVO.getCount(); + // 查询 CartItemDO + TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId()); + + // 存在,则进行数量更新 + if (tradeItem != null) { + checkProductSku(skuId, tradeItem.getCount() + count); + cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()) + .setSelected(true).setCount(tradeItem.getCount() + count)); + return; + } + + // 不存在,则进行插入 + ProductSkuRespDTO sku = checkProductSku(skuId, count); + cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId()) + .setSelected(true).setCount(count)); + } + + @Override + public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) { + // 校验 TradeCartItemDO 存在 + TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId()); + if (tradeItem == null) { + throw exception(CARD_ITEM_NOT_FOUND); + } + // 校验商品 SKU + checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount()); + + // 更新数量 + cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount())); + } + + @Override + public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) { + // 查询 CartItemDO 列表 + List cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds()); + if (CollUtil.isEmpty(cartItems)) { + return; + } + + // 更新选中 + cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId), + new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected())); + } + + /** + * 购物车删除商品 + * + * @param userId 用户编号 + * @param skuIds 商品 SKU 编号的数组 + */ + @Override + public void deleteCartItems(Long userId, Collection skuIds) { + // 查询 CartItemDO 列表 + List cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds); + if (CollUtil.isEmpty(cartItems)) { + return; + } + + // 批量标记删除 + cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId)); + } + + @Override + public Integer getCartCount(Long userId) { + return cartItemMapper.selectSumByUserId(userId); + } + + @Override + public AppTradeCartDetailRespVO getCartDetail(Long userId) { + // 获得购物车的商品 + List cartItems = cartItemMapper.selectListByUserId(userId, null); + // 如果未空,则返回空结果 + if (CollUtil.isEmpty(cartItems)) { + return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO(); + } + + // 调用价格服务,计算价格 + PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems)); + + // 转换返回 + Map cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId); + Map orderItemMap = convertMap(priceCalculate.getOrder().getItems(), + PriceCalculateRespDTO.OrderItem::getSkuId); + List itemGroups = new ArrayList<>(cartItems.size()); + // ① 场景一,营销活动,订单级别 TODO 芋艿:待测试 + priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel())) + .forEach(promotion -> { + AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>()) + .setPromotion(TradeCartConvert.INSTANCE.convert(promotion)); + itemGroups.add(itemGroup); + promotion.getItems().forEach(promotionItem -> { + PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId()); + Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId()); + TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId()); + itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu + }); + }); + // ② 场景二,营销活动,商品级别 + orderItemMap.values().forEach(orderItem -> { + AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null); + itemGroups.add(itemGroup); + TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId()); + itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu + }); + return new AppTradeCartDetailRespVO().setItemGroups(itemGroups) + .setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder())); + } + + /** + * 校验商品 SKU 是否合法 + * 1. 是否存在 + * 2. 是否下架 + * 3. 库存不足 + * + * @param skuId 商品 SKU 编号 + * @param count 商品数量 + * @return 商品 SKU + */ + private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) { + ProductSkuRespDTO sku = productSkuApi.getSku(skuId); + if (sku == null || CommonStatusEnum.DISABLE.getStatus().equals(sku.getStatus())) { + throw exception(SKU_NOT_EXISTS); + } + if (count > sku.getStock()) { + throw exception(SKU_STOCK_NOT_ENOUGH); + } + return sku; + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java index f3fc4b5e13..f9e635fac2 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.order; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; /** + * 交易订单 Service 接口 + * * @author LeeYan9 * @since 2022-08-26 */ @@ -10,10 +12,12 @@ public interface TradeOrderService { /** * 创建交易订单 + * * @param loginUserId 登录用户 - * @param clientIp 用户ip地址 + * @param userIp 用户 IP 地址 * @param createReqVO 创建交易订单请求模型 - * @return 交易订单创建结果 + * @return 交易订单的编号 */ - Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO); + Long createTradeOrder(Long loginUserId, String userIp, AppTradeOrderCreateReqVO createReqVO); + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index 3554e7d4ca..95df76afe6 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -1,138 +1,231 @@ package cn.iocoder.yudao.module.trade.service.order; -import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.market.api.price.PriceApi; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; -import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.member.api.address.AddressApi; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; -import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; -import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.PriceApi; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item; import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; -import cn.iocoder.yudao.module.trade.convert.order.TradeOrderItemConvert; -import cn.iocoder.yudao.module.trade.convert.pay.PayOrderConvert; -import cn.iocoder.yudao.module.trade.convert.price.PriceConvert; -import cn.iocoder.yudao.module.trade.convert.sku.ProductSkuConvert; 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.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper; -import cn.iocoder.yudao.module.trade.enums.enums.ErrorCodeConstants; -import lombok.RequiredArgsConstructor; +import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Objects; +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.*; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_SKU_NOT_SALE; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_SPU_NOT_FOUND; /** + * 交易订单 Service 实现类 + * * @author LeeYan9 * @since 2022-08-26 */ @Service -@RequiredArgsConstructor public class TradeOrderServiceImpl implements TradeOrderService { - private final TradeOrderMapper tradeOrderMapper; + @Resource + private TradeOrderMapper tradeOrderMapper; + @Resource + private TradeOrderItemMapper tradeOrderItemMapper; - private final TradeOrderItemMapper tradeOrderItemMapper; - - private final PriceApi priceApi; - - private final ProductSkuApi productSkuApi; - - private final ProductSpuApi productSpuApi; - - private final PayOrderApi payOrderApi; + @Resource + private PriceApi priceApi; + @Resource + private ProductSkuApi productSkuApi; + @Resource + private ProductSpuApi productSpuApi; + @Resource + private PayOrderApi payOrderApi; + @Resource + private AddressApi addressApi; + @Resource + private CouponApi couponApi; + @Resource + private TradeOrderProperties tradeOrderProperties; @Override @Transactional(rollbackFor = Exception.class) - public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) { - - List items = createReqVO.getItems(); - // 商品SKU检查 sku可售状态,库存 - List skuInfos = productSkuApi.getSkusByIds(CollectionUtils.convertSet(items, Item::getSkuId)); - Map skuInfoMap = CollectionUtils.convertMap(skuInfos, SkuInfoRespDTO::getId); - checkSaleableAndStockFromSpu(skuInfoMap, items); - - // 商品SPU检查 sku可售状态,库存 - List spuInfos = productSpuApi.getSpusByIds(CollectionUtils.convertSet(skuInfos, SkuInfoRespDTO::getSpuId)); - checkSaleableFromSpu(spuInfos); + public Long createTradeOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) { + // 商品 SKU 检查:可售状态、库存 + List skus = validateSkuSaleable(createReqVO.getItems()); + // 商品 SPU 检查:可售状态 + List spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId)); + // 用户收件地址的校验 + AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId()); // 价格计算 - PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId); - PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO); + PriceCalculateRespDTO priceResp = priceApi.calculatePrice(TradeOrderConvert.INSTANCE.convert(createReqVO, userId)); - // 订单信息记录 - TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder()); + // 插入 TradeOrderDO 订单 + TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address); + // 插入 TradeOrderItemDO 订单项 + List tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); + + // 订单创建完后的逻辑 + afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus); + // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! + return tradeOrderDO.getId(); + } + + /** + * 校验商品 SKU 是否可出售 + * + * @param items 商品 SKU + * @return 商品 SKU 数组 + */ + private List validateSkuSaleable(List items) { + List skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId)); + // SKU 不存在 + if (items.size() != skus.size()) { + throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_NOT_FOUND); + } + // 校验是否禁用 or 库存不足 + Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); + items.forEach(item -> { + ProductSkuRespDTO sku = skuMap.get(item.getSkuId()); + // SKU 禁用 + if (ObjectUtil.notEqual(CommonStatusEnum.ENABLE.getStatus(), sku.getStatus())) { + throw exception(ORDER_CREATE_SKU_NOT_SALE); + } + // SKU 库存不足 + if (item.getCount() > sku.getStock()) { + throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH); + } + }); + return skus; + } + + /** + * 校验商品 SPU 是否可出售 + * + * @param spuIds 商品 SPU 编号数组 + * @return 商品 SPU 数组 + */ + private List validateSpuSaleable(Set spuIds) { + List spus = productSpuApi.getSpuList(spuIds); + // SPU 不存在 + if (spus.size() != spuIds.size()) { + throw exception(ORDER_CREATE_SPU_NOT_FOUND); + } + // 校验是否存在禁用的 SPU + ProductSpuRespDTO spu = CollectionUtils.findFirst(spus, + spuDTO -> ObjectUtil.notEqual(ProductSpuStatusEnum.ENABLE.getStatus(), spuDTO.getStatus())); + if (spu != null) { + throw exception(ErrorCodeConstants.ORDER_CREATE_SPU_NOT_SALE); + } + return spus; + } + + /** + * 校验收件地址是否存在 + * + * @param userId 用户编号 + * @param addressId 收件地址编号 + * @return 收件地址 + */ + private AddressRespDTO validateAddress(Long userId, Long addressId) { + AddressRespDTO address = addressApi.getAddress(userId, addressId); + if (Objects.isNull(address)) { + throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND); + } + return address; + } + + private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, + PriceCalculateRespDTO.Order order, AddressRespDTO address) { + TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, order, address); + tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的; + tradeOrderDO.setStatus(TradeOrderStatusEnum.WAITING_PAYMENT.getStatus()); + tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType()); + tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); + tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum)); + tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源? + tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息 + tradeOrderDO.setDeliveryStatus(false); // 物流信息 + tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息 tradeOrderMapper.insert(tradeOrderDO); + return tradeOrderDO; + } - // 订单项信息记录 - List tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getItems()); - //-填充订单项-SKU信息 - fillItemsInfoFromSku(tradeOrderItems, skuInfoMap); - tradeOrderItemMapper.insertBatch(tradeOrderItems); + private List createTradeOrderItems(TradeOrderDO tradeOrderDO, + List orderItems, List skus) { + List tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus); + tradeOrderItemMapper.insertBatch(tradeOrderItemDOs); + return tradeOrderItemDOs; + } + + /** + * 执行创建完创建完订单后的逻辑 + * + * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 + * + * @param userId 用户编号 + * @param createReqVO 创建订单请求 + * @param tradeOrderDO 交易订单 + */ + private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, + TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus) { + // 下单时扣减商品库存 + productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs))); + + // 删除购物车商品 TODO 芋艿:待实现 + + // 扣减积分,抵扣金额 TODO 芋艿:待实现 + + // 有使用优惠券时更新 + if (createReqVO.getCouponId() != null) { + couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId) + .setOrderId(tradeOrderDO.getId())); + } - // 库存扣减 - List skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems); - productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems)); // 生成预支付 + createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus); - PayOrderDataCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); - return payOrderApi.createPayOrder(payOrderCreateReqDTO); + // 增加订单日志 TODO 芋艿:待实现 } - private void fillItemsInfoFromSku(List tradeOrderItems, - Map spuInfos) { - for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { - // 填充SKU信息 - SkuInfoRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId()); - tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId()); - tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl()); - tradeOrderItem.setName(skuInfoRespDTO.getName()); - // todo - List property = - BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class); - tradeOrderItem.setProperties(property); - } + private void createPayOrder(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus) { + // 创建支付单,用于后续的支付 + PayOrderInfoCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( + tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties); + Long payOrderId = payOrderApi.createPayOrder(payOrderCreateReqDTO); + + // 更新到交易单上 + tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId)); } - private void checkSaleableFromSpu(List spuInfos) { - SpuInfoRespDTO spu = CollectionUtils.findFirst(spuInfos, - spuInfoDTO -> !Objects.equals(ProductSpuStatusEnum.ENABLE.getStyle(), spuInfoDTO.getStatus())); - if (Objects.isNull(spu)) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SPU_NOT_SALE); - } - } - - private void checkSaleableAndStockFromSpu(Map skuInfoMap, - List items) { - // sku 不存在 - if (items.size() != skuInfoMap.size()) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND); - } - for (Item item : items) { - SkuInfoRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId()); - // sku禁用 - if (!Objects.equals(CommonStatusEnum.ENABLE.getStatus(), skuInfoDTO.getStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_SALE); - } - // sku库存不足 - if (item.getCount() > skuInfoDTO.getStock()) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_STOCK_NOT_ENOUGH); - } - } - - } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java new file mode 100644 index 0000000000..669680c3de --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java @@ -0,0 +1,260 @@ +package cn.iocoder.yudao.module.trade.service.order; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.member.api.address.AddressApi; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; +import cn.iocoder.yudao.module.promotion.api.price.PriceApi; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +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.mysql.order.TradeOrderMapper; +import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * {@link TradeOrderServiceImpl} 的单元测试类 + * + * @author LeeYan9 + * @since 2022-09-07 + */ +@Import({TradeOrderServiceImpl.class, TradeOrderConfig.class}) +class TradeOrderServiceTest extends BaseDbUnitTest { + + @Resource + private TradeOrderServiceImpl tradeOrderService; + + @Resource + private TradeOrderMapper tradeOrderMapper; + @Resource + private TradeOrderItemMapper tradeOrderItemMapper; + + @MockBean + private ProductSpuApi productSpuApi; + @MockBean + private ProductSkuApi productSkuApi; + @MockBean + private PriceApi priceApi; + @MockBean + private PayOrderApi payOrderApi; + @MockBean + private AddressApi addressApi; + @MockBean + private CouponApi couponApi; + + @MockBean + private TradeOrderProperties tradeOrderProperties; + + @BeforeEach + public void setUp() { + when(tradeOrderProperties.getAppId()).thenReturn(888L); + when(tradeOrderProperties.getExpireTime()).thenReturn(Duration.ofDays(1)); + } + + @Test + public void testCreateTradeOrder_success() { + // 准备参数 + Long userId = 100L; + String userIp = "127.0.0.1"; + AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO() + .setAddressId(10L).setCouponId(101L).setRemark("我是备注").setFromCart(true) + .setItems(Arrays.asList(new AppTradeOrderCreateReqVO.Item().setSkuId(1L).setCount(3), + new AppTradeOrderCreateReqVO.Item().setSkuId(2L).setCount(4))); + // mock 方法(商品 SKU 检查) + ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L) + .setPrice(50).setStock(100).setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(111L).setValueId(222L)))); + ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L) + .setPrice(20).setStock(50).setStatus(CommonStatusEnum.ENABLE.getStatus())) + .setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(333L).setValueId(444L))); + when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02)); + // mock 方法(商品 SPU 检查) + ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L) + .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()).setName("商品 1")); + ProductSpuRespDTO spu02 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(21L) + .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())); + when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02)); + // mock 方法(用户收件地址的校验) + AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿") + .setMobile("15601691300").setAreaId(3306L).setPostCode("85757").setDetailAddress("土豆村"); + when(addressApi.getAddress(eq(userId), eq(10L))).thenReturn(addressRespDTO); + // mock 方法(价格计算) + PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem() + .setSpuId(11L).setSkuId(1L).setCount(3).setOriginalPrice(150).setOriginalUnitPrice(50) + .setDiscountPrice(20).setPayPrice(130).setOrderPartPrice(7).setOrderDividePrice(35); + PriceCalculateRespDTO.OrderItem priceOrderItem02 = new PriceCalculateRespDTO.OrderItem() + .setSpuId(21L).setSkuId(2L).setCount(4).setOriginalPrice(80).setOriginalUnitPrice(20) + .setDiscountPrice(40).setPayPrice(40).setOrderPartPrice(15).setOrderDividePrice(25); + PriceCalculateRespDTO.Order priceOrder = new PriceCalculateRespDTO.Order() + .setOriginalPrice(230).setOrderPrice(100).setDiscountPrice(0).setCouponPrice(30) + .setPointPrice(10).setDeliveryPrice(20).setPayPrice(80).setCouponId(101L).setCouponPrice(30) + .setItems(Arrays.asList(priceOrderItem01, priceOrderItem02)); + when(priceApi.calculatePrice(argThat(priceCalculateReqDTO -> { + assertEquals(priceCalculateReqDTO.getUserId(), 100L); + assertEquals(priceCalculateReqDTO.getCouponId(), 101L); + assertEquals(priceCalculateReqDTO.getItems().get(0).getSkuId(), 1L); + assertEquals(priceCalculateReqDTO.getItems().get(0).getCount(), 3); + assertEquals(priceCalculateReqDTO.getItems().get(1).getSkuId(), 2L); + assertEquals(priceCalculateReqDTO.getItems().get(1).getCount(), 4); + return true; + }))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder)); + // mock 方法(创建支付单) + when(payOrderApi.createPayOrder(argThat(createReqDTO -> { + assertEquals(createReqDTO.getAppId(), 888L); + assertEquals(createReqDTO.getUserIp(), userIp); + assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 + assertEquals(createReqDTO.getSubject(), "商品 1 等多件"); + assertNull(createReqDTO.getBody()); + assertEquals(createReqDTO.getAmount(), 80); + assertNotNull(createReqDTO.getExpireTime()); + return true; + }))).thenReturn(1000L); + + // 调用方法 + Long tradeOrderId = tradeOrderService.createTradeOrder(userId, userIp, reqVO); + // 断言 TradeOrderDO 订单 + List tradeOrderDOs = tradeOrderMapper.selectList(); + assertEquals(tradeOrderDOs.size(), 1); + TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0); + assertEquals(tradeOrderDO.getId(), tradeOrderId); + assertNotNull(tradeOrderDO.getNo()); + assertEquals(tradeOrderDO.getType(), TradeOrderTypeEnum.NORMAL.getType()); + assertEquals(tradeOrderDO.getTerminal(), TerminalEnum.H5.getTerminal()); + assertEquals(tradeOrderDO.getUserId(), userId); + assertEquals(tradeOrderDO.getUserIp(), userIp); + assertEquals(tradeOrderDO.getStatus(), TradeOrderStatusEnum.WAITING_PAYMENT.getStatus()); + assertEquals(tradeOrderDO.getProductCount(), 7); + assertNull(tradeOrderDO.getFinishTime()); + assertNull(tradeOrderDO.getCancelTime()); + assertNull(tradeOrderDO.getCancelType()); + assertEquals(tradeOrderDO.getUserRemark(), "我是备注"); + assertNull(tradeOrderDO.getRemark()); + assertFalse(tradeOrderDO.getPayed()); + assertNull(tradeOrderDO.getPayTime()); + assertEquals(tradeOrderDO.getOriginalPrice(), 230); + assertEquals(tradeOrderDO.getOrderPrice(), 100); + assertEquals(tradeOrderDO.getDiscountPrice(), 0); + assertEquals(tradeOrderDO.getAdjustPrice(), 0); + assertEquals(tradeOrderDO.getPayPrice(), 80); + assertEquals(tradeOrderDO.getPayOrderId(), 1000L); + assertNull(tradeOrderDO.getPayChannel()); + assertNull(tradeOrderDO.getDeliveryTemplateId()); + assertNull(tradeOrderDO.getExpressNo()); + assertFalse(tradeOrderDO.getDeliveryStatus()); + assertNull(tradeOrderDO.getDeliveryTime()); + assertNull(tradeOrderDO.getReceiveTime()); + assertEquals(tradeOrderDO.getReceiverName(), "芋艿"); + assertEquals(tradeOrderDO.getReceiverMobile(), "15601691300"); + assertEquals(tradeOrderDO.getReceiverAreaId(), 3306L); + assertEquals(tradeOrderDO.getReceiverPostCode(), 85757); + assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村"); + assertEquals(tradeOrderDO.getRefundStatus(), TradeOrderRefundStatusEnum.NONE.getStatus()); + assertEquals(tradeOrderDO.getRefundPrice(), 0); + assertEquals(tradeOrderDO.getCouponPrice(), 30); + assertEquals(tradeOrderDO.getPointPrice(), 10); + // 断言 TradeOrderItemDO 订单(第 1 个) + List tradeOrderItemDOs = tradeOrderItemMapper.selectList(); + assertEquals(tradeOrderItemDOs.size(), 2); + TradeOrderItemDO tradeOrderItemDO01 = tradeOrderItemDOs.get(0); + assertNotNull(tradeOrderItemDO01.getId()); + assertEquals(tradeOrderItemDO01.getUserId(), userId); + assertEquals(tradeOrderItemDO01.getOrderId(), tradeOrderId); + assertEquals(tradeOrderItemDO01.getSpuId(), 11L); + assertEquals(tradeOrderItemDO01.getSkuId(), 1L); + assertEquals(tradeOrderItemDO01.getProperties().size(), 1); + assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L); + assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L); + assertEquals(tradeOrderItemDO01.getName(), sku01.getName()); + assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl()); + assertEquals(tradeOrderItemDO01.getCount(), 3); + assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150); + assertEquals(tradeOrderItemDO01.getOriginalUnitPrice(), 50); + assertEquals(tradeOrderItemDO01.getDiscountPrice(), 20); + assertEquals(tradeOrderItemDO01.getPayPrice(), 130); + assertEquals(tradeOrderItemDO01.getOrderPartPrice(), 7); + assertEquals(tradeOrderItemDO01.getOrderDividePrice(), 35); + assertEquals(tradeOrderItemDO01.getRefundStatus(), TradeOrderItemRefundStatusEnum.NONE.getStatus()); + assertEquals(tradeOrderItemDO01.getRefundTotal(), 0); + // 断言 TradeOrderItemDO 订单(第 2 个) + TradeOrderItemDO tradeOrderItemDO02 = tradeOrderItemDOs.get(1); + assertNotNull(tradeOrderItemDO02.getId()); + assertEquals(tradeOrderItemDO02.getUserId(), userId); + assertEquals(tradeOrderItemDO02.getOrderId(), tradeOrderId); + assertEquals(tradeOrderItemDO02.getSpuId(), 21L); + assertEquals(tradeOrderItemDO02.getSkuId(), 2L); + assertEquals(tradeOrderItemDO02.getProperties().size(), 1); + assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L); + assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L); + assertEquals(tradeOrderItemDO02.getName(), sku02.getName()); + assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl()); + assertEquals(tradeOrderItemDO02.getCount(), 4); + assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80); + assertEquals(tradeOrderItemDO02.getOriginalUnitPrice(), 20); + assertEquals(tradeOrderItemDO02.getDiscountPrice(), 40); + assertEquals(tradeOrderItemDO02.getPayPrice(), 40); + assertEquals(tradeOrderItemDO02.getOrderPartPrice(), 15); + assertEquals(tradeOrderItemDO02.getOrderDividePrice(), 25); + assertEquals(tradeOrderItemDO02.getRefundStatus(), TradeOrderItemRefundStatusEnum.NONE.getStatus()); + assertEquals(tradeOrderItemDO02.getRefundTotal(), 0); + // 校验调用 + verify(productSkuApi).updateSkuStock(argThat(new ArgumentMatcher() { + + @Override + public boolean matches(ProductSkuUpdateStockReqDTO updateStockReqDTO) { + assertEquals(updateStockReqDTO.getItems().size(), 2); + assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L); + assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3); + assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L); + assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4); + return true; + } + + })); + verify(couponApi).useCoupon(argThat(reqDTO -> { + assertEquals(reqDTO.getId(), reqVO.getCouponId()); + assertEquals(reqDTO.getUserId(), userId); + assertEquals(reqDTO.getOrderId(), tradeOrderId); + return true; + })); +// //mock 支付订单信息 +// when(payOrderApi.createPayOrder(any())).thenReturn(1L); + +// //价格 +// assertEquals(calculateRespDTO.getOrder().getItems().get(0).getPresentPrice(), tradeOrderItemDO.getPresentPrice()); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-unit-test.yaml b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000000..113a0fee5f --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-unit-test.yaml @@ -0,0 +1,53 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +# Resilience4j 配置项 + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao.module + trade: + order: + app-id: 1 + merchant-order-id: 1 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/logback.xml b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/logback.xml new file mode 100644 index 0000000000..daf756bff0 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql new file mode 100644 index 0000000000..cd16db8c60 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql @@ -0,0 +1,2 @@ +DELETE FROM trade_order; +DELETE FROM trade_order_item; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000000..fda7251dcb --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,72 @@ +CREATE TABLE IF NOT EXISTS "trade_order" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar NOT NULL, + "type" int NOT NULL, + "terminal" int NOT NULL, + "user_id" bigint NOT NULL, + "user_ip" varchar NOT NULL, + "user_remark" varchar, + "status" int NOT NULL, + "product_count" int NOT NULL, + "cancel_type" int, + "remark" varchar, + "payed" bit NOT NULL, + "pay_time" datetime, + "finish_time" datetime, + "cancel_time" datetime, + "original_price" int NOT NULL, + "order_price" int NOT NULL, + "discount_price" int NOT NULL, + "delivery_price" int NOT NULL, + "adjust_price" int NOT NULL, + "pay_price" int NOT NULL, + "pay_order_id" int, + "pay_channel" int, + "delivery_template_id" int, + "express_no" int, + "delivery_status" bit NOT NULL, + "delivery_time" datetime, + "receive_time" datetime, + "receiver_name" varchar NOT NULL, + "receiver_mobile" varchar NOT NULL, + "receiver_area_id" int NOT NULL, + "receiver_post_code" int, + "receiver_detail_address" varchar NOT NULL, + "refund_status" int NOT NULL, + "refund_price" int NOT NULL, + "coupon_id" bigint NOT NULL, + "coupon_price" int NOT NULL, + "point_price" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '交易订单表'; + +CREATE TABLE IF NOT EXISTS "trade_order_item" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "order_id" bigint NOT NULL, + "spu_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "properties" varchar, + "name" varchar NOT NULL, + "pic_url" varchar, + "count" int NOT NULL, + "original_price" int NOT NULL, + "original_unit_price" int NOT NULL, + "discount_price" int NOT NULL, + "pay_price" int NOT NULL, + "order_part_price" int NOT NULL, + "order_divide_price" int NOT NULL, + "refund_status" int NOT NULL, + "refund_total" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '交易订单明细表'; diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java new file mode 100644 index 0000000000..75ba025639 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; + +/** + * 用户收件地址 API 接口 + * + * @author 芋道源码 + */ +public interface AddressApi { + + /** + * 获得用户收件地址 + * + * @param id 收件地址编号 + * @param userId 用户编号 + * @return 用户收件地址 + */ + AddressRespDTO getAddress(Long id, Long userId); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java new file mode 100644 index 0000000000..cc8eb4701b --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.member.api.address.dto; + +import lombok.Data; + +/** + * 用户收件地址 Response DTO + * + * @author 芋道源码 + */ +@Data +public class AddressRespDTO { + + /** + * 编号 + */ + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 收件人名称 + */ + private String name; + /** + * 手机号 + */ + private String mobile; + /** + * 地区编号 + */ + private Long areaId; + /** + * 邮编 + */ + private String postCode; + /** + * 收件详细地址 + */ + private String detailAddress; + /** + * 是否默认 + * + * true - 默认收件地址 + */ + private Boolean defaulted; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java index c6c37f9352..235ebb2c4a 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java @@ -2,6 +2,12 @@ package cn.iocoder.yudao.module.member.api.user; import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + /** * 会员用户的 API 接口 * @@ -17,4 +23,30 @@ public interface MemberUserApi { */ UserRespDTO getUser(Long id); + /** + * 获得会员用户信息们 + * + * @param ids 用户编号的数组 + * @return 用户信息们 + */ + List getUsers(Collection ids); + + /** + * 获得会员用户 Map + * + * @param ids 用户编号的数组 + * @return 会员用户 Map + */ + default Map getUserMap(Collection ids) { + return convertMap(getUsers(ids), UserRespDTO::getId); + } + + /** + * 基于用户昵称,模糊匹配用户列表 + * + * @param nickname 用户昵称,模糊匹配 + * @return 用户信息的列表 + */ + List getUserListByNickname(String nickname); + } diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java deleted file mode 100644 index 89802839ba..0000000000 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.member.api.user.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 用户信息 Response DTO - * - * @author 芋道源码 - */ -@Data -public class UserInfoDTO { - - /** - * 用户ID - */ - private Long id; - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - /** - * 帐号状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 手机 - */ - private String mobile; - /** - * 注册 IP - */ - private String registerIp; - /** - * 最后登录IP - */ - private String loginIp; - /** - * 最后登录时间 - */ - private LocalDateTime loginDate; - -} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java new file mode 100644 index 0000000000..fd0f4843db --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.service.address.AddressService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 用户收件地址 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AddressApiImpl implements AddressApi { + + @Resource + private AddressService addressService; + + @Override + public AddressRespDTO getAddress(Long id, Long userId) { + return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java index 35e7b51cec..0c3e616a03 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java @@ -8,6 +8,8 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; /** * 会员用户的 API 实现类 @@ -27,4 +29,14 @@ public class MemberUserApiImpl implements MemberUserApi { return UserConvert.INSTANCE.convert2(user); } + @Override + public List getUsers(Collection ids) { + return UserConvert.INSTANCE.convertList2(userService.getUserList(ids)); + } + + @Override + public List getUserListByNickname(String nickname) { + return UserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname)); + } + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java deleted file mode 100644 index 795c8162c8..0000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.member.controller.admin.user; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO; -import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; -import cn.iocoder.yudao.module.member.convert.user.UserConvert; -import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; -import cn.iocoder.yudao.module.member.service.user.MemberUserService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -/** - * @author Banging - */ -@Slf4j -@Api("用户管理") -@RestController(value = "memberUserController") -@RequestMapping("/user") -public class UserController { - - @Resource - private MemberUserService userService; - - @ApiOperation(value = "用户信息获取",notes = "用户基本信息的获取") - @GetMapping("/{tel}") - public CommonResult getUserInfo(@PathVariable String tel){ - MemberUserDO user = userService.getUserByMobile(tel); - return CommonResult.success(UserConvert.INSTANCE.convertInfo(user)); - } -} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java index 32e684c849..0a79b08861 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java @@ -17,4 +17,7 @@ public class AppUserInfoRespVO { @ApiModelProperty(value = "用户头像", required = true, example = "/infra/file/get/35a12e57-4297-4faa-bf7d-7ed2f211c952") private String avatar; + + @ApiModelProperty(value = "用户手机号", required = true, example = "15601691300") + private String mobile; } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java index fdc01e5a81..49a01805d8 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java @@ -1,13 +1,15 @@ package cn.iocoder.yudao.module.member.convert.address; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; - +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.member.controller.app.address.vo.*; -import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; + +import java.util.List; /** * 用户收件地址 Convert @@ -29,4 +31,6 @@ public interface AddressConvert { PageResult convertPage(PageResult page); + AddressRespDTO convert02(AddressDO bean); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java index 6be0d6b41f..c2128c1042 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java @@ -1,12 +1,13 @@ package cn.iocoder.yudao.module.member.convert.user; -import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO; import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +import java.util.List; + @Mapper public interface UserConvert { @@ -15,5 +16,7 @@ public interface UserConvert { AppUserInfoRespVO convert(MemberUserDO bean); UserRespDTO convert2(MemberUserDO bean); - UserInfoDTO convertInfo(MemberUserDO bean); + + List convertList2(List list); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java index 9ba09a111c..5e63aae360 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.member.dal.mysql.user; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * 会员 User Mapper * @@ -16,4 +19,9 @@ public interface MemberUserMapper extends BaseMapperX { return selectOne(MemberUserDO::getMobile, mobile); } + default List selectListByNicknameLike(String nickname) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(MemberUserDO::getNickname, nickname)); + } + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java index 3b63c8d717..da3b4fd4ae 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java @@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobile import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import java.io.InputStream; +import java.util.Collection; +import java.util.List; /** * 会员用户 Service 接口 @@ -21,6 +23,15 @@ public interface MemberUserService { */ MemberUserDO getUserByMobile(String mobile); + /** + * 基于用户昵称,模糊匹配用户列表 + * + * @param nickname 用户昵称,模糊匹配 + * @return 用户信息的列表 + */ + List getUserListByNickname(String nickname); + + /** * 基于手机号创建用户。 * 如果用户已经存在,则直接进行返回 @@ -47,6 +58,14 @@ public interface MemberUserService { */ MemberUserDO getUser(Long id); + /** + * 通过用户 ID 查询用户们 + * + * @param ids 用户 ID + * @return 用户对象信息数组 + */ + List getUserList(Collection ids); + /** * 修改用户昵称 * @param userId 用户id diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java index c6e6cf9e87..2657e922e8 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java @@ -19,6 +19,9 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.validation.Valid; import java.io.InputStream; +import java.util.Collection; +import java.util.Date; +import java.util.List; import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -51,6 +54,11 @@ public class MemberUserServiceImpl implements MemberUserService { return memberUserMapper.selectByMobile(mobile); } + @Override + public List getUserListByNickname(String nickname) { + return memberUserMapper.selectListByNicknameLike(nickname); + } + @Override public MemberUserDO createUserIfAbsent(String mobile, String registerIp) { // 用户已经存在 @@ -86,6 +94,11 @@ public class MemberUserServiceImpl implements MemberUserService { return memberUserMapper.selectById(id); } + @Override + public List getUserList(Collection ids) { + return memberUserMapper.selectBatchIds(ids); + } + @Override public void updateUserNickname(Long userId, String nickname) { MemberUserDO user = this.checkUserExists(userId); diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java index 204ce7a32f..36022b172b 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java @@ -3,18 +3,19 @@ package cn.iocoder.yudao.module.pay.api.order; import javax.validation.Valid; /** + * 支付单 API 接口 + * * @author LeeYan9 * @since 2022-08-26 */ public interface PayOrderApi { - /** * 创建支付单 * * @param reqDTO 创建请求 * @return 支付单编号 */ - Long createPayOrder(@Valid PayOrderDataCreateReqDTO reqDTO); + Long createPayOrder(@Valid PayOrderInfoCreateReqDTO reqDTO); } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java similarity index 84% rename from yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java rename to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java index ca225f9589..b5f64478d8 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.pay.api.order; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; @@ -11,10 +11,11 @@ import java.time.LocalDateTime; /** * 支付单创建 Request DTO + * * @author LeeYan9 */ @Data -public class PayOrderDataCreateReqDTO implements Serializable { +public class PayOrderInfoCreateReqDTO implements Serializable { /** * 应用编号 @@ -43,7 +44,7 @@ public class PayOrderDataCreateReqDTO implements Serializable { /** * 商品描述 */ - @NotEmpty(message = "商品描述信息不能为空") +// @NotEmpty(message = "商品描述信息不能为空") // 允许空 @Length(max = 128, message = "商品描述信息长度不能超过128") private String body; @@ -53,7 +54,7 @@ public class PayOrderDataCreateReqDTO implements Serializable { * 支付金额,单位:分 */ @NotNull(message = "支付金额不能为空") - @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + @Min(value = 1, message = "支付金额必须大于零") private Integer amount; /** @@ -62,4 +63,4 @@ public class PayOrderDataCreateReqDTO implements Serializable { @NotNull(message = "支付过期时间不能为空") private LocalDateTime expireTime; -} \ No newline at end of file +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java index ff902be92f..d5dd64146a 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.pay.api; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; -import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; import org.springframework.stereotype.Service; /** @@ -11,7 +11,7 @@ import org.springframework.stereotype.Service; public class PayOrderApiImpl implements PayOrderApi { @Override - public Long createPayOrder(PayOrderDataCreateReqDTO reqDTO) { + public Long createPayOrder(PayOrderInfoCreateReqDTO reqDTO) { return null; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java new file mode 100644 index 0000000000..6f2556d357 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java @@ -0,0 +1,19 @@ +//package cn.iocoder.yudao.module.pay.api.order; +// +//import org.springframework.stereotype.Service; +//import org.springframework.transaction.annotation.Transactional; +// +///** +// * @author LeeYan9 +// * @since 2022-09-06 +// */ +//@Service +//public class PayOrderApiImpl implements PayOrderApi { +// +// @Override +// @Transactional(rollbackFor = Exception.class) +// public Long createPayOrder(PayOrderInfoCreateReqDTO reqDTO) { +// return null; +// } +// +//} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java index 708586b823..790accd92b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -16,7 +18,7 @@ public class RoleUpdateStatusReqVO { @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 CommonStatusEnum 枚举") @NotNull(message = "状态不能为空") -// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") private Integer status; } diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 7dd658a2bf..061e0d9bdd 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -36,33 +36,26 @@ yudao-module-infra-biz ${revision} - cn.iocoder.boot yudao-module-pay-biz ${revision} - - - - - - - - - - - - - - - - - - - - - + + cn.iocoder.boot + yudao-module-promotion-biz + ${revision} + + + cn.iocoder.boot + yudao-module-product-biz + ${revision} + + + cn.iocoder.boot + yudao-module-trade-biz + ${revision} + diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index c49ae9d9a4..87f7810918 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -27,6 +27,7 @@ spring: write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 fail-on-empty-beans: false # 允许序列化无属性的 Bean + # Cache 配置项 cache: type: REDIS @@ -163,6 +164,10 @@ yudao: send-maximum-quantity-per-day: 10 begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 end-code: 9999 # 这里配置 9999 的原因是,测试方便。 + trade: + order: + app-id: 1 # 商户编号 + expire-time: 2h # 支付的过期时间 debug: false diff --git a/yudao-ui-admin/src/api/mall/CouponTemplete/CouponTemplete.js b/yudao-ui-admin/src/api/mall/CouponTemplete/CouponTemplete.js deleted file mode 100644 index 639a75a1da..0000000000 --- a/yudao-ui-admin/src/api/mall/CouponTemplete/CouponTemplete.js +++ /dev/null @@ -1,54 +0,0 @@ -import request from '@/utils/request' - -// 创建优惠券模板 -export function create(data) { - return request({ - url: '/coupon/template/create', - method: 'post', - data: data - }) -} - -// 更新优惠券模板 -export function update(data) { - return request({ - url: '/coupon/template/update', - method: 'put', - data: data - }) -} - -// 删除优惠券模板 -export function deleteCouponTemplete (id) { - return request({ - url: '/coupon/template/delete?id=' + id, - method: 'delete' - }) -} - -// 获得优惠券模板 -export function get(id) { - return request({ - url: '/coupon/template/get?id=' + id, - method: 'get' - }) -} - -// 获得优惠券模板分页 -export function getPage(query) { - return request({ - url: '/coupon/template/page', - method: 'get', - params: query - }) -} - -// 导出优惠券模板 Excel -export function exportExcel(query) { - return request({ - url: '/coupon/template/export-excel', - method: 'get', - params: query, - responseType: 'blob' - }) -} diff --git a/yudao-ui-admin/src/api/mall/product/property.js b/yudao-ui-admin/src/api/mall/product/property.js index 58ae320f6a..5046b92338 100644 --- a/yudao-ui-admin/src/api/mall/product/property.js +++ b/yudao-ui-admin/src/api/mall/product/property.js @@ -43,6 +43,25 @@ export function getPropertyPage(query) { }) } +// 获得规格名称列表 +export function getPropertyList(query) { + return request({ + url: '/product/property/list', + method: 'get', + params: query + }) +} + +// 获得规格名称列表 +export function getPropertyListAndValue(query) { + return request({ + url: '/product/property/listAndValue', + method: 'get', + params: query + }) +} + + // 导出规格名称 Excel export function exportPropertyExcel(query) { return request({ @@ -52,3 +71,49 @@ export function exportPropertyExcel(query) { responseType: 'blob' }) } + +// ------------------------ 规格名称值 ------------------- + +// 获得规格名称值分页 +export function getPropertyValuePage(query) { + return request({ + url: '/product/property/value/page', + method: 'get', + params: query + }) +} + +// 获得规格名称值 +export function getPropertyValue(id) { + return request({ + url: '/product/property/value/get?id=' + id, + method: 'get' + }) +} + + +// 创建规格名称值 +export function createPropertyValue(data) { + return request({ + url: '/product/property/value/create', + method: 'post', + data: data + }) +} + +// 更新规格名称值 +export function updatePropertyValue(data) { + return request({ + url: '/product/property/value/update', + method: 'put', + data: data + }) +} + +// 删除规格名称 +export function deletePropertyValue(id) { + return request({ + url: '/product/property/value/delete?id=' + id, + method: 'delete' + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/sku.js b/yudao-ui-admin/src/api/mall/product/sku.js new file mode 100644 index 0000000000..72b401074a --- /dev/null +++ b/yudao-ui-admin/src/api/mall/product/sku.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获得商品 SKU 选项的列表 +export function getSkuOptionList() { + return request({ + url: '/product/sku/get-option-list', + method: 'get', + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/spu.js b/yudao-ui-admin/src/api/mall/product/spu.js index fc4a545f27..7cce82f3b4 100644 --- a/yudao-ui-admin/src/api/mall/product/spu.js +++ b/yudao-ui-admin/src/api/mall/product/spu.js @@ -1,6 +1,6 @@ import request from '@/utils/request' -// 创建商品spu +// 创建商品 SPU export function createSpu(data) { return request({ url: '/product/spu/create', @@ -9,7 +9,7 @@ export function createSpu(data) { }) } -// 更新商品spu +// 更新商品 SPU export function updateSpu(data) { return request({ url: '/product/spu/update', @@ -18,7 +18,7 @@ export function updateSpu(data) { }) } -// 删除商品spu +// 删除商品 SPU export function deleteSpu(id) { return request({ url: '/product/spu/delete?id=' + id, @@ -34,7 +34,15 @@ export function getSpu(id) { }) } -// 获得商品spu分页 +// 获得商品 SPU 详情 +export function getSpuDetail(id) { + return request({ + url: '/product/spu/get/detail?id=' + id, + method: 'get' + }) +} + +// 获得商品 SPU 分页 export function getSpuPage(query) { return request({ url: '/product/spu/page', @@ -42,3 +50,11 @@ export function getSpuPage(query) { params: query }) } + +// 获得商品 SPU 精简列表 +export function getSpuSimpleList() { + return request({ + url: '/product/spu/get-simple-list', + method: 'get', + }) +} diff --git a/yudao-ui-admin/src/api/mall/promotion/coupon.js b/yudao-ui-admin/src/api/mall/promotion/coupon.js new file mode 100755 index 0000000000..d5a113b817 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/promotion/coupon.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 删除优惠劵 +export function deleteCoupon(id) { + return request({ + url: '/promotion/coupon/delete?id=' + id, + method: 'delete' + }) +} + +// 获得优惠劵分页 +export function getCouponPage(query) { + return request({ + url: '/promotion/coupon/page', + method: 'get', + params: query + }) +} diff --git a/yudao-ui-admin/src/api/mall/promotion/couponTemplate.js b/yudao-ui-admin/src/api/mall/promotion/couponTemplate.js new file mode 100755 index 0000000000..29f81a19cf --- /dev/null +++ b/yudao-ui-admin/src/api/mall/promotion/couponTemplate.js @@ -0,0 +1,67 @@ +import request from '@/utils/request' + +// 创建优惠劵模板 +export function createCouponTemplate(data) { + return request({ + url: '/promotion/coupon-template/create', + method: 'post', + data: data + }) +} + +// 更新优惠劵模板 +export function updateCouponTemplate(data) { + return request({ + url: '/promotion/coupon-template/update', + method: 'put', + data: data + }) +} + +// 更新优惠劵模板的状态 +export function updateCouponTemplateStatus(id, status) { + const data = { + id, + status + } + return request({ + url: '/promotion/coupon-template/update-status', + method: 'put', + data: data + }) +} + +// 删除优惠劵模板 +export function deleteCouponTemplate(id) { + return request({ + url: '/promotion/coupon-template/delete?id=' + id, + method: 'delete' + }) +} + +// 获得优惠劵模板 +export function getCouponTemplate(id) { + return request({ + url: '/promotion/coupon-template/get?id=' + id, + method: 'get' + }) +} + +// 获得优惠劵模板分页 +export function getCouponTemplatePage(query) { + return request({ + url: '/promotion/coupon-template/page', + method: 'get', + params: query + }) +} + +// 导出优惠劵模板 Excel +export function exportCouponTemplateExcel(query) { + return request({ + url: '/promotion/coupon-template/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/api/mall/promotion/discountActivity.js b/yudao-ui-admin/src/api/mall/promotion/discountActivity.js new file mode 100755 index 0000000000..fd32597e3d --- /dev/null +++ b/yudao-ui-admin/src/api/mall/promotion/discountActivity.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 创建限时折扣活动 +export function createDiscountActivity(data) { + return request({ + url: '/promotion/discount-activity/create', + method: 'post', + data: data + }) +} + +// 更新限时折扣活动 +export function updateDiscountActivity(data) { + return request({ + url: '/promotion/discount-activity/update', + method: 'put', + data: data + }) +} + +// 关闭限时折扣活动 +export function closeDiscountActivity(id) { + return request({ + url: '/promotion/discount-activity/close?id=' + id, + method: 'put' + }) +} + +// 删除限时折扣活动 +export function deleteDiscountActivity(id) { + return request({ + url: '/promotion/discount-activity/delete?id=' + id, + method: 'delete' + }) +} + +// 获得限时折扣活动 +export function getDiscountActivity(id) { + return request({ + url: '/promotion/discount-activity/get?id=' + id, + method: 'get' + }) +} + +// 获得限时折扣活动分页 +export function getDiscountActivityPage(query) { + return request({ + url: '/promotion/discount-activity/page', + method: 'get', + params: query + }) +} diff --git a/yudao-ui-admin/src/api/mall/promotion/rewardActivity.js b/yudao-ui-admin/src/api/mall/promotion/rewardActivity.js new file mode 100755 index 0000000000..b792f903b7 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/promotion/rewardActivity.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 创建满减送活动 +export function createRewardActivity(data) { + return request({ + url: '/promotion/reward-activity/create', + method: 'post', + data: data + }) +} + +// 更新满减送活动 +export function updateRewardActivity(data) { + return request({ + url: '/promotion/reward-activity/update', + method: 'put', + data: data + }) +} + +// 关闭满减送活动 +export function closeRewardActivity(id) { + return request({ + url: '/promotion/reward-activity/close?id=' + id, + method: 'put' + }) +} + +// 删除满减送活动 +export function deleteRewardActivity(id) { + return request({ + url: '/promotion/reward-activity/delete?id=' + id, + method: 'delete' + }) +} + +// 获得满减送活动 +export function getRewardActivity(id) { + return request({ + url: '/promotion/reward-activity/get?id=' + id, + method: 'get' + }) +} + +// 获得满减送活动分页 +export function getRewardActivityPage(query) { + return request({ + url: '/promotion/reward-activity/page', + method: 'get', + params: query + }) +} diff --git a/yudao-ui-admin/src/assets/icons/svg/map.svg b/yudao-ui-admin/src/assets/icons/svg/map.svg new file mode 100644 index 0000000000..04c1036edb --- /dev/null +++ b/yudao-ui-admin/src/assets/icons/svg/map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yudao-ui-admin/src/components/VideoUpload/index.vue b/yudao-ui-admin/src/components/VideoUpload/index.vue new file mode 100644 index 0000000000..4809e12378 --- /dev/null +++ b/yudao-ui-admin/src/components/VideoUpload/index.vue @@ -0,0 +1,211 @@ + + + + + + diff --git a/yudao-ui-admin/src/layout/components/IframeToggle/index.vue b/yudao-ui-admin/src/layout/components/IframeToggle/index.vue new file mode 100644 index 0000000000..f688410bbe --- /dev/null +++ b/yudao-ui-admin/src/layout/components/IframeToggle/index.vue @@ -0,0 +1,24 @@ + + + diff --git a/yudao-ui-admin/src/layout/components/InnerLink/index.vue b/yudao-ui-admin/src/layout/components/InnerLink/index.vue new file mode 100644 index 0000000000..f8095ecc00 --- /dev/null +++ b/yudao-ui-admin/src/layout/components/InnerLink/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/yudao-ui-admin/src/router/index.js b/yudao-ui-admin/src/router/index.js index 4257fd5144..309a7a3b81 100644 --- a/yudao-ui-admin/src/router/index.js +++ b/yudao-ui-admin/src/router/index.js @@ -98,6 +98,17 @@ export const constantRoutes = [ meta: {title: '字典数据', icon: '', activeMenu: '/system/dict'} } ] + }, { + path: '/property', + component: Layout, + hidden: true, + children: [{ + path: 'value/:propertyId(\\d+)', + component: (resolve) => require(['@/views/mall/product/property/value'], resolve), + name: 'PropertyValue', + meta: {title: '规格数据', icon: '', activeMenu: '/product/property'} + } + ] }, { path: '/job', component: Layout, @@ -120,6 +131,23 @@ export const constantRoutes = [ meta: {title: '修改生成配置', activeMenu: '/infra/codegen'} } ] + }, { + path: '/spu', + component: Layout, + hidden: true, + children: [{ + path: 'edit/:spuId(\\d+)', + component: (resolve) => require(['@/views/mall/product/spu/save'], resolve), + name: 'SpuEdit', + meta: {title: '修改商品', activeMenu: '/product/spu'} + }, + { + path: 'add', + component: (resolve) => require(['@/views/mall/product/spu/save'], resolve), + name: 'SpuAdd', + meta: {title: '添加商品', activeMenu: '/product/spu'} + } + ] }, { path: '/bpm', component: Layout, @@ -168,6 +196,28 @@ export const constantRoutes = [ meta: {title: '流程详情', activeMenu: '/bpm/task/my'} } ] + }, + { + path: '/order', + component: Layout, + name: '订单管理', + meta: { title: '订单管理' }, + alwaysShow: true, + children: [ + { + path: '/mall/trade/order', + name: '商品订单', + meta: { title: '商品订单' }, + component: (resolve) => require(['@/views/mall/trade/order'], resolve) + }, + { + path: '/mall/trade/order/detail', + name: '订单详情', + hidden: true, + meta: { title: '订单详情' }, + component: (resolve) => require(['@/views/mall/trade/order/detail'], resolve) + } + ] } ] diff --git a/yudao-ui-admin/src/utils/constants.js b/yudao-ui-admin/src/utils/constants.js index 67ecfdd462..3efb0e3c07 100644 --- a/yudao-ui-admin/src/utils/constants.js +++ b/yudao-ui-admin/src/utils/constants.js @@ -185,37 +185,132 @@ export const PayOrderRefundStatusEnum = { * 支付退款订单状态枚举 */ export const PayRefundStatusEnum = { - CREATE:{ - status:0, + CREATE: { + status: 0, name: '退款订单生成' }, - SUCCESS:{ - status:1, + SUCCESS: { + status: 1, name: '退款成功' }, - FAILURE:{ - status:2, + FAILURE: { + status: 2, name: '退款失败' }, - PROCESSING_NOTIFY:{ - status:3, + PROCESSING_NOTIFY: { + status: 3, name: '退款中,渠道通知结果' }, - PROCESSING_QUERY:{ - status:4, + PROCESSING_QUERY: { + status: 4, name: '退款中,系统查询结果' }, - UNKNOWN_RETRY:{ - status:5, + UNKNOWN_RETRY: { + status: 5, name: '状态未知,请重试' }, - UNKNOWN_QUERY:{ - status:6, + UNKNOWN_QUERY: { + status: 6, name: '状态未知,系统查询结果' }, - CLOSE:{ - status:99, + CLOSE: { + status: 99, name: '退款关闭' } } +/** + * 商品 SPU 状态 + */ +export const ProductSpuStatusEnum = { + RECYCLE: { + status: -1, + name: '回收站' + }, + DISABLE: { + status: 0, + name: '下架' + }, + ENABLE: { + status: 1, + name: '上架' + }, +} + +/** + * 优惠类型枚举 + */ +export const PromotionDiscountTypeEnum = { + PRICE: { + type: 1, + name: '满减' + }, + PERCENT: { + type: 2, + name: '折扣' + } +} + +/** + * 优惠劵模板的有限期类型的枚举 + */ +export const CouponTemplateValidityTypeEnum = { + DATE: { + type: 1, + name: '固定日期可用' + }, + TERM: { + type: 2, + name: '领取之后可用' + } +} + +/** + * 营销的商品范围枚举 + */ +export const PromotionProductScopeEnum = { + ALL: { + scope: 1, + name: '全部商品参与' + }, + SPU: { + scope: 2, + name: '指定商品参与' + } +} + +/** + * 营销的条件类型枚举 + */ +export const PromotionConditionTypeEnum = { + PRICE: { + type: 10, + name: '满 N 元' + }, + COUNT: { + type: 20, + name: '满 N 件' + } +} + +/** + * 促销活动的状态枚举 + */ +export const PromotionActivityStatusEnum = { + WAIT: { + type: 10, + name: '未开始' + }, + RUN: { + type: 20, + name: '进行中' + }, + END: { + type: 30, + name: '已结束' + }, + CLOSE: { + type: 40, + name: '已关闭' + } +} diff --git a/yudao-ui-admin/src/utils/dict.js b/yudao-ui-admin/src/utils/dict.js index 1ddde889a7..f669c43166 100644 --- a/yudao-ui-admin/src/utils/dict.js +++ b/yudao-ui-admin/src/utils/dict.js @@ -56,6 +56,18 @@ export const DICT_TYPE = { PAY_ORDER_REFUND_STATUS: 'pay_order_refund_status', // 商户支付订单退款状态 PAY_REFUND_ORDER_STATUS: 'pay_refund_order_status', // 退款订单状态 PAY_REFUND_ORDER_TYPE: 'pay_refund_order_type', // 退款订单类别 + + // ========== MALL - PRODUCT 模块 ========== + PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态 + + // ========== MALL - PROMOTION 模块 ========== + PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型 + PROMOTION_PRODUCT_SCOPE: 'promotion_product_scope', // 营销的商品范围 + PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE: 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型 + PROMOTION_COUPON_STATUS: 'promotion_coupon_status', // 优惠劵的状态 + PROMOTION_COUPON_TAKE_TYPE: 'promotion_coupon_take_type', // 优惠劵的领取方式 + PROMOTION_ACTIVITY_STATUS: 'promotion_activity_status', // 优惠活动的状态 + PROMOTION_CONDITION_TYPE: 'promotion_condition_type', // 营销的条件类型枚举 } /** @@ -114,6 +126,3 @@ export function getDictDataLabel(dictType, value) { const dict = getDictData(dictType, value); return dict ? dict.label : ''; } - -export class getDictDataL { -} diff --git a/yudao-ui-admin/src/views/mall/CouponTemplete/index.vue b/yudao-ui-admin/src/views/mall/CouponTemplete/index.vue deleted file mode 100644 index 73920f22fa..0000000000 --- a/yudao-ui-admin/src/views/mall/CouponTemplete/index.vue +++ /dev/null @@ -1,460 +0,0 @@ - - - diff --git a/yudao-ui-admin/src/views/mall/product/brand/index.vue b/yudao-ui-admin/src/views/mall/product/brand/index.vue index b43fb5f5d9..4ba42d3f2e 100644 --- a/yudao-ui-admin/src/views/mall/product/brand/index.vue +++ b/yudao-ui-admin/src/views/mall/product/brand/index.vue @@ -1,8 +1,7 @@