headers) {
+ Long tenantId = TenantContextHolder.getTenantId();
+ if (tenantId != null) {
+ headers.put(HEADER_TENANT_ID, tenantId.toString());
+ }
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
index a2b11b1dc..f554e0b52 100644
--- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
+++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
@@ -9,10 +9,11 @@ import io.minio.*;
import java.io.ByteArrayInputStream;
import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN;
+import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_TENCENT;
/**
* 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
- *
+ *
* S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库
*
* @author 芋道源码
@@ -78,6 +79,11 @@ public class S3FileClient extends AbstractFileClient {
.replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
.replaceAll("https://", "");
}
+ // 腾讯云必须有 region,否则会报错
+ if (config.getEndpoint().contains(ENDPOINT_TENCENT)) {
+ return StrUtil.subAfter(config.getEndpoint(), ".cos.", false)
+ .replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint
+ }
return null;
}
diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java
index 151159f5e..0c46e8aa6 100644
--- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java
+++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java
@@ -19,6 +19,7 @@ public class S3FileClientConfig implements FileClientConfig {
public static final String ENDPOINT_QINIU = "qiniucs.com";
public static final String ENDPOINT_ALIYUN = "aliyuncs.com";
+ public static final String ENDPOINT_TENCENT = "myqcloud.com";
/**
* 节点地址
diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
index c369d49d6..042a4f736 100644
--- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
@@ -8,8 +8,11 @@ import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor;
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
+import cn.iocoder.yudao.framework.mq.job.RedisPendingMessageResendJob;
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisServerCommands;
@@ -24,7 +27,7 @@ import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
-import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.List;
import java.util.Properties;
@@ -35,6 +38,7 @@ import java.util.Properties;
* @author 芋道源码
*/
@Slf4j
+@EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息
@AutoConfiguration(after = YudaoRedisAutoConfiguration.class)
public class YudaoMQAutoConfiguration {
@@ -69,9 +73,20 @@ public class YudaoMQAutoConfiguration {
return container;
}
+ /**
+ * 创建 Redis Stream 重新消费的任务
+ */
+ @Bean
+ public RedisPendingMessageResendJob redisPendingMessageResendJob(List> listeners,
+ RedisMQTemplate redisTemplate,
+ @Value("${spring.application.name}") String groupName,
+ RedissonClient redissonClient) {
+ return new RedisPendingMessageResendJob(listeners, redisTemplate, groupName, redissonClient);
+ }
+
/**
* 创建 Redis Stream 集群消费的容器
- *
+ *
* Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@@ -99,7 +114,8 @@ public class YudaoMQAutoConfiguration {
// 创建 listener 对应的消费者分组
try {
redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup());
- } catch (Exception ignore) {}
+ } catch (Exception ignore) {
+ }
// 设置 listener 对应的 redisTemplate
listener.setRedisMQTemplate(redisMQTemplate);
// 创建 Consumer 对象
diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java
new file mode 100644
index 000000000..f4ba050c0
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java
@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.framework.mq.job;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
+import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.data.redis.connection.stream.Consumer;
+import org.springframework.data.redis.connection.stream.MapRecord;
+import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
+import org.springframework.data.redis.connection.stream.ReadOffset;
+import org.springframework.data.redis.connection.stream.StreamOffset;
+import org.springframework.data.redis.connection.stream.StreamRecords;
+import org.springframework.data.redis.core.StreamOperations;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 这个任务用于处理,crash 之后的消费者未消费完的消息
+ */
+@Slf4j
+@AllArgsConstructor
+public class RedisPendingMessageResendJob {
+
+ private static final String LOCK_KEY = "redis:pending:msg:lock";
+
+ private final List> listeners;
+ private final RedisMQTemplate redisTemplate;
+ private final String groupName;
+ private final RedissonClient redissonClient;
+
+ /**
+ * 一分钟执行一次,这里选择每分钟的35秒执行,是为了避免整点任务过多的问题
+ */
+ @Scheduled(cron = "35 * * * * ?")
+ public void messageResend() {
+ RLock lock = redissonClient.getLock(LOCK_KEY);
+ // 尝试加锁
+ if (lock.tryLock()) {
+ try {
+ execute();
+ } catch (Exception ex) {
+ log.error("[messageResend][执行异常]", ex);
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ private void execute() {
+ StreamOperations ops = redisTemplate.getRedisTemplate().opsForStream();
+ listeners.forEach(listener -> {
+ PendingMessagesSummary pendingMessagesSummary = ops.pending(listener.getStreamKey(), groupName);
+ // 每个消费者的 pending 队列消息数量
+ Map pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer();
+ pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> {
+ log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount);
+
+ // 从消费者的 pending 队列中读取消息
+ List> records = ops.read(Consumer.from(groupName, consumerName), StreamOffset.create(listener.getStreamKey(), ReadOffset.from("0")));
+ if (CollUtil.isEmpty(records)) {
+ return;
+ }
+ for (MapRecord record : records) {
+ // 重新投递消息
+ redisTemplate.getRedisTemplate().opsForStream().add(StreamRecords.newRecord()
+ .ofObject(record.getValue()) // 设置内容
+ .withStreamKey(listener.getStreamKey()));
+
+ // ack 消息消费完成
+ redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, record);
+ }
+ });
+ });
+ }
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
index fb4af6d69..e220d011d 100644
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
+++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.mybatis.core.mapper;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -75,9 +76,13 @@ public interface BaseMapperX extends BaseMapper {
return selectList(new LambdaQueryWrapper().in(field, values));
}
+ default List selectList(SFunction leField, SFunction geField, Object value) {
+ return selectList(new LambdaQueryWrapper().le(leField, value).ge(geField, value));
+ }
+
/**
* 逐条插入,适合少量数据插入,或者对性能要求不高的场景
- *
+ *
* 如果大量,请使用 {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch(Collection)} 方法
* 使用示例,可见 RoleMenuBatchInsertMapper、UserRoleBatchInsertMapper 类
*
diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java
index c18bd248c..e98f4980f 100644
--- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java
@@ -33,6 +33,10 @@ public class AssertUtils {
public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) {
Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
Arrays.stream(expectedFields).forEach(expectedField -> {
+ // 忽略 jacoco 自动生成的 $jacocoData 属性的情况
+ if (expectedField.isSynthetic()) {
+ return;
+ }
// 如果是忽略的属性,则不进行比对
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {
return;
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/LocalTimeJson.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/LocalTimeJson.java
new file mode 100644
index 000000000..f9ff37511
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/LocalTimeJson.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.jackson.core.databind;
+
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_HOUR_MINUTE_SECOND;
+
+public class LocalTimeJson {
+
+ public static final LocalTimeSerializer SERIALIZER = new LocalTimeSerializer(DateTimeFormatter
+ .ofPattern(FORMAT_HOUR_MINUTE_SECOND)
+ .withZone(ZoneId.systemDefault()));
+
+ public static final LocalTimeDeserializer DESERIALIZABLE = new LocalTimeDeserializer(DateTimeFormatter
+ .ofPattern(FORMAT_HOUR_MINUTE_SECOND)
+ .withZone(ZoneId.systemDefault()));
+
+}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/application-unit-test.yaml b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/application-unit-test.yaml
index 25bfe0a62..1bbe0f530 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/application-unit-test.yaml
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/application-unit-test.yaml
@@ -9,7 +9,7 @@ spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
- url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
+ url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java
index c4dcfe8a0..2492c803d 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java
@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.infra.dal.mysql.file;
+import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.file.core.client.db.DBFileContentFrameworkDAO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
+import java.util.List;
+import java.util.Optional;
@Repository
public class FileContentDAOImpl implements DBFileContentFrameworkDAO {
@@ -27,9 +30,11 @@ public class FileContentDAOImpl implements DBFileContentFrameworkDAO {
@Override
public byte[] selectContent(Long configId, String path) {
- FileContentDO fileContentDO = fileContentMapper.selectOne(
- buildQuery(configId, path).select(FileContentDO::getContent));
- return fileContentDO != null ? fileContentDO.getContent() : null;
+ List list = fileContentMapper.selectList(
+ buildQuery(configId, path).select(FileContentDO::getContent).orderByDesc(FileContentDO::getId));
+ return Optional.ofNullable(CollUtil.getFirst(list))
+ .map(FileContentDO::getContent)
+ .orElse(null);
}
private LambdaQueryWrapper buildQuery(Long configId, String path) {
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
index 90f5816f3..8293ffef0 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
@@ -18,6 +18,8 @@ import org.springframework.stereotype.Component;
import java.util.*;
import static cn.hutool.core.text.CharSequenceUtil.*;
+import static cn.hutool.core.util.RandomUtil.randomEle;
+import static cn.hutool.core.util.RandomUtil.randomInt;
/**
* 代码生成器的 Builder,负责:
@@ -128,6 +130,7 @@ public class CodegenBuilder {
// 初始化 Column 列的默认字段
processColumnOperation(column); // 处理 CRUD 相关的字段的默认值
processColumnUI(column); // 处理 UI 相关的字段的默认值
+ processColumnExample(column); // 处理字段的 swagger example 示例
}
return columns;
}
@@ -169,4 +172,42 @@ public class CodegenBuilder {
}
}
+ /**
+ * 处理字段的 swagger example 示例
+ *
+ * @param column 字段
+ */
+ private void processColumnExample(CodegenColumnDO column) {
+ // id、price、count 等可能是整数的后缀
+ if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "id", "price", "count")) {
+ column.setExample(String.valueOf(randomInt(1, Short.MAX_VALUE)));
+ return;
+ }
+ // name
+ if (StrUtil.endWithIgnoreCase(column.getJavaField(), "name")) {
+ column.setExample(randomEle(new String[]{"张三", "李四", "王五", "赵六", "芋艿"}));
+ return;
+ }
+ // status
+ if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "status", "type")) {
+ column.setExample(randomEle(new String[]{"1", "2"}));
+ return;
+ }
+ // url
+ if (StrUtil.endWithIgnoreCase(column.getColumnName(), "url")) {
+ column.setExample("https://www.iocoder.cn");
+ return;
+ }
+ // reason
+ if (StrUtil.endWithIgnoreCase(column.getColumnName(), "reason")) {
+ column.setExample(randomEle(new String[]{"不喜欢", "不对", "不好", "不香"}));
+ return;
+ }
+ // description、memo、remark
+ if (StrUtil.endWithAnyIgnoreCase(column.getColumnName(), "description", "memo", "remark")) {
+ column.setExample(randomEle(new String[]{"你猜", "随便", "你说的对"}));
+ return;
+ }
+ }
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
index f68c09eb1..48361c14c 100755
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.infra.service.file;
-import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -20,7 +19,6 @@ import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -79,20 +77,36 @@ public class FileConfigServiceImpl implements FileConfigService {
@Resource
private Validator validator;
- @Resource
- @Lazy // 注入自己,所以延迟加载
- private FileConfigService self;
-
@Override
@PostConstruct
public void initFileClients() {
- // 获取文件配置,如果有更新
- List configs = loadFileConfigIfUpdate(maxUpdateTime);
- if (CollUtil.isEmpty(configs)) {
+ initLocalCacheIfUpdate(null);
+ }
+
+ @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
+ public void schedulePeriodicRefresh() {
+ initLocalCacheIfUpdate(this.maxUpdateTime);
+ }
+
+ /**
+ * 刷新本地缓存
+ *
+ * @param maxUpdateTime 最大更新时间
+ * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
+ * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
+ */
+ private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
+ // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
+ // 如果没有增量的数据变化,则不进行本地缓存的刷新
+ if (maxUpdateTime != null
+ && fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
+ log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
return;
}
+ List configs = fileConfigMapper.selectList();
+ log.info("[initLocalCacheIfUpdate][缓存文件配置,数量为:{}]", configs.size());
- // 创建或更新支付 Client
+ // 第二步:构建缓存。创建或更新文件 Client
configs.forEach(config -> {
fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
// 如果是 master,进行设置
@@ -101,35 +115,8 @@ public class FileConfigServiceImpl implements FileConfigService {
}
});
- // 写入缓存
- maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime);
- log.info("[initFileClients][初始化 FileConfig 数量为 {}]", configs.size());
- }
-
- @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
- public void schedulePeriodicRefresh() {
- self.initFileClients();
- }
-
- /**
- * 如果文件配置发生变化,从数据库中获取最新的全量文件配置。
- * 如果未发生变化,则返回空
- *
- * @param maxUpdateTime 当前文件配置的最大更新时间
- * @return 文件配置列表
- */
- private List loadFileConfigIfUpdate(LocalDateTime maxUpdateTime) {
- // 第一步,判断是否要更新。
- if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
- log.info("[loadFileConfigIfUpdate][首次加载全量文件配置]");
- } else { // 判断数据库中是否有更新的文件配置
- if (fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
- return null;
- }
- log.info("[loadFileConfigIfUpdate][增量加载全量文件配置]");
- }
- // 第二步,如果有更新,则从数据库加载所有文件配置
- return fileConfigMapper.selectList();
+ // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
+ this.maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime);
}
@Override
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
index 06fe3002a..94a9e11df 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -1,7 +1,7 @@
-
+
-
+
@@ -79,8 +79,7 @@
import { ref, unref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
-import { useVxeGrid } from '@/hooks/web/useVxeGrid'
-import { VxeGridInstance } from 'vxe-table'
+import { useXTable } from '@/hooks/web/useXTable'
import { FormExpose } from '@/components/Form'
// 业务相关的 import
import { rules, allSchemas } from './${classNameVar}.data'
@@ -90,8 +89,7 @@ const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
// 列表相关的变量
-const xGrid = ref() // 列表 Grid Ref
-const { gridOptions, getList, deleteData, exportList } = useVxeGrid<${simpleClassName}Api.${simpleClassName}VO>({
+const [registerTable, { reload, deleteData, exportList }] = useXTable({
allSchemas: allSchemas,
getListApi: ${simpleClassName}Api.get${simpleClassName}PageApi,
deleteApi: ${simpleClassName}Api.delete${simpleClassName}Api,
@@ -123,7 +121,7 @@ const handleCreate = () => {
// 导出操作
const handleExport = async () => {
- await exportList(xGrid, '${table.classComment}.xls')
+ await exportList('${table.classComment}.xls')
}
// 修改操作
@@ -145,7 +143,7 @@ const handleDetail = async (rowId: number) => {
// 删除操作
const handleDelete = async (rowId: number) => {
- await deleteData(xGrid, rowId)
+ await deleteData(rowId)
}
// 提交按钮
@@ -169,7 +167,7 @@ const submitForm = async () => {
} finally {
actionLoading.value = false
// 刷新列表
- await getList(xGrid)
+ await reload()
}
}
})
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java
index 2d4285bee..90bde8f4e 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java
@@ -79,7 +79,7 @@ public class FileServiceTest extends BaseDbUnitTest {
FileClient client = mock(FileClient.class);
when(fileConfigService.getMasterFileClient()).thenReturn(client);
String url = randomString();
- when(client.upload(same(content), same(path), same("image/jpeg"))).thenReturn(url);
+ when(client.upload(same(content), same(path), eq("image/jpeg"))).thenReturn(url);
when(client.getId()).thenReturn(10L);
String name = "单测文件名";
// 调用
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml
index 3a2079cdc..31e5ae5c9 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml
@@ -9,7 +9,7 @@ spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
- url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
+ url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
diff --git a/yudao-module-mall/pom.xml b/yudao-module-mall/pom.xml
index 4c362d526..37484f00c 100644
--- a/yudao-module-mall/pom.xml
+++ b/yudao-module-mall/pom.xml
@@ -15,18 +15,15 @@
${project.artifactId}
- 商城大模块,由 product 商品、promotion 营销、trade 交易 coupon等组成
+ 商城大模块,由 product 商品、promotion 营销、trade 交易等组成
-
-
yudao-module-promotion-api
yudao-module-promotion-biz
yudao-module-product-api
yudao-module-product-biz
yudao-module-trade-api
yudao-module-trade-biz
-
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApi.java
new file mode 100644
index 000000000..83269f91d
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApi.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.product.api.property;
+
+import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 商品属性值 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface ProductPropertyValueApi {
+
+ /**
+ * 根据编号数组,获得属性值列表
+ *
+ * @param ids 编号数组
+ * @return 属性值明细列表
+ */
+ List getPropertyValueDetailList(Collection ids);
+
+}
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/dto/ProductPropertyValueDetailRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/dto/ProductPropertyValueDetailRespDTO.java
new file mode 100644
index 000000000..2a1ab71a9
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/property/dto/ProductPropertyValueDetailRespDTO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.product.api.property.dto;
+
+import lombok.Data;
+
+/**
+ * 商品属性项的明细 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ProductPropertyValueDetailRespDTO {
+
+ /**
+ * 属性的编号
+ */
+ private Long propertyId;
+
+ /**
+ * 属性的名称
+ */
+ private String propertyName;
+
+ /**
+ * 属性值的编号
+ */
+ private Long valueId;
+
+ /**
+ * 属性值的名称
+ */
+ private String valueName;
+
+}
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
index 4b324149b..aaaf767f2 100644
--- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
+++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
@@ -18,17 +18,17 @@ public class ProductSkuRespDTO {
* 商品 SKU 编号,自增
*/
private Long id;
- /**
- * 商品 SKU 名字
- */
- private String name;
/**
* SPU 编号
*/
private Long spuId;
+ /**
+ * SPU 名字
+ */
+ private String spuName;
/**
- * 规格值数组,JSON 格式
+ * 属性数组,JSON 格式
*/
private List properties;
/**
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 86875b119..4adad0afc 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
@@ -15,28 +15,30 @@ public interface ErrorCodeConstants {
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
- ErrorCode CATEGORY_LEVEL_ERROR = new ErrorCode(1008001005, "商品分类不正确,原因:必须使用第三级的商品分类下");
// ========== 商品品牌相关编号 1008002000 ==========
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌不存在");
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
- // ========== 商品规格名称 1008003000 ==========
- ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "规格名称不存在");
- ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "规格名称已存在");
+ // ========== 商品属性项 1008003000 ==========
+ ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "属性项不存在");
+ ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "属性项的名称已存在");
+ ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1008003002, "属性项下存在属性值,无法删除");
- // ========== 规格值 1008004000 ==========
- ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "规格值不存在");
- ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1008004001, "规格值已存在");
+ // ========== 商品属性值 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 不存在");
+ ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第三级的商品分类下");
+ ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
// ========== 商品 SKU 1008006000 ==========
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复");
- ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 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/ProductSpuSpecTypeEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java
index 30ece744d..fbc227b53 100644
--- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java
+++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java
@@ -21,11 +21,11 @@ public enum ProductSpuSpecTypeEnum implements IntArrayValuable {
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuSpecTypeEnum::getType).toArray();
/**
- * 规格
+ * 规格类型
*/
private final Integer type;
/**
- * 规格名
+ * 规格名称
*/
private final String name;
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 6b7f03ac2..2223cf23d 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
@@ -35,4 +35,14 @@ public enum ProductSpuStatusEnum implements IntArrayValuable {
return ARRAYS;
}
+ /**
+ * 判断是否处于【上架】状态
+ *
+ * @param status 状态
+ * @return 是否处于【上架】状态
+ */
+ public static boolean isEnable(Integer status) {
+ return ENABLE.getStatus().equals(status);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApiImpl.java
new file mode 100644
index 000000000..9aab9e560
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApiImpl.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.product.api.property;
+
+import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
+import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert;
+import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 商品属性值 API 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class ProductPropertyValueApiImpl implements ProductPropertyValueApi {
+
+ @Resource
+ private ProductPropertyValueService productPropertyValueService;
+
+ @Override
+ public List getPropertyValueDetailList(Collection ids) {
+ return ProductPropertyValueConvert.INSTANCE.convertList02(
+ productPropertyValueService.getPropertyValueDetailList(ids));
+ }
+
+}
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 54ee1fbbd..8f5120723 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
@@ -1,9 +1,14 @@
package cn.iocoder.yudao.module.product.controller.admin.property;
+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.module.product.controller.admin.property.vo.property.*;
+import cn.iocoder.yudao.module.product.convert.property.ProductPropertyConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
+import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
@@ -13,12 +18,13 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
-
+import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-@Tag(name = "管理后台 - 规格名称")
+@Tag(name = "管理后台 - 商品属性项")
@RestController
@RequestMapping("/product/property")
@Validated
@@ -26,16 +32,18 @@ public class ProductPropertyController {
@Resource
private ProductPropertyService productPropertyService;
+ @Resource
+ private ProductPropertyValueService productPropertyValueService;
@PostMapping("/create")
- @Operation(summary = "创建规格名称")
+ @Operation(summary = "创建属性项")
@PreAuthorize("@ss.hasPermission('product:property:create')")
public CommonResult createProperty(@Valid @RequestBody ProductPropertyCreateReqVO createReqVO) {
return success(productPropertyService.createProperty(createReqVO));
}
@PutMapping("/update")
- @Operation(summary = "更新规格名称")
+ @Operation(summary = "更新属性项")
@PreAuthorize("@ss.hasPermission('product:property:update')")
public CommonResult updateProperty(@Valid @RequestBody ProductPropertyUpdateReqVO updateReqVO) {
productPropertyService.updateProperty(updateReqVO);
@@ -43,7 +51,7 @@ public class ProductPropertyController {
}
@DeleteMapping("/delete")
- @Operation(summary = "删除规格名称")
+ @Operation(summary = "删除属性项")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('product:property:delete')")
public CommonResult deleteProperty(@RequestParam("id") Long id) {
@@ -52,32 +60,40 @@ public class ProductPropertyController {
}
@GetMapping("/get")
- @Operation(summary = "获得规格名称")
+ @Operation(summary = "获得属性项")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult getProperty(@RequestParam("id") Long id) {
- return success(productPropertyService.getProperty(id));
+ return success(ProductPropertyConvert.INSTANCE.convert(productPropertyService.getProperty(id)));
}
@GetMapping("/list")
- @Operation(summary = "获得规格名称列表")
+ @Operation(summary = "获得属性项列表")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult> getPropertyList(@Valid ProductPropertyListReqVO listReqVO) {
- return success(productPropertyService.getPropertyList(listReqVO));
+ return success(ProductPropertyConvert.INSTANCE.convertList(productPropertyService.getPropertyList(listReqVO)));
}
@GetMapping("/page")
- @Operation(summary = "获得规格名称分页")
+ @Operation(summary = "获得属性项分页")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) {
- return success(productPropertyService.getPropertyPage(pageVO));
+ return success(ProductPropertyConvert.INSTANCE.convertPage(productPropertyService.getPropertyPage(pageVO)));
}
- @GetMapping("/listAndValue")
- @Operation(summary = "获得规格名称列表")
+ @GetMapping("/get-value-list")
+ @Operation(summary = "获得属性项列表")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult> getPropertyAndValueList(@Valid ProductPropertyListReqVO listReqVO) {
- return success(productPropertyService.getPropertyAndValueList(listReqVO));
+ // 查询属性项
+ List keys = productPropertyService.getPropertyList(listReqVO);
+ if (CollUtil.isEmpty(keys)) {
+ return success(Collections.emptyList());
+ }
+ // 查询属性值
+ List values = productPropertyValueService.getPropertyValueListByPropertyId(
+ convertSet(keys, ProductPropertyDO::getId));
+ return success(ProductPropertyConvert.INSTANCE.convertList(keys, values));
}
}
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
index cdcd1f7a3..5c377220a 100644
--- 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
@@ -2,11 +2,11 @@ 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.convert.propertyvalue.ProductPropertyValueConvert;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
@@ -20,7 +20,7 @@ import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-@Tag(name = "管理后台 - 规格值名称")
+@Tag(name = "管理后台 - 商品属性值")
@RestController
@RequestMapping("/product/property/value")
@Validated
@@ -30,14 +30,14 @@ public class ProductPropertyValueController {
private ProductPropertyValueService productPropertyValueService;
@PostMapping("/create")
- @Operation(summary = "创建规格名称")
+ @Operation(summary = "创建属性值")
@PreAuthorize("@ss.hasPermission('product:property:create')")
public CommonResult createProperty(@Valid @RequestBody ProductPropertyValueCreateReqVO createReqVO) {
return success(productPropertyValueService.createPropertyValue(createReqVO));
}
@PutMapping("/update")
- @Operation(summary = "更新规格名称")
+ @Operation(summary = "更新属性值")
@PreAuthorize("@ss.hasPermission('product:property:update')")
public CommonResult updateProperty(@Valid @RequestBody ProductPropertyValueUpdateReqVO updateReqVO) {
productPropertyValueService.updatePropertyValue(updateReqVO);
@@ -45,7 +45,7 @@ public class ProductPropertyValueController {
}
@DeleteMapping("/delete")
- @Operation(summary = "删除规格名称")
+ @Operation(summary = "删除属性值")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('product:property:delete')")
public CommonResult deleteProperty(@RequestParam("id") Long id) {
@@ -54,17 +54,17 @@ public class ProductPropertyValueController {
}
@GetMapping("/get")
- @Operation(summary = "获得规格名称")
+ @Operation(summary = "获得属性值")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult getProperty(@RequestParam("id") Long id) {
- return success(productPropertyValueService.getPropertyValue(id));
+ return success(ProductPropertyValueConvert.INSTANCE.convert(productPropertyValueService.getPropertyValue(id)));
}
@GetMapping("/page")
- @Operation(summary = "获得规格名称分页")
+ @Operation(summary = "获得属性值分页")
@PreAuthorize("@ss.hasPermission('product:property:query')")
public CommonResult> getPropertyValuePage(@Valid ProductPropertyValuePageReqVO pageVO) {
- return success(productPropertyValueService.getPropertyValueListPage(pageVO));
+ return success(ProductPropertyValueConvert.INSTANCE.convertPage(productPropertyValueService.getPropertyValuePage(pageVO)));
}
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java
index d72a3036f..e69de29bb 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java
@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.admin.property.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.ToString;
-
-import java.util.List;
-
-/**
- * @Description: ProductPropertyViewRespVO
- * @Author: franky
- * @CreateDate: 2022/7/5 21:29
- * @Version: 1.0.0
- */
-@Schema(description = "管理后台 - 规格名称详情展示 Request VO")
-@Data
-@ToString(callSuper = true)
-public class ProductPropertyViewRespVO {
-
- @Schema(description = "规格名称id", example = "1")
- public Long propertyId;
-
- @Schema(description = "规格名称", example = "内存")
- public String name;
-
- @Schema(description = "规格属性值集合", example = "[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]")
- public List propertyValues;
-
- @Data
- @Schema(description = "规格属性值元组")
- public static class Tuple2 {
- private final long id;
- private final String name;
-
- public Tuple2(Long id, String name) {
- this.id = id;
- this.name = name;
- }
-
- }
-
-
-}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java
index 300255724..ec569d1f3 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java
@@ -1,29 +1,35 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
-import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-import java.time.LocalDateTime;
import java.util.List;
-@Schema(description = "管理后台 - 规格 + 规格值 Response VO")
+@Schema(description = "管理后台 - 商品属性项 + 属性值 Response VO")
@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class ProductPropertyAndValueRespVO extends ProductPropertyBaseVO {
+public class ProductPropertyAndValueRespVO {
- @Schema(description = "规格的编号", required = true, example = "1024")
+ @ApiModelProperty(value = "属性项的编号", required = true, example = "1024")
private Long id;
- @Schema(description = "创建时间", required = true)
- private LocalDateTime createTime;
+ @ApiModelProperty(value = "属性项的名称", required = true, example = "颜色")
+ private String name;
/**
- * 规格值的集合
+ * 属性值的集合
*/
- private List values;
+ private List values;
+
+ @ApiModel("管理后台 - 属性值的简单 Response VO")
+ @Data
+ public static class Value {
+
+ @Schema(description = "属性值的编号", required = true, example = "2048")
+ private Long id;
+
+ @Schema(description = "属性值的名称", required = true, example = "红色")
+ private String name;
+
+ }
}
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 3bb4ec530..1f05af4b2 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
@@ -1,26 +1,22 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
/**
- * 规格名称 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 商品属性项 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ProductPropertyBaseVO {
- @Schema(description = "规格名称", required = true, example = "颜色")
- @NotBlank(message = "规格名称不能为空")
+ @Schema(description = "名称", required = true, example = "颜色")
+ @NotBlank(message = "名称不能为空")
private String name;
@Schema(description = "备注", example = "颜色")
private String remark;
- @Schema(description = "状态-参见 CommonStatusEnum 枚举", required = true, example = "1")
- @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 5cc8c099f..b854dd73c 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,10 +1,11 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
-@Schema(description = "管理后台 - 规格名称创建 Request VO")
+@Schema(description = "管理后台 - 属性项创建 Request 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/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
index 00d3372fe..242caff84 100644
--- 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
@@ -4,15 +4,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
-@Schema(description = "管理后台 - 规格名称 List Request VO")
+@Schema(description = "管理后台 - 属性项 List Request VO")
@Data
@ToString(callSuper = true)
public class ProductPropertyListReqVO {
- @Schema(description = "规格名称", example = "颜色")
+ @Schema(description = "名称", example = "颜色")
private String name;
- @Schema(description = "状态,参见 CommonStatusEnum 枚举", required = true, example = "1")
- 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/ProductPropertyPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java
index c997f9bcb..a725e2076 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java
@@ -11,16 +11,16 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-@Schema(description = "管理后台 - 规格名称分页 Request VO")
+@Schema(description = "管理后台 - 属性项 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductPropertyPageReqVO extends PageParam {
- @Schema(description = "规格名称", example = "颜色")
+ @Schema(description = "名称", example = "颜色")
private String name;
- @Schema(description = "状态,参见 CommonStatusEnum 枚举", required = true, example = "1")
+ @Schema(description = "状态-参见 CommonStatusEnum 枚举", required = true, example = "1")
private Integer status;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
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
index 1fb81461f..b33e615fc 100644
--- 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
@@ -7,13 +7,13 @@ import lombok.ToString;
import java.time.LocalDateTime;
-@Schema(description = "管理后台 - 规格 + 规格值 Response VO")
+@Schema(description = "管理后台 - 属性项 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductPropertyRespVO extends ProductPropertyBaseVO {
- @Schema(description = "规格的编号", required = true, example = "1024")
+ @Schema(description = "编号", required = true, example = "1024")
private Long id;
@Schema(description = "创建时间", required = true)
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 87d7accd8..33ba8f2c1 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
@@ -1,9 +1,13 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import javax.validation.constraints.*;
-@Schema(description = "管理后台 - 规格名称更新 Request VO")
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 属性项更新 Request 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/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 966027c59..da3bf5068 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
@@ -1,4 +1,5 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -6,24 +7,20 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
-* 规格值 Base VO,提供给添加、修改、详细的子 VO 使用
+* 属性值 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ProductPropertyValueBaseVO {
- @Schema(description = "规格编号", required = true, example = "1024")
- @NotNull(message = "规格编号不能为空")
+ @Schema(description = "属性项的编号", required = true, example = "1024")
+ @NotNull(message = "属性项的编号不能为空")
private Long propertyId;
- @Schema(description = "规格值名字", required = true, example = "红色")
- @NotEmpty(message = "规格值名字不能为空")
+ @Schema(description = "名称", required = true, example = "红色")
+ @NotEmpty(message = "名称名字不能为空")
private String name;
- @Schema(description = "状态,参见 CommonStatusEnum 枚举", required = true, example = "1")
- @NotNull(message = "状态不能为空")
- private Integer status;
-
@Schema(description = "备注", 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/ProductPropertyValueCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java
index 23a23b0fe..a643927ae 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java
@@ -1,8 +1,9 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
-@Schema(description = "管理后台 - 规格值创建 Request VO")
+@Schema(description = "管理后台 - 商品属性值创建 Request 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/property/vo/value/ProductPropertyValueDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueDetailRespVO.java
new file mode 100644
index 000000000..55bae16df
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueDetailRespVO.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel("管理后台 - 商品属性值的明细 Response VO")
+@Data
+public class ProductPropertyValueDetailRespVO {
+
+ @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-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
index bafa24ebd..e484324f0 100644
--- 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
@@ -5,26 +5,20 @@ import io.swagger.v3.oas.annotations.media.Schema;
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;
-
-@Schema(description = "管理后台 - 规格名称值分页 Request VO")
+@Schema(description = "管理后台 - 商品属性值分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductPropertyValuePageReqVO extends PageParam {
- @Schema(description = "规格id", example = "1024")
+ @Schema(description = "属性项的编号", example = "1024")
private String propertyId;
- @Schema(description = "规格值", example = "红色")
+ @Schema(description = "名称", example = "红色")
private String name;
- @Schema(description = "状态,参见 CommonStatusEnum 枚举", required = true, example = "1")
+ @Schema(description = "状态 - 参见 CommonStatusEnum 枚举", required = true, example = "1")
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/ProductPropertyValueRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java
index d914d88a1..dac075b86 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java
@@ -7,13 +7,13 @@ import lombok.ToString;
import java.time.LocalDateTime;
-@Schema(description = "管理后台 - 规格值 Response VO")
+@Schema(description = "管理后台 - 商品属性值 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductPropertyValueRespVO extends ProductPropertyValueBaseVO {
- @Schema(description = "主键", required = true, example = "10")
+ @Schema(description = "编号", required = true, example = "10")
private Long id;
@Schema(description = "创建时间")
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 b3a34aeb7..e23b8927c 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
@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
-@Schema(description = "管理后台 - 规格值更新 Request VO")
+@Schema(description = "管理后台 - 商品属性值更新 Request 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/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 313cedefa..7c1117d57 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
@@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.product.controller.admin.sku.vo;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@@ -15,54 +17,56 @@ import javax.validation.constraints.NotNull;
@Data
public class ProductSkuBaseVO {
- @Schema(description = "商品 SKU 名字", required = true, example = "芋道")
+ @Schema(description = "商品 SKU 名字", required = true, example = "芋道")
@NotEmpty(message = "商品 SKU 名字不能为空")
private String name;
- @Schema(description = "销售价格,单位:分", required = true, example = "1024")
+ @Schema(description = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分")
@NotNull(message = "销售价格,单位:分不能为空")
private Integer price;
- @Schema(description = "市场价,单位:分", example = "1024")
+ @Schema(description = "市场价", example = "1024", notes = "单位:分")
private Integer marketPrice;
- @Schema(description = "成本价,单位:分", example = "1024")
+ @Schema(description = "成本价", example = "1024", notes = "单位:分")
private Integer costPrice;
- @Schema(description = "条形码", example = "haha")
+ @Schema(description = "条形码", example = "haha")
private String barCode;
- @Schema(description = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png")
+ @Schema(description = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png")
@NotNull(message = "图片地址不能为空")
private String picUrl;
- @Schema(description = "SKU 状态,参见 CommonStatusEnum 枚举", required = true, example = "1")
+ @Schema(description = "SKU 状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
@NotNull(message = "SKU 状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
- @Schema(description = "库存", required = true, example = "1")
+ @Schema(description = "库存", required = true, example = "1")
@NotNull(message = "库存不能为空")
private Integer stock;
- @Schema(description = "预警预存", example = "1")
+ @Schema(description = "预警预存", example = "1")
private Integer warnStock;
- @Schema(description = "商品重量,单位:kg 千克", example = "1")
+ @Schema(description = "商品重量 - 单位:kg 千克", example = "1")
private Double weight;
- @Schema(description = "商品体积,单位:m^3 平米", example = "1024")
+ @Schema(description = "商品体积 - 单位:m^3 平米", example = "1024")
private Double volume;
- @Schema(description = "规格值")
+ @ApiModel("商品属性")
@Data
+ @AllArgsConstructor
+ @NoArgsConstructor
public static class Property {
- @Schema(description = "属性编号", required = true, example = "1")
+ @Schema(description = "属性编号", required = true, example = "1")
@NotNull(message = "属性编号不能为空")
private Long propertyId;
- @Schema(description = "属性值编号", required = true, example = "1024")
+ @Schema(description = "属性值编号", required = true, example = "1024")
@NotNull(message = "属性值编号不能为空")
private Long valueId;
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 7383e2416..496475f99 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
@@ -13,11 +13,8 @@ import java.util.List;
@ToString(callSuper = true)
public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO {
- @Schema(description = "商品 SKU 编号", example = "1")
- private Long id;
-
/**
- * 规格值数组
+ * 属性数组
*/
private List properties;
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 0b0ec665a..95e394ada 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
@@ -21,7 +21,7 @@ public class ProductSkuRespVO extends ProductSkuBaseVO {
private LocalDateTime createTime;
/**
- * 规格值数组
+ * 属性数组
*/
private List properties;
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http
new file mode 100644
index 000000000..4ab7b4f71
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http
@@ -0,0 +1,4 @@
+### 获得商品 SPU 明细
+GET {{baseUrl}}/product/spu/get-detail?id=4
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenentId}}
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 0801da438..10da8104f 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
@@ -3,8 +3,13 @@ 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.*;
+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.service.property.ProductPropertyValueService;
+import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
+import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
@@ -15,10 +20,11 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
-import java.util.Collection;
import java.util.List;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
@Tag(name = "管理后台 - 商品 SPU")
@RestController
@@ -27,64 +33,61 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
public class ProductSpuController {
@Resource
- private ProductSpuService spuService;
+ private ProductSpuService productSpuService;
+ @Resource
+ private ProductSkuService productSkuService;
+ @Resource
+ private ProductPropertyValueService productPropertyValueService;
@PostMapping("/create")
@Operation(summary = "创建商品 SPU")
@PreAuthorize("@ss.hasPermission('product:spu:create')")
public CommonResult createProductSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) {
- return success(spuService.createSpu(createReqVO));
+ return success(productSpuService.createSpu(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新商品 SPU")
@PreAuthorize("@ss.hasPermission('product:spu:update')")
public CommonResult updateSpu(@Valid @RequestBody ProductSpuUpdateReqVO updateReqVO) {
- spuService.updateSpu(updateReqVO);
+ productSpuService.updateSpu(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除商品 SPU")
- @Parameter(name = "id", description = "编号", required = true)
+ @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('product:spu:delete')")
public CommonResult deleteSpu(@RequestParam("id") Long id) {
- spuService.deleteSpu(id);
+ productSpuService.deleteSpu(id);
return success(true);
}
- // TODO 芋艿:修改接口
- @GetMapping("/get/detail")
- @Operation(summary = "获得商品 SPU")
- @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @GetMapping("/get-detail")
+ @Operation(summary = "获得商品 SPU 明细")
+ @Parameter(name = "id", value = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult getSpuDetail(@RequestParam("id") Long id) {
- return success(spuService.getSpuDetail(id));
- }
+ // 获得商品 SPU
+ ProductSpuDO spu = productSpuService.getSpu(id);
+ if (spu == null) {
+ throw exception(SPU_NOT_EXISTS);
+ }
- @GetMapping("/get")
- @Operation(summary = "获得商品 SPU")
- @Parameter(name = "id", description = "编号", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('product:spu:query')")
- public CommonResult getSpu(@RequestParam("id") Long id) {
- return success(spuService.getSpu(id));
- }
-
-
- @GetMapping("/list")
- @Operation(summary = "获得商品 SPU 列表")
- @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
- @PreAuthorize("@ss.hasPermission('product:spu:query')")
- public CommonResult> getSpuList(@RequestParam("ids") Collection ids) {
- List list = spuService.getSpuList(ids);
- return success(ProductSpuConvert.INSTANCE.convertList(list));
+ // 查询商品 SKU
+ List skus = productSkuService.getSkuListBySpuIdAndStatus(spu.getId(), null);
+ // 查询商品属性
+ List propertyValues = productPropertyValueService
+ .getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
+ // 拼接
+ return success(ProductSpuConvert.INSTANCE.convert03(spu, skus, propertyValues));
}
@GetMapping("/get-simple-list")
@Operation(summary = "获得商品 SPU 精简列表")
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult> getSpuSimpleList() {
- List list = spuService.getSpuList();
+ List list = productSpuService.getSpuList();
return success(ProductSpuConvert.INSTANCE.convertList02(list));
}
@@ -92,7 +95,7 @@ public class ProductSpuController {
@Operation(summary = "获得商品 SPU 分页")
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
- return success(spuService.getSpuPage(pageVO));
+ return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO)));
}
}
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 1c3c22485..22d1d3025 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
@@ -11,81 +11,66 @@ import javax.validation.constraints.NotNull;
import java.util.List;
/**
-* 商品 SPU Base VO,提供给添加、修改、详细的子 VO 使用
-* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
-*/
+ * 商品 SPU Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
@Data
public class ProductSpuBaseVO {
- @Schema(description = "商品名称", required = true, example = "芋道")
+ @Schema(description = = "商品名称", required = true, example = "芋道")
@NotEmpty(message = "商品名称不能为空")
private String name;
- @Schema(description = "商品编码", example = "yudaoyuanma")
+ @Schema(description = = "商品编码", example = "yudaoyuanma")
private String code;
- @Schema(description = "促销语", example = "好吃!")
+ @Schema(description = = "促销语", example = "好吃!")
private String sellPoint;
- @Schema(description = "商品详情", required = true, example = "我是商品描述")
+ @Schema(description = = "商品详情", required = true, example = "我是商品描述")
@NotNull(message = "商品详情不能为空")
private String description;
- @Schema(description = "商品分类编号", required = true, example = "1")
+ @Schema(description = = "商品分类编号", required = true, example = "1")
@NotNull(message = "商品分类编号不能为空")
private Long categoryId;
- @Schema(description = "商品品牌编号", example = "1")
+ @Schema(description = = "商品品牌编号", example = "1")
private Long brandId;
- @Schema(description = "商品图片的数组", required = true)
+ @Schema(description = = "商品图片的数组", required = true)
@NotNull(message = "商品图片的数组不能为空")
private List picUrls;
- @Schema(description = "商品视频", required = true)
+ @Schema(description = = "商品视频", required = true)
private String videoUrl;
- @Schema(description = "排序字段", required = true, example = "1")
+ @Schema(description = = "排序字段", required = true, example = "1")
private Integer sort;
- @Schema(description = "商品状态,参见 ProductSpuStatusEnum 枚举类", required = true, example = "1")
+ @Schema(description = = "商品状态 参见 ProductSpuStatusEnum 枚举类", required = true, example = "1")
@NotNull(message = "商品状态不能为空")
@InEnum(ProductSpuStatusEnum.class)
private Integer status;
// ========== SKU 相关字段 =========
- @Schema(description = "规格类型,参见 ProductSpuSpecTypeEnum 枚举类", required = true, example = "1")
+ @Schema(description = = "规格类型 参见 ProductSpuSpecTypeEnum 枚举类", required = true, example = "1")
@NotNull(message = "规格类型不能为空")
@InEnum(ProductSpuSpecTypeEnum.class)
private Integer specType;
- @Schema(description = "是否展示库存", required = true, example = "true")
+ @Schema(description = = "是否展示库存", required = true, example = "true")
@NotNull(message = "是否展示库存不能为空")
private Boolean showStock;
- @Schema(description = "库存", required = true, example = "true")
- private Integer totalStock;
-
- @Schema(description = "市场价", example = "1024")
+ @Schema(description = = "市场价", example = "1024")
private Integer marketPrice;
- @Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")
- private Integer minPrice;
-
- @Schema(description = "最大价格,单位使用:分", required = true, example = "1024")
- private Integer maxPrice;
-
// ========== 统计相关字段 =========
- @Schema(description = "商品销量", example = "1024")
- private Integer salesCount;
-
- @Schema(description = "虚拟销量", required = true, example = "1024")
+ @Schema(description = = "虚拟销量", required = true, example = "1024")
@NotNull(message = "虚拟销量不能为空")
private Integer virtualSalesCount;
- @Schema(description = "点击量", 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 e60d2fff5..a8cf9f5b9 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,26 +1,21 @@
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.property.vo.value.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
-import java.time.LocalDateTime;
import java.util.List;
-@Schema(description = "管理后台 - 商品 SPU 详细 Response VO,包括关联的 SKU 等信息")
+@Schema(description = "管理后台 - 商品 SPU 详细 Response VO 包括关联的 SKU 等信息")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
-public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
+public class ProductSpuDetailRespVO extends ProductSpuRespVO {
- @Schema(description = "主键", required = true, example = "1")
- private Long id;
-
- @Schema(description = "创建时间")
- private LocalDateTime createTime;
+ // ========== SKU 相关字段 =========
/**
* SKU 数组
@@ -34,31 +29,10 @@ public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
public static class Sku extends ProductSkuBaseVO {
/**
- * 规格的数组
+ * 属性数组
*/
- private List properties;
+ private List properties;
}
- @Schema(description = "管理后台 - 商品规格的详细 Response VO")
- @Data
- @EqualsAndHashCode(callSuper = true)
- @ToString(callSuper = true)
- public static class Property extends ProductSkuBaseVO.Property {
-
- @Schema(description = "规格的名字", required = true, example = "颜色")
- private String propertyName;
-
- @Schema(description = "规格值的名字", required = true, example = "蓝色")
- private String valueName;
-
- }
-
- @Schema(description = "分类 id 数组,一直递归到一级父节点", example = "4")
- private Long categoryId;
-
- // TODO @芋艿:在瞅瞅~
- @Schema(description = "规格属性修改和详情展示组合", 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 3d30ad013..d4714328f 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
@@ -18,13 +18,13 @@ public class ProductSpuPageReqVO extends PageParam {
@Schema(description = "商品编码", example = "yudaoyuanma")
private String code;
- @Schema(description = "分类id", example = "1")
+ @Schema(description = "分类编号", example = "1")
private Long categoryId;
@Schema(description = "商品品牌编号", example = "1")
private Long brandId;
- @Schema(description = "上下架状态,参见 ProductSpuStatusEnum 枚举值", example = "1")
+ @Schema(description = "上下架状态 参见 ProductSpuStatusEnum 枚举值", example = "1")
private Integer status;
@Schema(description = "销量最小值", example = "1")
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 a08c4900e..49303a7a3 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
@@ -6,7 +6,6 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
-import java.util.List;
@Schema(description = "管理后台 - 商品 SPU Response VO")
@Data
@@ -20,4 +19,22 @@ public class ProductSpuRespVO extends ProductSpuBaseVO {
@Schema(description = "创建时间")
private LocalDateTime createTime;
+ // ========== SKU 相关字段 =========
+
+ @ApiModelProperty(value = "库存", required = true, example = "true")
+ private Integer totalStock;
+
+ @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
+ private Integer minPrice;
+
+ @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
+ private Integer maxPrice;
+
+ @ApiModelProperty(value = "商品销量", example = "1024")
+ private Integer salesCount;
+
+ // ========== 统计相关字段 =========
+
+ @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/app/property/package-info.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/package-info.java
new file mode 100644
index 000000000..379e85180
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位符,无时间作用,避免 package 缩进
+ */
+package cn.iocoder.yudao.module.product.controller.app.property;
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/property/package-info.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/property/package-info.java
new file mode 100644
index 000000000..6538bea3c
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/property/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位符,无时间作用,避免 package 缩进
+ */
+package cn.iocoder.yudao.module.product.controller.app.property.vo.property;
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/value/AppProductPropertyValueDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/value/AppProductPropertyValueDetailRespVO.java
new file mode 100644
index 000000000..516dd077f
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/property/vo/value/AppProductPropertyValueDetailRespVO.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.product.controller.app.property.vo.value;
+
+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-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http
new file mode 100644
index 000000000..04df7bfec
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http
@@ -0,0 +1,8 @@
+### 获得订单交易的分页 TODO
+GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10
+Authorization: Bearer {{appToken}}
+tenant-id: {{appTenentId}}
+
+### 获得商品 SPU 明细
+GET {{appApi}}/product/spu/get-detail?id=4
+tenant-id: {{appTenentId}}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
index 289b89e1d..b2f8b495d 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
@@ -1,11 +1,19 @@
package cn.iocoder.yudao.module.product.controller.app.spu;
-import cn.hutool.core.bean.BeanUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-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.controller.app.spu.vo.AppSpuRespVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
+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.enums.spu.ProductSpuStatusEnum;
+import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
+import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
+import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -17,28 +25,54 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
+import java.util.List;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_ENABLE;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
-@Tag(name = "用户 APP - 商品spu")
+@Tag(name = "用户 APP - 商品spu")
@RestController
@RequestMapping("/product/spu")
@Validated
public class AppProductSpuController {
@Resource
- private ProductSpuService spuService;
+ private ProductSpuService productSpuService;
+ @Resource
+ private ProductSkuService productSkuService;
+ @Resource
+ private ProductPropertyValueService productPropertyValueService;
@GetMapping("/page")
- @Operation(summary = "获得商品spu分页")
- public CommonResult> getSpuPage(@Valid AppSpuPageReqVO pageVO) {
- return success(spuService.getSpuPage(pageVO));
+ @Operation(summary = "获得商品 SPU 分页")
+ public CommonResult> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
+ PageResult pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus());
+ return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult));
}
- @GetMapping("/")
- @Operation(summary = "获取商品 - 通过商品id")
- public CommonResult getSpu(@RequestParam("spuId") Long spuId) {
- AppSpuRespVO appSpuRespVO = BeanUtil.toBean(spuService.getSpu(spuId), AppSpuRespVO.class);
- return success(appSpuRespVO);
+ @GetMapping("/get-detail")
+ @Operation(summary = "获得商品 SPU 明细")
+ @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
+ public CommonResult getSpuDetail(@RequestParam("id") Long id) {
+ // 获得商品 SPU
+ ProductSpuDO spu = productSpuService.getSpu(id);
+ if (spu == null) {
+ throw exception(SPU_NOT_EXISTS);
+ }
+ if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
+ throw exception(SPU_NOT_ENABLE);
+ }
+
+ // 查询商品 SKU
+ List skus = productSkuService.getSkuListBySpuIdAndStatus(spu.getId(),
+ CommonStatusEnum.ENABLE.getStatus());
+ // 查询商品属性
+ List propertyValues = productPropertyValueService
+ .getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
+ // 拼接
+ return success(ProductSpuConvert.INSTANCE.convert(spu, skus, propertyValues));
}
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java
new file mode 100644
index 000000000..699ca1caa
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java
@@ -0,0 +1,92 @@
+package cn.iocoder.yudao.module.product.controller.app.spu.vo;
+
+import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@ApiModel("用户 App - 商品 SPU 明细 Response VO")
+@Data
+public class AppProductSpuDetailRespVO {
+
+ @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
+ private Long id;
+
+ // ========== 基本信息 =========
+
+ @ApiModelProperty(value = "商品名称", required = true, example = "芋道")
+ private String name;
+
+ @ApiModelProperty(value = "促销语", example = "好吃!")
+ private String sellPoint;
+
+ @ApiModelProperty(value = "商品详情", required = true, example = "我是商品描述")
+ private String description;
+
+ @ApiModelProperty(value = "商品分类编号", required = true, example = "1")
+ private Long categoryId;
+
+ @ApiModelProperty(value = "商品图片的数组", required = true)
+ private List picUrls;
+
+ @ApiModelProperty(value = "商品视频", required = true)
+ private String videoUrl;
+
+ // ========== SKU 相关字段 =========
+
+ @ApiModelProperty(value = "规格类型", required = true, example = "1", notes = "参见 ProductSpuSpecTypeEnum 枚举类")
+ private Integer specType;
+
+ @ApiModelProperty(value = "是否展示库存", required = true, example = "true")
+ private Boolean showStock;
+
+ @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
+ private Integer minPrice;
+
+ @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
+ private Integer maxPrice;
+
+ /**
+ * SKU 数组
+ */
+ private List skus;
+
+ // ========== 统计相关字段 =========
+
+ @ApiModelProperty(value = "商品销量", required = true, example = "1024")
+ private Integer salesCount;
+
+ @ApiModel("用户 App - 商品 SPU 明细的 SKU 信息")
+ @Data
+ public static class Sku {
+
+ @ApiModelProperty(value = "商品 SKU 编号", example = "1")
+ private Long id;
+
+ /**
+ * 商品属性数组
+ */
+ private List properties;
+
+ @ApiModelProperty(value = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分")
+ private Integer price;
+
+ @ApiModelProperty(value = "市场价", example = "1024", notes = "单位:分")
+ private Integer marketPrice;
+
+ @ApiModelProperty(value = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png")
+ private String picUrl;
+
+ @ApiModelProperty(value = "库存", required = true, example = "1")
+ private Integer stock;
+
+ @ApiModelProperty(value = "商品重量", example = "1", notes = "单位:kg 千克")
+ private Double weight;
+
+ @ApiModelProperty(value = "商品体积", example = "1024", notes = "单位:m^3 平米")
+ private Double volume;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java
new file mode 100644
index 000000000..60fc9235e
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.product.controller.app.spu.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@ApiModel("用户 App - 商品 SPU 分页项 Response VO")
+@Data
+public class AppProductSpuPageItemRespVO {
+
+ @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
+ private Long id;
+
+ @ApiModelProperty(value = "商品名称", required = true, example = "芋道")
+ @NotEmpty(message = "商品名称不能为空")
+ private String name;
+
+ @ApiModelProperty(value = "分类编号", required = true)
+ @NotNull(message = "分类编号不能为空")
+ private Long categoryId;
+
+ @ApiModelProperty(value = "商品图片的数组", required = true)
+ private List picUrls;
+
+ @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
+ private Integer minPrice;
+
+ @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
+ private Integer maxPrice;
+
+ // ========== 统计相关字段 =========
+
+ @ApiModelProperty(value = "商品销量", example = "1024")
+ private Integer salesCount;
+
+}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java
new file mode 100644
index 000000000..f19a7c21a
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.product.controller.app.spu.vo;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.AssertTrue;
+
+@ApiModel("用户 App - 商品 SPU 分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppProductSpuPageReqVO extends PageParam {
+
+ public static final String SORT_FIELD_PRICE = "price";
+ public static final String SORT_FIELD_SALES_COUNT = "salesCount";
+
+ @ApiModelProperty(value = "分类编号", example = "1")
+ private Long categoryId;
+
+ @ApiModelProperty(value = "关键字", example = "好看")
+ private String keyword;
+
+ @ApiModelProperty(value = "排序字段", example = "price", notes = "参见 AppSpuPageReqVO.SORT_FIELD_XXX 常量")
+ private String sortField;
+
+ @ApiModelProperty(value = "排序方式", example = "true", notes = "true - 升序;false - 降序")
+ private Boolean sortAsc;
+
+ @AssertTrue(message = "排序字段不合法")
+ @JsonIgnore
+ public boolean isSortFieldValid() {
+ if (StrUtil.isEmpty(sortField)) {
+ return true;
+ }
+ return StrUtil.equalsAny(sortField, SORT_FIELD_PRICE, SORT_FIELD_SALES_COUNT);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java
index fb67a1708..e69de29bb 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java
@@ -1,17 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.app.spu.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-@Schema(description = "App - 商品spu分页 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class AppSpuPageReqVO extends PageParam {
-
- @Schema(description = "分类id")
- private Long categoryId;
-}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java
index 20c82afbc..e69de29bb 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java
@@ -1,51 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.app.spu.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-import java.util.List;
-
-@Schema(description = "App - 商品spu分页 Request VO")
-@Data
-public class AppSpuPageRespVO {
-
- @Schema(description = "主键", required = true, example = "1")
- private Long id;
-
- @Schema(description = "商品名称")
- private String name;
-
- @Schema(description = "卖点", required = true)
- @NotNull(message = "卖点不能为空")
- private String sellPoint;
-
- @Schema(description = "描述", required = true)
- @NotNull(message = "描述不能为空")
- private String description;
-
- @Schema(description = "分类id", required = true)
- @NotNull(message = "分类id不能为空")
- private Long categoryId;
-
- @Schema(description = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true)
- @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空")
- private List picUrls;
-
- @Schema(description = "排序字段", required = true)
- @NotNull(message = "排序字段不能为空")
- private Integer sort;
-
- @Schema(description = "点赞初始人数")
- private Integer likeCount;
-
- @Schema(description = "价格 单位使用:分")
- private Integer price;
-
- @Schema(description = "库存数量")
- private Integer quantity;
-
- @Schema(description = "上下架状态: 0 上架(开启) 1 下架(禁用)")
- private Integer status;
-
-}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java
index 79072074b..e69de29bb 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java
@@ -1,21 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.app.spu.vo;
-
-import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-/**
- *
- *
- *
- *
- * @author LuoWenFeng
- */
-@Schema(description = "App - 商品spu Response VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class AppSpuRespVO extends ProductSpuRespVO {
-
-
-}
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 91f811ad6..211bcc293 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
@@ -1,20 +1,21 @@
package cn.iocoder.yudao.module.product.convert.property;
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.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;
import java.util.List;
+import java.util.Map;
/**
- * 规格名称 Convert
+ * 属性项 Convert
*
* @author 芋道源码
*/
@@ -27,12 +28,21 @@ public interface ProductPropertyConvert {
ProductPropertyDO convert(ProductPropertyUpdateReqVO bean);
- ProductPropertyAndValueRespVO convert(ProductPropertyRespVO bean);
-
ProductPropertyRespVO convert(ProductPropertyDO bean);
List convertList(List list);
PageResult convertPage(PageResult page);
+ default List convertList(List keys, List values) {
+ Map> valueMap = CollectionUtils.convertMultiMap(values, ProductPropertyValueDO::getPropertyId);
+ return CollectionUtils.convertList(keys, key -> {
+ ProductPropertyAndValueRespVO respVO = convert02(key);
+ respVO.setValues(convertList02(valueMap.get(key.getId())));
+ return respVO;
+ });
+ }
+ ProductPropertyAndValueRespVO convert02(ProductPropertyDO bean);
+ List convertList02(List list);
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java
index 331ad623f..d6167c174 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java
@@ -1,18 +1,25 @@
package cn.iocoder.yudao.module.product.convert.propertyvalue;
-import java.util.*;
-
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.product.api.property.dto.ProductPropertyValueDetailRespDTO;
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.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
+import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+
/**
- * 规格值 Convert
+ * 属性值 Convert
*
* @author 芋道源码
*/
@@ -31,6 +38,18 @@ public interface ProductPropertyValueConvert {
PageResult convertPage(PageResult page);
- List convertList03(List list);
+ default List convertList(List values, List keys) {
+ Map keyMap = convertMap(keys, ProductPropertyDO::getId);
+ return CollectionUtils.convertList(values, value -> {
+ ProductPropertyValueDetailRespBO valueDetail = new ProductPropertyValueDetailRespBO()
+ .setValueId(value.getId()).setValueName(value.getName());
+ // 设置属性项
+ MapUtils.findAndThen(keyMap, value.getPropertyId(),
+ key -> valueDetail.setPropertyId(key.getId()).setPropertyName(key.getName()));
+ return valueDetail;
+ });
+ }
+
+ List convertList02(List list);
}
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 b29d612be..f397dfc48 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,5 +1,7 @@
package cn.iocoder.yudao.module.product.convert.sku;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
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;
@@ -10,9 +12,8 @@ import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -32,12 +33,16 @@ public interface ProductSkuConvert {
List convertList(List list);
- List convertSkuDOList(List list);
+ List convertList06(List list);
+
+ default List convertList06(List list, Long spuId, String spuName) {
+ List result = convertList06(list);
+ result.forEach(item -> item.setSpuId(spuId).setSpuName(spuName));
+ return result;
+ }
ProductSkuRespDTO convert02(ProductSkuDO bean);
- List convertList02(List list);
-
List convertList03(List list);
List convertList04(List list);
@@ -66,4 +71,23 @@ public interface ProductSkuConvert {
return spuIdAndStockMap;
}
+ default Collection convertPropertyValueIds(List list) {
+ if (CollUtil.isEmpty(list)) {
+ return new HashSet<>();
+ }
+ return list.stream().filter(item -> item.getProperties() != null)
+ .flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性
+ .map(ProductSkuDO.Property::getValueId) // 将每个 Property 转换成对应的 propertyId,最后形成集合
+ .collect(Collectors.toSet());
+ }
+
+ default String buildPropertyKey(ProductSkuDO bean) {
+ if (CollUtil.isEmpty(bean.getProperties())) {
+ return StrUtil.EMPTY;
+ }
+ List properties = new ArrayList<>(bean.getProperties());
+ properties.sort(Comparator.comparing(ProductSkuDO.Property::getValueId));
+ return properties.stream().map(m -> String.valueOf(m.getValueId())).collect(Collectors.joining());
+ }
+
}
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 98b7d8837..fcf6d6436 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,18 +1,29 @@
package cn.iocoder.yudao.module.product.convert.spu;
+import cn.hutool.core.collection.CollUtil;
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.property.vo.value.ProductPropertyValueDetailRespVO;
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.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
+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.property.bo.ProductPropertyValueDetailRespBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+
+import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
- * 商品spu Convert
+ * 商品 SPU Convert
*
* @author 芋道源码
*/
@@ -25,18 +36,73 @@ public interface ProductSpuConvert {
ProductSpuDO convert(ProductSpuUpdateReqVO bean);
- ProductSpuRespVO convert(ProductSpuDO bean);
-
- List convertList(List list);
+ List convertList(List list);
PageResult convertPage(PageResult page);
- ProductSpuPageReqVO convert(AppSpuPageReqVO bean);
-
- AppSpuPageRespVO convertAppResp(ProductSpuDO list);
+ ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean);
List convertList2(List list);
List convertList02(List list);
+ default AppProductSpuDetailRespVO convert(ProductSpuDO spu, List skus,
+ List propertyValues) {
+ AppProductSpuDetailRespVO spuVO = convert02(spu)
+ .setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0));
+ spuVO.setSkus(convertList03(skus));
+ // 处理商品属性
+ Map propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
+ for (int i = 0; i < skus.size(); i++) {
+ List properties = skus.get(i).getProperties();
+ if (CollUtil.isEmpty(properties)) {
+ continue;
+ }
+ AppProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
+ sku.setProperties(new ArrayList<>(properties.size()));
+ // 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
+ properties.forEach(property -> {
+ ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
+ if (propertyValue == null) {
+ return;
+ }
+ sku.getProperties().add(convert03(propertyValue));
+ });
+ }
+ return spuVO;
+ }
+ AppProductSpuDetailRespVO convert02(ProductSpuDO spu);
+ List convertList03(List skus);
+ AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
+
+ PageResult convertPage02(PageResult page);
+
+ default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List skus,
+ List propertyValues) {
+ ProductSpuDetailRespVO spuVO = convert03(spu);
+ spuVO.setSkus(convertList04(skus));
+ // 处理商品属性
+ Map propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
+ for (int i = 0; i < skus.size(); i++) {
+ List properties = skus.get(i).getProperties();
+ if (CollUtil.isEmpty(properties)) {
+ continue;
+ }
+ ProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
+ sku.setProperties(new ArrayList<>(properties.size()));
+ // 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
+ properties.forEach(property -> {
+ ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
+ if (propertyValue == null) {
+ return;
+ }
+ sku.getProperties().add(convert04(propertyValue));
+ });
+ }
+ return spuVO;
+ }
+ ProductSpuDetailRespVO convert03(ProductSpuDO spu);
+ List convertList04(List skus);
+ ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
+
}
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 b3831491e..2976674c1 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
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.product.dal.dataobject.property;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -8,7 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
- * 规格名称 DO
+ * 商品属性项 DO
*
* @author 芋道源码
*/
@@ -28,20 +27,12 @@ public class ProductPropertyDO extends BaseDO {
@TableId
private Long id;
/**
- * 规格名称
+ * 名称
*/
private String name;
- /**
- * 状态
- *
- * 枚举 {@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 b75f0d592..d73fe06b2 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
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.product.dal.dataobject.property;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -9,7 +8,7 @@ import lombok.*;
/**
- * 规格值 DO
+ * 商品属性值 DO
*
* @author 芋道源码
*/
@@ -29,21 +28,15 @@ public class ProductPropertyValueDO extends BaseDO {
@TableId
private Long id;
/**
- * 规格键编号
+ * 属性项的编号
*
* 关联 {@link ProductPropertyDO#getId()}
*/
private Long propertyId;
/**
- * 规格值名字
+ * 名称
*/
private String name;
- /**
- * 状态
- *
- * 枚举 {@link CommonStatusEnum}
- */
- private Integer status;
/**
* 备注
*
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 f953534ed..3836f20e5 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
@@ -35,10 +35,6 @@ public class ProductSkuDO extends BaseDO {
*/
@TableId
private Long id;
- /**
- * 商品 SKU 名字
- */
- private String name;
/**
* SPU 编号
*
@@ -46,7 +42,13 @@ public class ProductSkuDO extends BaseDO {
*/
private Long spuId;
/**
- * 规格值数组,JSON 格式
+ * SPU 名字
+ *
+ * 冗余 {@link ProductSkuDO#getSpuName()}
+ */
+ private String spuName;
+ /**
+ * 属性数组,JSON 格式
*/
@TableField(typeHandler = PropertyTypeHandler.class)
private List properties;
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 890df3477..26f8d5239 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
@@ -3,31 +3,30 @@ 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.property.ProductPropertyListReqVO;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
-/**
- * 规格名称 Mapper
- *
- * @author 芋道源码
- */
@Mapper
public interface ProductPropertyMapper extends BaseMapperX {
default PageResult selectPage(ProductPropertyPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX()
.likeIfPresent(ProductPropertyDO::getName, reqVO.getName())
- .eqIfPresent(ProductPropertyDO::getStatus, reqVO.getStatus())
.betweenIfPresent(ProductPropertyDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProductPropertyDO::getId));
}
default ProductPropertyDO selectByName(String name) {
- return selectOne(new LambdaQueryWrapperX()
- .eqIfPresent(ProductPropertyDO::getName, name));
+ return selectOne(ProductPropertyDO::getName, name);
+ }
+
+ default List selectList(ProductPropertyListReqVO listReqVO) {
+ return selectList(new LambdaQueryWrapperX()
+ .eqIfPresent(ProductPropertyDO::getName, listReqVO.getName()));
}
}
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 ca9bafab2..402df51e7 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
@@ -7,17 +7,13 @@ import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.Produc
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import org.apache.ibatis.annotations.Mapper;
+import java.util.Collection;
import java.util.List;
-/**
- * 规格值 Mapper
- *
- * @author 芋道源码
- */
@Mapper
public interface ProductPropertyValueMapper extends BaseMapperX {
- default List selectListByPropertyId(List propertyIds) {
+ default List selectListByPropertyId(Collection propertyIds) {
return selectList(new LambdaQueryWrapperX()
.inIfPresent(ProductPropertyValueDO::getPropertyId, propertyIds));
}
@@ -28,17 +24,20 @@ public interface ProductPropertyValueMapper extends BaseMapperX().eq(ProductPropertyValueDO::getPropertyId, propertyId)
- .eq(ProductPropertyValueDO::getDeleted, false));
+ default void deleteByPropertyId(Long propertyId) {
+ delete(new LambdaQueryWrapperX()
+ .eq(ProductPropertyValueDO::getPropertyId, propertyId));
}
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));
}
+ default Integer selectCountByPropertyId(Long propertyId) {
+ return selectCount(ProductPropertyValueDO::getPropertyId, propertyId).intValue();
+ }
+
}
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 3e5a9ce8f..56bc54499 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
@@ -8,6 +8,7 @@ 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.Collection;
import java.util.List;
/**
@@ -22,6 +23,17 @@ public interface ProductSkuMapper extends BaseMapperX {
return selectList(ProductSkuDO::getSpuId, spuId);
}
+ default List selectListBySpuIdAndStatus(Long spuId,
+ Integer status) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProductSkuDO::getSpuId, spuId)
+ .eqIfPresent(ProductSkuDO::getStatus, status));
+ }
+
+ default List selectListBySpuId(Collection spuIds) {
+ return selectList(ProductSkuDO::getSpuId, spuIds);
+ }
+
default void deleteBySpuId(Long spuId) {
delete(new LambdaQueryWrapperX().eq(ProductSkuDO::getSpuId, spuId));
}
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 a271b5051..57a3125d8 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
@@ -4,10 +4,12 @@ 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.spu.vo.ProductSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
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.Objects;
import java.util.Set;
/**
@@ -44,6 +46,19 @@ public interface ProductSpuMapper extends BaseMapperX {
.orderByDesc(ProductSpuDO::getSort));
}
+ default PageResult selectPage(AppProductSpuPageReqVO pageReqVO, Integer status) {
+ LambdaQueryWrapperX query = new LambdaQueryWrapperX()
+ .eqIfPresent(ProductSpuDO::getCategoryId, pageReqVO.getCategoryId())
+ .eqIfPresent(ProductSpuDO::getStatus, status);
+ // 排序逻辑
+ if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) {
+ query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getMaxPrice);
+ } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {
+ query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getSalesCount);
+ }
+ return selectPage(pageReqVO, query);
+ }
+
/**
* 更新商品 SPU 库存
*
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java
index 117f35931..01967857e 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java
@@ -2,7 +2,7 @@
* trade 模块,主要实现交易相关功能
* 例如:订单、退款、购物车等功能。
*
- * 1. Controller URL:以 /trade/ 开头,避免和其它 Module 冲突
- * 2. DataObject 表名:以 trade_ 开头,方便在数据库中区分
+ * 1. Controller URL:以 /product/ 开头,避免和其它 Module 冲突
+ * 2. DataObject 表名:以 product_ 开头,方便在数据库中区分
*/
package cn.iocoder.yudao.module.product;
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java
index 118e8118b..32a4c030d 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java
@@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCateg
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
import javax.validation.Valid;
-import java.util.Collection;
import java.util.List;
/**
@@ -47,12 +46,19 @@ public interface ProductCategoryService {
ProductCategoryDO getCategory(Long id);
/**
- * 获得商品分类列表
+ * 校验商品分类
*
- * @param ids 编号
- * @return 商品分类列表
+ * @param id 分类编号
*/
- List getEnableCategoryList(Collection ids);
+ void validateCategory(Long id);
+
+ /**
+ * 获得商品分类的层级
+ *
+ * @param id 编号
+ * @return 商品分类的层级
+ */
+ Integer getCategoryLevel(Long id);
/**
* 获得商品分类列表
@@ -62,14 +68,6 @@ public interface ProductCategoryService {
*/
List getEnableCategoryList(ProductCategoryListReqVO listReqVO);
- /**
- * 验证选择的商品分类的级别是否合法
- * 例如说,商品发布的时候,必须在第 3 级别
- *
- * @param id 分类编号
- */
- void validateCategoryLevel(Long id);
-
/**
* 获得开启状态的商品分类列表
*
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 ffe55e9bf..f0d10b8b8 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
@@ -11,7 +11,6 @@ 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.Objects;
@@ -90,31 +89,40 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
}
}
- @Override
- public void validateCategoryLevel(Long id) {
- // 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);
- }
- }
-
@Override
public ProductCategoryDO getCategory(Long id) {
return productCategoryMapper.selectById(id);
}
@Override
- public List getEnableCategoryList(Collection ids) {
- return productCategoryMapper.selectBatchIds(ids);
+ public void validateCategory(Long id) {
+ ProductCategoryDO category = productCategoryMapper.selectById(id);
+ if (category == null) {
+ throw exception(CATEGORY_NOT_EXISTS);
+ }
+ if (Objects.equals(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
+ throw exception(CATEGORY_DISABLED, category.getName());
+ }
+ }
+
+ @Override
+ public Integer getCategoryLevel(Long id) {
+ if (Objects.equals(id, ProductCategoryDO.PARENT_ID_NULL)) {
+ return 0;
+ }
+ int level = 1;
+ for (int i = 0; i < 100; i++) {
+ ProductCategoryDO category = productCategoryMapper.selectById(id);
+ // 如果没有父节点,break 结束
+ if (category == null
+ || Objects.equals(category.getParentId(), ProductCategoryDO.PARENT_ID_NULL)) {
+ break;
+ }
+ // 继续递归父节点
+ level++;
+ id = category.getParentId();
+ }
+ return level;
}
@Override
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 8780c7865..564bc82a9 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,6 @@
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.ProductPropertyRespVO;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
@@ -10,14 +9,15 @@ import java.util.Collection;
import java.util.List;
/**
- * 规格名称 Service 接口
+ * 商品属性项 Service 接口
*
* @author 芋道源码
*/
public interface ProductPropertyService {
/**
- * 创建规格名称
+ * 创建属性项
+ * 注意,如果已经存在该属性项,直接返回它的编号即可
*
* @param createReqVO 创建信息
* @return 编号
@@ -25,56 +25,48 @@ public interface ProductPropertyService {
Long createProperty(@Valid ProductPropertyCreateReqVO createReqVO);
/**
- * 更新规格名称
+ * 更新属性项
*
* @param updateReqVO 更新信息
*/
void updateProperty(@Valid ProductPropertyUpdateReqVO updateReqVO);
/**
- * 删除规格名称
+ * 删除属性项
*
* @param id 编号
*/
void deleteProperty(Long id);
/**
- * 获得规格名称列表
+ * 获得属性项列表
* @param listReqVO 集合查询
- * @return 规格名称集合
+ * @return 属性项集合
*/
- List getPropertyList(ProductPropertyListReqVO listReqVO);
+ List getPropertyList(ProductPropertyListReqVO listReqVO);
/**
* 获取属性名称分页
*
* @param pageReqVO 分页条件
- * @return 规格名称分页
+ * @return 属性项分页
*/
- PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO);
+ PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO);
/**
- * 获得指定编号的规格名称
+ * 获得指定编号的属性项
*
* @param id 编号
- * @return 规格名称
+ * @return 属性项
*/
- ProductPropertyRespVO getProperty(Long id);
+ ProductPropertyDO getProperty(Long id);
/**
- * 根据规格属性编号的集合,获得对应的规格 + 规格值的集合
+ * 根据属性项的编号的集合,获得对应的属性项数组
*
- * @param ids 规格编号的集合
- * @return 对应的规格
+ * @param ids 属性项的编号的集合
+ * @return 属性项数组
*/
- List getPropertyList(Collection ids);
-
- /**
- * 获得规格名称 + 值的列表
- *
- * @param listReqVO 列表查询
- * @return 规格名称 + 值的列表
- */
- List getPropertyAndValueList(ProductPropertyListReqVO listReqVO);
+ List getPropertyList(Collection ids);
}
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 74e370f9c..328c343d6 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,16 +1,15 @@
package cn.iocoder.yudao.module.product.service.property;
+import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-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.property.ProductPropertyCreateReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyListReqVO;
+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.convert.property.ProductPropertyConvert;
-import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
-import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyMapper;
-import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -18,15 +17,12 @@ 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.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;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
/**
- * 规格名称 Service 实现类
+ * 商品属性项 Service 实现类
*
* @author 芋道源码
*/
@@ -38,15 +34,18 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
private ProductPropertyMapper productPropertyMapper;
@Resource
- private ProductPropertyValueMapper productPropertyValueMapper;
+ @Lazy // 延迟加载,解决循环依赖问题
+ private ProductPropertyValueService productPropertyValueService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createProperty(ProductPropertyCreateReqVO createReqVO) {
- // 校验存在
- if (productPropertyMapper.selectByName(createReqVO.getName()) != null) {
- throw exception(PROPERTY_EXISTS);
+ // 如果已经添加过该属性项,直接返回
+ ProductPropertyDO dbProperty = productPropertyMapper.selectByName(createReqVO.getName());
+ if (dbProperty != null) {
+ return dbProperty.getId();
}
+
// 插入
ProductPropertyDO property = ProductPropertyConvert.INSTANCE.convert(createReqVO);
productPropertyMapper.insert(property);
@@ -57,12 +56,14 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProperty(ProductPropertyUpdateReqVO updateReqVO) {
- // 校验存在
- this.validatePropertyExists(updateReqVO.getId());
+ validatePropertyExists(updateReqVO.getId());
+ // 校验名字重复
ProductPropertyDO productPropertyDO = productPropertyMapper.selectByName(updateReqVO.getName());
- if (productPropertyDO != null && !productPropertyDO.getId().equals(updateReqVO.getId())) {
+ if (productPropertyDO != null &&
+ ObjUtil.notEqual(productPropertyDO.getId(), updateReqVO.getId())) {
throw exception(PROPERTY_EXISTS);
}
+
// 更新
ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO);
productPropertyMapper.updateById(updateObj);
@@ -71,11 +72,16 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
@Override
public void deleteProperty(Long id) {
// 校验存在
- this.validatePropertyExists(id);
+ validatePropertyExists(id);
+ // 校验其下是否有规格值
+ if (productPropertyValueService.getPropertyValueCountByPropertyId(id) > 0) {
+ throw exception(PROPERTY_DELETE_FAIL_VALUE_EXISTS);
+ }
+
// 删除
productPropertyMapper.deleteById(id);
- //同步删除属性值
- productPropertyValueMapper.deletePropertyValueByPropertyId(id);
+ // 同步删除属性值
+ productPropertyValueService.deletePropertyValueByPropertyId(id);
}
private void validatePropertyExists(Long id) {
@@ -85,41 +91,23 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
}
@Override
- public List getPropertyList(ProductPropertyListReqVO listReqVO) {
- return ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectList(new LambdaQueryWrapperX()
- .likeIfPresent(ProductPropertyDO::getName, listReqVO.getName())
- .eqIfPresent(ProductPropertyDO::getStatus, listReqVO.getStatus())));
+ public List getPropertyList(ProductPropertyListReqVO listReqVO) {
+ return productPropertyMapper.selectList(listReqVO);
}
@Override
- public PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO) {
- //获取属性列表
- PageResult pageResult = productPropertyMapper.selectPage(pageReqVO);
- return ProductPropertyConvert.INSTANCE.convertPage(pageResult);
+ public PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO) {
+ return productPropertyMapper.selectPage(pageReqVO);
}
@Override
- public ProductPropertyRespVO getProperty(Long id) {
- ProductPropertyDO property = productPropertyMapper.selectById(id);
- return ProductPropertyConvert.INSTANCE.convert(property);
+ public ProductPropertyDO getProperty(Long id) {
+ return productPropertyMapper.selectById(id);
}
@Override
- public List getPropertyList(Collection ids) {
- return ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(ids));
+ public List getPropertyList(Collection ids) {
+ return 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;
- });
- }
}
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
index 9dcbd72e7..553e2578d 100644
--- 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
@@ -3,22 +3,23 @@ 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.dal.dataobject.property.ProductPropertyValueDO;
+import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
+import java.util.Collection;
import java.util.List;
/**
- *
- * 规格值 Service 接口
- *
+ * 商品属性值 Service 接口
*
* @author LuoWenFeng
*/
public interface ProductPropertyValueService {
/**
- * 创建规格值
+ * 创建属性值
+ * 注意,如果已经存在该属性值,直接返回它的编号即可
*
* @param createReqVO 创建信息
* @return 编号
@@ -26,40 +27,64 @@ public interface ProductPropertyValueService {
Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO);
/**
- * 更新规格值
+ * 更新属性值
*
* @param updateReqVO 更新信息
*/
void updatePropertyValue(ProductPropertyValueUpdateReqVO updateReqVO);
/**
- * 删除规格值
+ * 删除属性值
*
* @param id 编号
*/
void deletePropertyValue(Long id);
/**
- * 获得规格值
+ * 获得属性值
*
* @param id 编号
- * @return 规格名称
+ * @return 属性值
*/
- ProductPropertyValueRespVO getPropertyValue(Long id);
+ ProductPropertyValueDO getPropertyValue(Long id);
/**
- * 获得规格值
+ * 根据属性项编号数组,获得属性值列表
*
- * @param id 编号
- * @return 规格名称
+ * @param propertyIds 属性项目编号数组
+ * @return 属性值列表
*/
- List getPropertyValueListByPropertyId(List id);
+ List getPropertyValueListByPropertyId(Collection propertyIds);
/**
- * 获取规格值 分页
+ * 根据编号数组,获得属性值列表
+ *
+ * @param ids 编号数组
+ * @return 属性值明细列表
+ */
+ List getPropertyValueDetailList(Collection ids);
+
+ /**
+ * 根据属性项编号,活的属性值数量
+ *
+ * @param propertyId 属性项编号数
+ * @return 属性值数量
+ */
+ Integer getPropertyValueCountByPropertyId(Long propertyId);
+
+ /**
+ * 获取属性值的分页
*
* @param pageReqVO 查询条件
- * @return
+ * @return 属性值的分页
*/
- PageResult getPropertyValueListPage(ProductPropertyValuePageReqVO pageReqVO);
+ PageResult getPropertyValuePage(ProductPropertyValuePageReqVO pageReqVO);
+
+ /**
+ * 删除指定属性项编号下的属性值们
+ *
+ * @param propertyId 属性项的编号
+ */
+ void deletePropertyValueByPropertyId(Long propertyId);
+
}
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
index 5addb37e8..e5bc6874b 100644
--- 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
@@ -1,26 +1,31 @@
package cn.iocoder.yudao.module.product.service.property;
+import cn.hutool.core.collection.CollUtil;
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.ProductPropertyDO;
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 cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
+import org.springframework.context.annotation.Lazy;
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;
-import java.util.Objects;
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.PROPERTY_VALUE_EXISTS;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS;
/**
- * 规格值 Service 实现类
+ * 商品属性值 Service 实现类
*
* @author LuoWenFeng
*/
@@ -31,45 +36,92 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
@Resource
private ProductPropertyValueMapper productPropertyValueMapper;
+ @Resource
+ @Lazy // 延迟加载,避免循环依赖
+ private ProductPropertyService productPropertyService;
+
@Override
public Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO) {
- if (productPropertyValueMapper.selectByName(createReqVO.getPropertyId(), createReqVO.getName()) != null) {
- throw exception(PROPERTY_VALUE_EXISTS);
+ // 如果已经添加过该属性值,直接返回
+ ProductPropertyValueDO dbValue = productPropertyValueMapper.selectByName(
+ createReqVO.getPropertyId(), createReqVO.getName());
+ if (dbValue != null) {
+ return dbValue.getId();
}
- ProductPropertyValueDO convert = ProductPropertyValueConvert.INSTANCE.convert(createReqVO);
- productPropertyValueMapper.insert(convert);
- return convert.getId();
+
+ // 新增
+ ProductPropertyValueDO value = ProductPropertyValueConvert.INSTANCE.convert(createReqVO);
+ productPropertyValueMapper.insert(value);
+ return value.getId();
}
@Override
public void updatePropertyValue(ProductPropertyValueUpdateReqVO updateReqVO) {
- ProductPropertyValueDO productPropertyValueDO = productPropertyValueMapper.selectByName(updateReqVO.getPropertyId(), updateReqVO.getName());
+ validatePropertyValueExists(updateReqVO.getId());
+ // 校验名字唯一
+ 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);
+
+ // 更新
+ ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO);
+ productPropertyValueMapper.updateById(updateObj);
}
@Override
public void deletePropertyValue(Long id) {
+ validatePropertyValueExists(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);
+ private void validatePropertyValueExists(Long id) {
+ if (productPropertyValueMapper.selectById(id) == null) {
+ throw exception(PROPERTY_VALUE_NOT_EXISTS);
+ }
}
@Override
- public List getPropertyValueListByPropertyId(List id) {
- return ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueMapper.selectList("property_id", id));
+ public ProductPropertyValueDO getPropertyValue(Long id) {
+ return productPropertyValueMapper.selectById(id);
}
@Override
- public PageResult getPropertyValueListPage(ProductPropertyValuePageReqVO pageReqVO) {
- return ProductPropertyValueConvert.INSTANCE.convertPage(productPropertyValueMapper.selectPage(pageReqVO));
+ public List getPropertyValueListByPropertyId(Collection propertyIds) {
+ return productPropertyValueMapper.selectListByPropertyId(propertyIds);
}
+
+ @Override
+ public List getPropertyValueDetailList(Collection ids) {
+ // 获得属性值列表
+ if (CollUtil.isEmpty(ids)) {
+ return Collections.emptyList();
+ }
+ List values = productPropertyValueMapper.selectBatchIds(ids);
+ if (CollUtil.isEmpty(values)) {
+ return Collections.emptyList();
+ }
+ // 获得属性项列表
+ List keys = productPropertyService.getPropertyList(
+ convertSet(values, ProductPropertyValueDO::getPropertyId));
+ // 组装明细
+ return ProductPropertyValueConvert.INSTANCE.convertList(values, keys);
+ }
+
+ @Override
+ public Integer getPropertyValueCountByPropertyId(Long propertyId) {
+ return productPropertyValueMapper.selectCountByPropertyId(propertyId);
+ }
+
+ @Override
+ public PageResult getPropertyValuePage(ProductPropertyValuePageReqVO pageReqVO) {
+ return productPropertyValueMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public void deletePropertyValueByPropertyId(Long propertyId) {
+ productPropertyValueMapper.deleteByPropertyId(propertyId);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/bo/ProductPropertyValueDetailRespBO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/bo/ProductPropertyValueDetailRespBO.java
new file mode 100644
index 000000000..6776731f9
--- /dev/null
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/bo/ProductPropertyValueDetailRespBO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.product.service.property.bo;
+
+import lombok.Data;
+
+/**
+ * 商品属性项的明细 Response BO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ProductPropertyValueDetailRespBO {
+
+ /**
+ * 属性的编号
+ */
+ private Long propertyId;
+
+ /**
+ * 属性的名称
+ */
+ private String propertyName;
+
+ /**
+ * 属性值的编号
+ */
+ private Long valueId;
+
+ /**
+ * 属性值的名称
+ */
+ private String valueName;
+
+}
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 1036d8348..621e12d9f 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
@@ -3,6 +3,7 @@ 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 org.springframework.lang.Nullable;
import java.util.Collection;
import java.util.List;
@@ -49,23 +50,25 @@ public interface ProductSkuService {
*
* @param list sku组合的集合
*/
- void validateSkus(List list, Integer specType);
+ void validateSkuList(List list, Integer specType);
/**
* 批量创建 SKU
*
* @param spuId 商品 SPU 编号
+ * @param spuName 商品 SPU 名称
* @param list SKU 对象集合
*/
- void createSkus(Long spuId, List list);
+ void createSkuList(Long spuId, String spuName, List list);
/**
* 根据 SPU 编号,批量更新它的 SKU 信息
*
* @param spuId SPU 编码
+ * @param spuName 商品 SPU 名称
* @param skus SKU 的集合
*/
- void updateSkus(Long spuId, List skus);
+ void updateSkuList(Long spuId, String spuName, List skus);
/**
* 更新 SKU 库存(增量)
@@ -77,20 +80,30 @@ public interface ProductSkuService {
void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO);
/**
- * 获得商品 sku 集合
+ * 获得商品 SKU 集合
*
* @param spuId spu 编号
* @return 商品sku 集合
*/
- List getSkusBySpuId(Long spuId);
+ List getSkuListBySpuId(Long spuId);
/**
- * 获得 spu 对应的 sku 集合
+ * 基于 SPU 编号和状态,获得商品 SKU 集合
+ *
+ * @param spuId SPU 编号
+ * @param status 状态
+ * @return 商品 SKU 集合
+ */
+ List getSkuListBySpuIdAndStatus(Long spuId,
+ @Nullable Integer status);
+
+ /**
+ * 获得 spu 对应的 SKU 集合
*
* @param spuIds spu 编码集合
* @return 商品 sku 集合
*/
- List getSkusBySpuIds(List spuIds);
+ List getSkuListBySpuId(List spuIds);
/**
* 通过 spuId 删除 sku 信息
@@ -104,7 +117,6 @@ public interface ProductSkuService {
*
* @return SKU 数组
*/
- List getSkusByAlarmStock();
-
+ List getSkuListByAlarmStock();
}
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 2791ae5d3..1ab2523cc 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,13 +1,13 @@
package cn.iocoder.yudao.module.product.service.sku;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
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;
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants;
@@ -25,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.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
@@ -78,22 +79,24 @@ public class ProductSkuServiceImpl implements ProductSkuService {
}
@Override
- public void validateSkus(List skus, Integer specType) {
+ public void validateSkuList(List skus, Integer specType) {
// 非多规格,不需要校验
if (ObjectUtil.notEqual(specType, ProductSpuSpecTypeEnum.DISABLE.getType())) {
return;
}
- // 1、校验规格属性存在
- 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 propertyList = productPropertyService.getPropertyList(propertyIds);
+ // 1、校验属性项存在
+ Set propertyIds = skus.stream().filter(p -> p.getProperties() != null)
+ .flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性
+ .map(ProductSkuBaseVO.Property::getPropertyId) // 将每个 Property 转换成对应的 propertyId,最后形成集合
+ .collect(Collectors.toSet());
+ List propertyList = productPropertyService.getPropertyList(propertyIds);
if (propertyList.size() != propertyIds.size()) {
throw exception(PROPERTY_NOT_EXISTS);
}
- // 2. 校验,一个 SKU 下,没有重复的规格。校验方式是,遍历每个 SKU ,看看是否有重复的规格 propertyId
- Map propertyValueMap = CollectionUtils.convertMap(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(propertyIds)), ProductPropertyValueRespVO::getId);
+ // 2. 校验,一个 SKU 下,没有重复的属性。校验方式是,遍历每个 SKU ,看看是否有重复的属性 propertyId
+ Map propertyValueMap = convertMap(productPropertyValueService.getPropertyValueListByPropertyId(propertyIds), ProductPropertyValueDO::getId);
skus.forEach(sku -> {
Set skuPropertyIds = convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId());
if (skuPropertyIds.size() != sku.getProperties().size()) {
@@ -101,7 +104,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
}
});
- // 3. 再校验,每个 Sku 的规格值的数量,是一致的。
+ // 3. 再校验,每个 Sku 的属性值的数量,是一致的。
int attrValueIdsSize = skus.get(0).getProperties().size();
for (int i = 1; i < skus.size(); i++) {
if (attrValueIdsSize != skus.get(i).getProperties().size()) {
@@ -119,21 +122,23 @@ public class ProductSkuServiceImpl implements ProductSkuService {
}
@Override
- public void createSkus(Long spuId, List