frist commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yudao</artifactId>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
<description>
|
||||
商城大模块,由 product 商品、promotion 营销、trade 交易、statistics 统计等组成
|
||||
</description>
|
||||
<modules>
|
||||
<module>yudao-module-promotion-api</module>
|
||||
<module>yudao-module-promotion-biz</module>
|
||||
<module>yudao-module-product-api</module>
|
||||
<module>yudao-module-product-biz</module>
|
||||
<module>yudao-module-trade-api</module>
|
||||
<module>yudao-module-trade-biz</module>
|
||||
<module>yudao-module-statistics-api</module>
|
||||
<module>yudao-module-statistics-biz</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
product 模块 API,暴露给其它模块调用
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
|
||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 参数校验 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.product.api.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.product.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 商品分类")
|
||||
public interface ProductCategoryApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/category";
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@Operation(summary = "校验部门是否合法")
|
||||
@Parameter(name = "ids", description = "商品分类编号数组", example = "1,2", required = true)
|
||||
CommonResult<Boolean> validateCategoryList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.product.api.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 产品评论")
|
||||
public interface ProductCommentApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/comment";
|
||||
|
||||
@PostMapping(PREFIX + "/create")
|
||||
@Operation(summary = "创建评论")
|
||||
CommonResult<Long> createComment(@RequestBody @Valid ProductCommentCreateReqDTO createReqDTO);
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.product.api.comment.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "RPC 服务 - 商品评论创建 Request DTO")
|
||||
@Data
|
||||
public class ProductCommentCreateReqDTO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
@Schema(description = "订单编号", example = "223")
|
||||
private Long orderId;
|
||||
@Schema(description = "交易订单项编号", example = "666")
|
||||
private Long orderItemId;
|
||||
|
||||
@Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "描述星级不能为空")
|
||||
private Integer descriptionScores;
|
||||
@Schema(description = "服务星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "服务星级不能为空")
|
||||
private Integer benefitScores;
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "好评")
|
||||
@NotNull(message = "评论内容不能为空")
|
||||
private String content;
|
||||
@Schema(description = "评论图片地址数组,以逗号分隔最多上传 9 张", example = "https://www.iocoder.cn/xxx.jpg,http://www.iocoder.cn/yyy.jpg")
|
||||
private List<String> picUrls;
|
||||
|
||||
@Schema(description = "是否匿名", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否匿名不能为空")
|
||||
private Boolean anonymous;
|
||||
@Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
|
||||
@NotNull(message = "评价人不能为空")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.product.api;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.product.api.property.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "RPC 服务 - 商品属性项的明细 Response DTO")
|
||||
@Data
|
||||
public class ProductPropertyValueDetailRespDTO {
|
||||
|
||||
@Schema(description = "属性的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long propertyId;
|
||||
@Schema(description = "属性的名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色")
|
||||
private String propertyName;
|
||||
|
||||
@Schema(description = "属性值的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long valueId;
|
||||
@Schema(description = "属性值的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色")
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
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.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 商品 SKU")
|
||||
public interface ProductSkuApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/sku";
|
||||
|
||||
@GetMapping(PREFIX + "/get")
|
||||
@Operation(summary = "查询 SKU 信息")
|
||||
@Parameter(name = "id", description = "SKU 编号", required = true, example = "1024")
|
||||
CommonResult<ProductSkuRespDTO> getSku(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/list")
|
||||
@Operation(summary = "批量查询 SKU 信息")
|
||||
@Parameter(name = "ids", description = "SKU 编号列表", required = true, example = "1024,2048")
|
||||
CommonResult<List<ProductSkuRespDTO>> getSkuList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
@GetMapping(PREFIX + "/list-by-spu-id")
|
||||
@Operation(summary = "批量查询 SKU 信息")
|
||||
@Parameter(name = "spuIds", description = "SPU 编号列表", required = true, example = "1024,2048")
|
||||
CommonResult<List<ProductSkuRespDTO>> getSkuListBySpuId(@RequestParam("spuIds") Collection<Long> spuIds);
|
||||
|
||||
@PostMapping(PREFIX + "/update-stock")
|
||||
@Operation(summary = "更新 SKU 库存(增加 or 减少)")
|
||||
CommonResult<Boolean> updateSkuStock(@RequestBody @Valid ProductSkuUpdateStockReqDTO updateStockReqDTO);
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku.dto;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "RPC 服务 - 商品 SKU 信息 Response DTO")
|
||||
@Data
|
||||
public class ProductSkuRespDTO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
@Schema(description = "SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "属性数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ProductPropertyValueDetailRespDTO> properties;
|
||||
|
||||
@Schema(description = "销售价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer price;
|
||||
@Schema(description = "市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer marketPrice;
|
||||
@Schema(description = "成本价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "300")
|
||||
private Integer costPrice;
|
||||
@Schema(description = "SKU 的条形码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789")
|
||||
private String barCode;
|
||||
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer stock;
|
||||
@Schema(description = "商品重量,单位:kg 千克", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.5")
|
||||
private Double weight;
|
||||
@Schema(description = "商品体积,单位:m^3 平米", requiredMode = Schema.RequiredMode.REQUIRED, example = "3.0")
|
||||
private Double volume;
|
||||
|
||||
@Schema(description = "一级分销的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "550")
|
||||
private Integer firstBrokeragePrice;
|
||||
@Schema(description = "二级分销的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "250")
|
||||
private Integer secondBrokeragePrice;
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "RPC 服务 - 商品 SKU 更新库存 Request DTO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductSkuUpdateStockReqDTO {
|
||||
|
||||
@Schema(description = "商品 SKU 数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "商品 SKU 不能为空")
|
||||
private List<Item> items;
|
||||
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "库存变化数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
@NotNull(message = "库存变化数量不能为空")
|
||||
private Integer incrCount; // 正数:增加库存;负数:扣减库存
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.product.api.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 商品 SPU")
|
||||
public interface ProductSpuApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/spu";
|
||||
|
||||
@GetMapping(PREFIX + "/list")
|
||||
@Schema(description = "批量查询 SPU 数组")
|
||||
@Parameter(name = "ids", description = "SPU 编号列表", required = true, example = "1,3,5")
|
||||
CommonResult<List<ProductSpuRespDTO>> getSpuList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 批量查询 SPU MAP
|
||||
*
|
||||
* @param ids SPU 编号列表
|
||||
* @return SPU MAP
|
||||
*/
|
||||
default Map<Long, ProductSpuRespDTO> getSpusMap(Collection<Long> ids) {
|
||||
return convertMap(getSpuList(ids).getCheckedData(), ProductSpuRespDTO::getId);
|
||||
}
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@Schema(description = "批量查询 SPU 数组,并且校验是否 SPU 是否有效")
|
||||
@Parameter(name = "ids", description = "SPU 编号列表", required = true, example = "1,3,5")
|
||||
CommonResult<List<ProductSpuRespDTO>> validateSpuList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
@GetMapping(PREFIX + "/get")
|
||||
@Schema(description = "获得 SPU")
|
||||
@Parameter(name = "id", description = "SPU 编号", required = true, example = "1")
|
||||
CommonResult<ProductSpuRespDTO> getSpu(@RequestParam("id") Long id);
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package cn.iocoder.yudao.module.product.api.spu.dto;
|
||||
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// TODO @LeeYan9: ProductSpuRespDTO
|
||||
|
||||
/**
|
||||
* 商品 SPU 信息 Response DTO
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-08-26
|
||||
*/
|
||||
@Data
|
||||
public class ProductSpuRespDTO {
|
||||
|
||||
/**
|
||||
* 商品 SPU 编号,自增
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
// ========== 基本信息 =========
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 单位
|
||||
*
|
||||
* 对应 product_unit 数据字典
|
||||
*/
|
||||
private Integer unit;
|
||||
|
||||
/**
|
||||
* 商品分类编号
|
||||
*/
|
||||
private Long categoryId;
|
||||
/**
|
||||
* 商品封面图
|
||||
*/
|
||||
private String picUrl;
|
||||
|
||||
/**
|
||||
* 商品状态
|
||||
* <p>
|
||||
* 枚举 {@link ProductSpuStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
/**
|
||||
* 规格类型
|
||||
*
|
||||
* false - 单规格
|
||||
* true - 多规格
|
||||
*/
|
||||
private Boolean specType;
|
||||
/**
|
||||
* 商品价格,单位使用:分
|
||||
*/
|
||||
private Integer price;
|
||||
/**
|
||||
* 市场价,单位使用:分
|
||||
*/
|
||||
private Integer marketPrice;
|
||||
/**
|
||||
* 成本价,单位使用:分
|
||||
*/
|
||||
private Integer costPrice;
|
||||
/**
|
||||
* 库存
|
||||
*/
|
||||
private Integer stock;
|
||||
|
||||
// ========== 物流相关字段 =========
|
||||
|
||||
/**
|
||||
* 配送方式数组
|
||||
*
|
||||
* 对应 DeliveryTypeEnum 枚举
|
||||
*/
|
||||
private List<Integer> deliveryTypes;
|
||||
|
||||
/**
|
||||
* 物流配置模板编号
|
||||
*
|
||||
* 对应 TradeDeliveryExpressTemplateDO 的 id 编号
|
||||
*/
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
/**
|
||||
* 赠送积分
|
||||
*/
|
||||
private Integer giveIntegral;
|
||||
|
||||
// ========== 分销相关字段 =========
|
||||
|
||||
/**
|
||||
* 分销类型
|
||||
*
|
||||
* false - 默认
|
||||
* true - 自行设置
|
||||
*/
|
||||
private Boolean subCommissionType;
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.product.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
|
||||
|
||||
/**
|
||||
* API 相关的枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class ApiConstants {
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*
|
||||
* 注意,需要保证和 spring.application.name 保持一致
|
||||
*/
|
||||
public static final String NAME = "product-server";
|
||||
|
||||
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/product";
|
||||
|
||||
public static final String VERSION = "1.0.0";
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package cn.iocoder.yudao.module.product.enums;
|
||||
|
||||
/**
|
||||
* product 字典类型的枚举类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface DictTypeConstants {
|
||||
|
||||
String PRODUCT_SPU_STATUS = "product_spu_status"; // 商品 SPU 状态
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.product.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* Product 错误码枚举类
|
||||
*
|
||||
* product 系统,使用 1-008-000-000 段
|
||||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 商品分类相关 1-008-001-000 ============
|
||||
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_008_001_000, "商品分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_008_001_001, "父分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_008_001_002, "父分类不能是二级分类");
|
||||
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_008_001_003, "存在子分类,无法删除");
|
||||
ErrorCode CATEGORY_DISABLED = new ErrorCode(1_008_001_004, "商品分类({})已禁用,无法使用");
|
||||
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1_008_001_005, "类别下存在商品,无法删除");
|
||||
|
||||
// ========== 商品品牌相关编号 1-008-002-000 ==========
|
||||
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1_008_002_000, "品牌不存在");
|
||||
ErrorCode BRAND_DISABLED = new ErrorCode(1_008_002_001, "品牌已禁用");
|
||||
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1_008_002_002, "品牌名称已存在");
|
||||
|
||||
// ========== 商品属性项 1-008-003-000 ==========
|
||||
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1_008_003_000, "属性项不存在");
|
||||
ErrorCode PROPERTY_EXISTS = new ErrorCode(1_008_003_001, "属性项的名称已存在");
|
||||
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1_008_003_002, "属性项下存在属性值,无法删除");
|
||||
|
||||
// ========== 商品属性值 1-008-004-000 ==========
|
||||
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1_008_004_000, "属性值不存在");
|
||||
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1_008_004_001, "属性值的名称已存在");
|
||||
|
||||
// ========== 商品 SPU 1-008-005-000 ==========
|
||||
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在");
|
||||
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
|
||||
ErrorCode SPU_SAVE_FAIL_COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_008_005_002, "商品 SPU 保存失败,原因:优惠劵不存在");
|
||||
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_003, "商品 SPU【{}】不处于上架状态");
|
||||
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_004, "商品 SPU 不处于回收站状态");
|
||||
|
||||
// ========== 商品 SKU 1-008-006-000 ==========
|
||||
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在");
|
||||
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1_008_006_001, "商品 SKU 的属性组合存在重复");
|
||||
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1_008_006_002, "一个 SPU 下的每个 SKU,其属性项必须一致");
|
||||
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1_008_006_003, "一个 SPU 下的每个 SKU,必须不重复");
|
||||
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_008_006_004, "商品 SKU 库存不足");
|
||||
|
||||
// ========== 商品 评价 1-008-007-000 ==========
|
||||
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, "商品评价不存在");
|
||||
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1_008_007_001, "订单的商品评价已存在");
|
||||
|
||||
// ========== 商品 收藏 1-008-008-000 ==========
|
||||
ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, "该商品已经被收藏");
|
||||
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, "商品收藏不存在");
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.product.enums;
|
||||
|
||||
/**
|
||||
* Product 常量
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface ProductConstants {
|
||||
|
||||
/**
|
||||
* 警戒库存 TODO 警戒库存暂时为 10,后期需要使用常量或者数据库配置替换
|
||||
*/
|
||||
int ALERT_STOCK = 10;
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.product.enums.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 商品评论的审批状态枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProductCommentAuditStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
NONE(0, "待审核"),
|
||||
APPROVE(1, "审批通过"),
|
||||
REJECT(2, "审批不通过"),;
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentAuditStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 审批状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.product.enums.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 商品评论的星级枚举
|
||||
*
|
||||
* @author wangzhs
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProductCommentScoresEnum implements ArrayValuable<Integer> {
|
||||
|
||||
ONE(1, "1星"),
|
||||
TWO(2, "2星"),
|
||||
THREE(3, "3星"),
|
||||
FOUR(4, "4星"),
|
||||
FIVE(5, "5星");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductCommentScoresEnum::getScores).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 星级
|
||||
*/
|
||||
private final Integer scores;
|
||||
|
||||
/**
|
||||
* 星级名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.product.enums.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 商品 SPU 状态
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProductSpuStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
RECYCLE(-1, "回收站"),
|
||||
DISABLE(0, "下架"),
|
||||
ENABLE(1, "上架");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ProductSpuStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否处于【上架】状态
|
||||
*
|
||||
* @param status 状态
|
||||
* @return 是否处于【上架】状态
|
||||
*/
|
||||
public static boolean isEnable(Integer status) {
|
||||
return ENABLE.getStatus().equals(status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
|
||||
## 感谢复旦核博士的建议!灰子哥,牛皮!
|
||||
FROM eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /yudao-module-product-biz
|
||||
WORKDIR /yudao-module-product-biz
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/yudao-module-product-biz.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
|
||||
|
||||
## 暴露后端项目的 48080 端口
|
||||
EXPOSE 48100
|
||||
|
||||
## 启动后端项目
|
||||
CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
|
||||
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-product-biz</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
product 模块,主要实现商品相关功能
|
||||
例如:品牌、商品分类、spu、sku等功能。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-env</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-member-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.product;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 项目的启动类
|
||||
*
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class ProductServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
|
||||
SpringApplication.run(ProductServerApplication.class, args);
|
||||
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.product.api.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 商品分类 API 接口实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ProductCategoryApiImpl implements ProductCategoryApi {
|
||||
|
||||
@Resource
|
||||
private ProductCategoryService productCategoryService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> validateCategoryList(Collection<Long> ids) {
|
||||
productCategoryService.validateCategoryList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.product.api.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 商品评论 API 实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ProductCommentApiImpl implements ProductCommentApi {
|
||||
|
||||
@Resource
|
||||
private ProductCommentService productCommentService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> createComment(ProductCommentCreateReqDTO createReqDTO) {
|
||||
return success(productCommentService.createComment(createReqDTO));
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package cn.iocoder.yudao.module.product.api;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 商品 SKU API 实现类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ProductSkuApiImpl implements ProductSkuApi {
|
||||
|
||||
@Resource
|
||||
private ProductSkuService productSkuService;
|
||||
|
||||
@Override
|
||||
public CommonResult<ProductSkuRespDTO> getSku(Long id) {
|
||||
ProductSkuDO sku = productSkuService.getSku(id);
|
||||
return success(BeanUtils.toBean(sku, ProductSkuRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<ProductSkuRespDTO>> getSkuList(Collection<Long> ids) {
|
||||
List<ProductSkuDO> skus = productSkuService.getSkuList(ids);
|
||||
return success(BeanUtils.toBean(skus, ProductSkuRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<ProductSkuRespDTO>> getSkuListBySpuId(Collection<Long> spuIds) {
|
||||
List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds);
|
||||
return success(BeanUtils.toBean(skus, ProductSkuRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
|
||||
productSkuService.updateSkuStock(updateStockReqDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.product.api.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 商品 SPU API 接口实现类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ProductSpuApiImpl implements ProductSpuApi {
|
||||
|
||||
@Resource
|
||||
private ProductSpuService spuService;
|
||||
|
||||
@Override
|
||||
public CommonResult<List<ProductSpuRespDTO>> getSpuList(Collection<Long> ids) {
|
||||
List<ProductSpuDO> spus = spuService.getSpuList(ids);
|
||||
return success(BeanUtils.toBean(spus, ProductSpuRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<ProductSpuRespDTO>> validateSpuList(Collection<Long> ids) {
|
||||
List<ProductSpuDO> spus = spuService.validateSpuList(ids);
|
||||
return success(BeanUtils.toBean(spus, ProductSpuRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<ProductSpuRespDTO> getSpu(Long id) {
|
||||
ProductSpuDO spu = spuService.getSpu(id);
|
||||
return success(BeanUtils.toBean(spu, ProductSpuRespDTO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand;
|
||||
|
||||
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.admin.brand.vo.*;
|
||||
import cn.iocoder.yudao.module.product.convert.brand.ProductBrandConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
|
||||
import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 商品品牌")
|
||||
@RestController
|
||||
@RequestMapping("/product/brand")
|
||||
@Validated
|
||||
public class ProductBrandController {
|
||||
|
||||
@Resource
|
||||
private ProductBrandService brandService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建品牌")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:create')")
|
||||
public CommonResult<Long> createBrand(@Valid @RequestBody ProductBrandCreateReqVO createReqVO) {
|
||||
return success(brandService.createBrand(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新品牌")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:update')")
|
||||
public CommonResult<Boolean> updateBrand(@Valid @RequestBody ProductBrandUpdateReqVO updateReqVO) {
|
||||
brandService.updateBrand(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除品牌")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:delete')")
|
||||
public CommonResult<Boolean> deleteBrand(@RequestParam("id") Long id) {
|
||||
brandService.deleteBrand(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得品牌")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:query')")
|
||||
public CommonResult<ProductBrandRespVO> getBrand(@RequestParam("id") Long id) {
|
||||
ProductBrandDO brand = brandService.getBrand(id);
|
||||
return success(ProductBrandConvert.INSTANCE.convert(brand));
|
||||
}
|
||||
|
||||
@GetMapping("/list-all-simple")
|
||||
@Operation(summary = "获取品牌精简信息列表", description = "主要用于前端的下拉选项")
|
||||
public CommonResult<List<ProductBrandSimpleRespVO>> getSimpleBrandList() {
|
||||
// 获取品牌列表,只要开启状态的
|
||||
List<ProductBrandDO> list = brandService.getBrandListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
// 排序后,返回给前端
|
||||
return success(ProductBrandConvert.INSTANCE.convertList1(list));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得品牌分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:query')")
|
||||
public CommonResult<PageResult<ProductBrandRespVO>> getBrandPage(@Valid ProductBrandPageReqVO pageVO) {
|
||||
PageResult<ProductBrandDO> pageResult = brandService.getBrandPage(pageVO);
|
||||
return success(ProductBrandConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得品牌列表")
|
||||
@PreAuthorize("@ss.hasPermission('product:brand:query')")
|
||||
public CommonResult<List<ProductBrandRespVO>> getBrandList(@Valid ProductBrandListReqVO listVO) {
|
||||
List<ProductBrandDO> list = brandService.getBrandList(listVO);
|
||||
list.sort(Comparator.comparing(ProductBrandDO::getSort));
|
||||
return success(ProductBrandConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 商品品牌 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class ProductBrandBaseVO {
|
||||
|
||||
@Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果")
|
||||
@NotNull(message = "品牌名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "品牌图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "品牌图片不能为空")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "品牌排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "品牌排序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "品牌描述", example = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品品牌创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductBrandCreateReqVO extends ProductBrandBaseVO {
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 商品品牌分页 Request VO")
|
||||
@Data
|
||||
public class ProductBrandListReqVO {
|
||||
|
||||
@Schema(description = "品牌名称", example = "苹果")
|
||||
private String name;
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.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;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 商品品牌分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductBrandPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "品牌名称", example = "苹果")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 品牌 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductBrandRespVO extends ProductBrandBaseVO {
|
||||
|
||||
@Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - 品牌精简信息 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductBrandSimpleRespVO {
|
||||
|
||||
@Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果")
|
||||
private String name;
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品品牌更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductBrandUpdateReqVO extends ProductBrandBaseVO {
|
||||
|
||||
@Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "品牌编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategorySaveReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 商品分类")
|
||||
@RestController
|
||||
@RequestMapping("/product/category")
|
||||
@Validated
|
||||
public class ProductCategoryController {
|
||||
|
||||
@Resource
|
||||
private ProductCategoryService categoryService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建商品分类")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:create')")
|
||||
public CommonResult<Long> createCategory(@Valid @RequestBody ProductCategorySaveReqVO createReqVO) {
|
||||
return success(categoryService.createCategory(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新商品分类")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:update')")
|
||||
public CommonResult<Boolean> updateCategory(@Valid @RequestBody ProductCategorySaveReqVO updateReqVO) {
|
||||
categoryService.updateCategory(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除商品分类")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('product:category:delete')")
|
||||
public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
|
||||
categoryService.deleteCategory(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得商品分类")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<ProductCategoryRespVO> getCategory(@RequestParam("id") Long id) {
|
||||
ProductCategoryDO category = categoryService.getCategory(id);
|
||||
return success(BeanUtils.toBean(category, ProductCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得商品分类列表")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<List<ProductCategoryRespVO>> getCategoryList(@Valid ProductCategoryListReqVO listReqVO) {
|
||||
List<ProductCategoryDO> list = categoryService.getCategoryList(listReqVO);
|
||||
list.sort(Comparator.comparing(ProductCategoryDO::getSort));
|
||||
return success(BeanUtils.toBean(list, ProductCategoryRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Schema(description = "管理后台 - 商品分类列表查询 Request VO")
|
||||
@Data
|
||||
public class ProductCategoryListReqVO {
|
||||
|
||||
@Schema(description = "分类名称", example = "办公文具")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "开启状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "父分类编号", example = "1")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "父分类编号数组", example = "1,2,3")
|
||||
private Collection<Long> parentIds;
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 商品分类 Response VO")
|
||||
@Data
|
||||
public class ProductCategoryRespVO {
|
||||
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公文具")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "移动端分类图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "分类描述", example = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 商品分类新增/更新 Request VO")
|
||||
@Data
|
||||
public class ProductCategorySaveReqVO {
|
||||
|
||||
@Schema(description = "分类编号", example = "2")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "父分类编号不能为空")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公文具")
|
||||
@NotBlank(message = "分类名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "移动端分类图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "移动端分类图不能为空")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "开启状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "分类描述", example = "描述")
|
||||
private String description;
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.*;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
||||
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 商品评价")
|
||||
@RestController
|
||||
@RequestMapping("/product/comment")
|
||||
@Validated
|
||||
public class ProductCommentController {
|
||||
|
||||
@Resource
|
||||
private ProductCommentService productCommentService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品评价分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:comment:query')")
|
||||
public CommonResult<PageResult<ProductCommentRespVO>> getCommentPage(@Valid ProductCommentPageReqVO pageVO) {
|
||||
PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductCommentRespVO.class));
|
||||
}
|
||||
|
||||
@PutMapping("/update-visible")
|
||||
@Operation(summary = "显示 / 隐藏评论")
|
||||
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
||||
public CommonResult<Boolean> updateCommentVisible(@Valid @RequestBody ProductCommentUpdateVisibleReqVO updateReqVO) {
|
||||
productCommentService.updateCommentVisible(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/reply")
|
||||
@Operation(summary = "商家回复")
|
||||
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
||||
public CommonResult<Boolean> commentReply(@Valid @RequestBody ProductCommentReplyReqVO replyVO) {
|
||||
productCommentService.replyComment(replyVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "添加自评")
|
||||
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
||||
public CommonResult<Boolean> createComment(@Valid @RequestBody ProductCommentCreateReqVO createReqVO) {
|
||||
productCommentService.createComment(createReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品评价创建 Request VO")
|
||||
@Data
|
||||
public class ProductCommentCreateReqVO {
|
||||
|
||||
@Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "16868")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "评价订单项", requiredMode = Schema.RequiredMode.REQUIRED, example = "19292")
|
||||
private Long orderItemId;
|
||||
|
||||
@Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉")
|
||||
@NotNull(message = "评价人名称不能为空")
|
||||
private String userNickname;
|
||||
|
||||
@Schema(description = "评价人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
@NotNull(message = "评价人头像不能为空")
|
||||
private String userAvatar;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "描述星级不能为空")
|
||||
private Integer descriptionScores;
|
||||
|
||||
@Schema(description = "服务星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "服务星级分不能为空")
|
||||
private Integer benefitScores;
|
||||
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "穿起来非常丝滑凉快")
|
||||
@NotNull(message = "评论内容不能为空")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "评论图片地址数组,以逗号分隔最多上传 9 张", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "[https://www.iocoder.cn/xx.png]")
|
||||
@Size(max = 9, message = "评论图片地址数组长度不能超过 9 张")
|
||||
private List<String> picUrls;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 商品评价分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductCommentPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "评价人名称", example = "王二狗")
|
||||
private String userNickname;
|
||||
|
||||
@Schema(description = "交易订单编号", example = "24428")
|
||||
private Long orderId;
|
||||
|
||||
@Schema(description = "商品SPU编号", example = "29502")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品SPU名称", example = "感冒药")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "评分星级 1-5 分", example = "5")
|
||||
@InEnum(ProductCommentScoresEnum.class)
|
||||
private Integer scores;
|
||||
|
||||
@Schema(description = "商家是否回复", example = "true")
|
||||
private Boolean replyStatus;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品评价的商家回复 Request VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class ProductCommentReplyReqVO {
|
||||
|
||||
@Schema(description = "评价编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15721")
|
||||
@NotNull(message = "评价编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商家回复内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "谢谢亲")
|
||||
@NotEmpty(message = "商家回复内容不能为空")
|
||||
private String replyContent;
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品评价 Response VO")
|
||||
@Data
|
||||
public class ProductCommentRespVO {
|
||||
|
||||
@Schema(description = "订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24965")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "16868")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉")
|
||||
private String userNickname;
|
||||
|
||||
@Schema(description = "评价人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
private String userAvatar;
|
||||
@Schema(description = "是否匿名", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean anonymous;
|
||||
|
||||
@Schema(description = "交易订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24428")
|
||||
private Long orderId;
|
||||
|
||||
@Schema(description = "评价订单项", requiredMode = Schema.RequiredMode.REQUIRED, example = "19292")
|
||||
private Long orderItemId;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "商品 SKU 图片地址", example = "https://www.iocoder.cn/yudao.jpg")
|
||||
private String skuPicUrl;
|
||||
|
||||
@Schema(description = "商品 SKU 规格值数组")
|
||||
private List<ProductSkuSaveReqVO.Property> skuProperties;
|
||||
|
||||
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer scores;
|
||||
|
||||
@Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer descriptionScores;
|
||||
|
||||
@Schema(description = "服务星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer benefitScores;
|
||||
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "穿起来非常丝滑凉快")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "评论图片地址数组,以逗号分隔最多上传 9 张", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]")
|
||||
private List<String> picUrls;
|
||||
|
||||
@Schema(description = "商家是否回复", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean replyStatus;
|
||||
|
||||
@Schema(description = "回复管理员编号", example = "9527")
|
||||
private Long replyUserId;
|
||||
|
||||
@Schema(description = "商家回复内容", example = "感谢好评哦亲(づ ̄3 ̄)づ╭❤~")
|
||||
private String replyContent;
|
||||
|
||||
@Schema(description = "商家回复时间", example = "2023-08-08 12:20:55")
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品评价可见修改 Request VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class ProductCommentUpdateVisibleReqVO {
|
||||
|
||||
@Schema(description = "评价编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15721")
|
||||
@NotNull(message = "评价编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否可见不能为空")
|
||||
private Boolean visible;
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.favorite;
|
||||
|
||||
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.favorite.vo.ProductFavoritePageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoriteRespVO;
|
||||
import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import 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 = "管理后台 - 商品收藏")
|
||||
@RestController
|
||||
@RequestMapping("/product/favorite")
|
||||
@Validated
|
||||
public class ProductFavoriteController {
|
||||
|
||||
@Resource
|
||||
private ProductFavoriteService productFavoriteService;
|
||||
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品收藏分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:favorite:query')")
|
||||
public CommonResult<PageResult<ProductFavoriteRespVO>> getFavoritePage(@Valid ProductFavoritePageReqVO pageVO) {
|
||||
PageResult<ProductFavoriteDO> pageResult = productFavoriteService.getFavoritePage(pageVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
// 拼接数据
|
||||
List<ProductSpuDO> spuList = productSpuService.getSpuList(convertSet(pageResult.getList(), ProductFavoriteDO::getSpuId));
|
||||
return success(ProductFavoriteConvert.INSTANCE.convertPage(pageResult, spuList));
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 商品收藏 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class ProductFavoriteBaseVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5036")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.favorite.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 = "管理后台 - 商品收藏分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductFavoritePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "5036")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.favorite.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品收藏的单个 Response VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class ProductFavoriteReqVO extends ProductFavoriteBaseVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32734")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.favorite.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.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品收藏 Response VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class ProductFavoriteRespVO extends ProductSpuRespVO {
|
||||
|
||||
@Schema(description = "userId", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
private Long spuId;
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.history;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryRespVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - 商品浏览记录")
|
||||
@RestController
|
||||
@RequestMapping("/product/browse-history")
|
||||
@Validated
|
||||
public class ProductBrowseHistoryController {
|
||||
|
||||
@Resource
|
||||
private ProductBrowseHistoryService browseHistoryService;
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品浏览记录分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:browse-history:query')")
|
||||
public CommonResult<PageResult<ProductBrowseHistoryRespVO>> getBrowseHistoryPage(@Valid ProductBrowseHistoryPageReqVO pageReqVO) {
|
||||
PageResult<ProductBrowseHistoryDO> pageResult = browseHistoryService.getBrowseHistoryPage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 得到商品 spu 信息
|
||||
Map<Long, ProductSpuDO> spuMap = productSpuService.getSpuMap(
|
||||
convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId));
|
||||
return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class,
|
||||
vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
|
||||
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
|
||||
.setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.history.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 商品浏览记录分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductBrowseHistoryPageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "4314")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户是否删除", example = "false")
|
||||
private Boolean userDeleted;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", example = "42")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.history.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "管理后台 - 商品浏览记录 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ProductBrowseHistoryRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502")
|
||||
private Long spuId;
|
||||
|
||||
// ========== 商品相关字段 ==========
|
||||
|
||||
@Schema(description = "商品 SPU 名称", example = "赵六")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "商品封面图", example = "https://domain/pic.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品单价", example = "100")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品销量", example = "100")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "库存", example = "100")
|
||||
private Integer stock;
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
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.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertySaveReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
|
||||
import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - 商品属性项")
|
||||
@RestController
|
||||
@RequestMapping("/product/property")
|
||||
@Validated
|
||||
public class ProductPropertyController {
|
||||
|
||||
@Resource
|
||||
private ProductPropertyService productPropertyService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建属性项")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:create')")
|
||||
public CommonResult<Long> createProperty(@Valid @RequestBody ProductPropertySaveReqVO createReqVO) {
|
||||
return success(productPropertyService.createProperty(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新属性项")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:update')")
|
||||
public CommonResult<Boolean> updateProperty(@Valid @RequestBody ProductPropertySaveReqVO updateReqVO) {
|
||||
productPropertyService.updateProperty(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除属性项")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('product:property:delete')")
|
||||
public CommonResult<Boolean> deleteProperty(@RequestParam("id") Long id) {
|
||||
productPropertyService.deleteProperty(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得属性项")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:query')")
|
||||
public CommonResult<ProductPropertyRespVO> getProperty(@RequestParam("id") Long id) {
|
||||
ProductPropertyDO property = productPropertyService.getProperty(id);
|
||||
return success(BeanUtils.toBean(property, ProductPropertyRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得属性项分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:query')")
|
||||
public CommonResult<PageResult<ProductPropertyRespVO>> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) {
|
||||
PageResult<ProductPropertyDO> pageResult = productPropertyService.getPropertyPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductPropertyRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得属性项精简列表")
|
||||
public CommonResult<List<ProductPropertyRespVO>> getPropertySimpleList() {
|
||||
List<ProductPropertyDO> list = productPropertyService.getPropertyList();
|
||||
return success(convertList(list, property -> new ProductPropertyRespVO() // 只返回 id、name 属性
|
||||
.setId(property.getId()).setName(property.getName())));
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
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.framework.common.util.object.BeanUtils;
|
||||
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.ProductPropertyValueSaveReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
|
||||
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.singleton;
|
||||
|
||||
@Tag(name = "管理后台 - 商品属性值")
|
||||
@RestController
|
||||
@RequestMapping("/product/property/value")
|
||||
@Validated
|
||||
public class ProductPropertyValueController {
|
||||
|
||||
@Resource
|
||||
private ProductPropertyValueService productPropertyValueService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建属性值")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:create')")
|
||||
public CommonResult<Long> createPropertyValue(@Valid @RequestBody ProductPropertyValueSaveReqVO createReqVO) {
|
||||
return success(productPropertyValueService.createPropertyValue(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新属性值")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:update')")
|
||||
public CommonResult<Boolean> updatePropertyValue(@Valid @RequestBody ProductPropertyValueSaveReqVO updateReqVO) {
|
||||
productPropertyValueService.updatePropertyValue(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除属性值")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:delete')")
|
||||
public CommonResult<Boolean> deletePropertyValue(@RequestParam("id") Long id) {
|
||||
productPropertyValueService.deletePropertyValue(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得属性值")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:query')")
|
||||
public CommonResult<ProductPropertyValueRespVO> getPropertyValue(@RequestParam("id") Long id) {
|
||||
ProductPropertyValueDO value = productPropertyValueService.getPropertyValue(id);
|
||||
return success(BeanUtils.toBean(value, ProductPropertyValueRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得属性值分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:property:query')")
|
||||
public CommonResult<PageResult<ProductPropertyValueRespVO>> getPropertyValuePage(@Valid ProductPropertyValuePageReqVO pageVO) {
|
||||
PageResult<ProductPropertyValueDO> pageResult = productPropertyValueService.getPropertyValuePage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductPropertyValueRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得属性值精简列表")
|
||||
@Parameter(name = "propertyId", description = "属性项编号", required = true, example = "1024")
|
||||
public CommonResult<List<ProductPropertyValueRespVO>> getPropertyValueSimpleList(@RequestParam("propertyId") Long propertyId) {
|
||||
List<ProductPropertyValueDO> list = productPropertyValueService.getPropertyValueListByPropertyId(singleton(propertyId));
|
||||
return success(convertList(list, value -> new ProductPropertyValueRespVO() // 只返回 id、name 属性
|
||||
.setId(value.getId()).setName(value.getName())));
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
|
||||
|
||||
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;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 属性项 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductPropertyPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "名称", example = "颜色")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 属性项 Response VO")
|
||||
@Data
|
||||
public class ProductPropertyRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "备注", example = "颜色")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.property;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 属性项新增/更新 Request VO")
|
||||
@Data
|
||||
public class ProductPropertySaveReqVO {
|
||||
|
||||
@Schema(description = "主键", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色")
|
||||
@NotBlank(message = "名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "备注", example = "颜色")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
|
||||
|
||||
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 = "管理后台 - 商品属性值分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductPropertyValuePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "属性项的编号", example = "1024")
|
||||
private String propertyId;
|
||||
|
||||
@Schema(description = "名称", example = "红色")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 商品属性值 Response VO")
|
||||
@Data
|
||||
public class ProductPropertyValueRespVO {
|
||||
|
||||
@Schema(description = "主键", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "属性项的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "属性项的编号不能为空")
|
||||
private Long propertyId;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色")
|
||||
@NotEmpty(message = "名称名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "备注", example = "颜色")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 商品属性值新增/更新 Request VO")
|
||||
@Data
|
||||
public class ProductPropertyValueSaveReqVO {
|
||||
|
||||
@Schema(description = "主键", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "属性项的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "属性项的编号不能为空")
|
||||
private Long propertyId;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色")
|
||||
@NotEmpty(message = "名称名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "备注", example = "颜色")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### 获得商品 SPU 明细
|
||||
GET {{baseUrl}}/product/spu/get-detail?id=4
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
|
||||
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.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.sku.ProductSkuService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
|
||||
|
||||
@Tag(name = "管理后台 - 商品 SPU")
|
||||
@RestController
|
||||
@RequestMapping("/product/spu")
|
||||
@Validated
|
||||
public class ProductSpuController {
|
||||
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
@Resource
|
||||
private ProductSkuService productSkuService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建商品 SPU")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:create')")
|
||||
public CommonResult<Long> createProductSpu(@Valid @RequestBody ProductSpuSaveReqVO createReqVO) {
|
||||
return success(productSpuService.createSpu(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新商品 SPU")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:update')")
|
||||
public CommonResult<Boolean> updateSpu(@Valid @RequestBody ProductSpuSaveReqVO updateReqVO) {
|
||||
productSpuService.updateSpu(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新商品 SPU Status")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:update')")
|
||||
public CommonResult<Boolean> updateStatus(@Valid @RequestBody ProductSpuUpdateStatusReqVO updateReqVO) {
|
||||
productSpuService.updateSpuStatus(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除商品 SPU")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:delete')")
|
||||
public CommonResult<Boolean> deleteSpu(@RequestParam("id") Long id) {
|
||||
productSpuService.deleteSpu(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得商品 SPU 明细")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<ProductSpuRespVO> getSpuDetail(@RequestParam("id") Long id) {
|
||||
// 获得商品 SPU
|
||||
ProductSpuDO spu = productSpuService.getSpu(id);
|
||||
if (spu == null) {
|
||||
return success(null);
|
||||
}
|
||||
// 查询商品 SKU
|
||||
List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
|
||||
return success(ProductSpuConvert.INSTANCE.convert(spu, skus));
|
||||
}
|
||||
|
||||
@GetMapping("/list-all-simple")
|
||||
@Operation(summary = "获得商品 SPU 精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<List<ProductSpuSimpleRespVO>> getSpuSimpleList() {
|
||||
List<ProductSpuDO> list = productSpuService.getSpuListByStatus(ProductSpuStatusEnum.ENABLE.getStatus());
|
||||
// 降序排序后,返回给前端
|
||||
list.sort(Comparator.comparing(ProductSpuDO::getSort).reversed());
|
||||
return success(BeanUtils.toBean(list, ProductSpuSimpleRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得商品 SPU 详情列表")
|
||||
@Parameter(name = "spuIds", description = "spu 编号列表", required = true, example = "[1,2,3]")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<List<ProductSpuRespVO>> getSpuList(@RequestParam("spuIds") Collection<Long> spuIds) {
|
||||
return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespListVO(
|
||||
productSpuService.getSpuList(spuIds), productSkuService.getSkuListBySpuId(spuIds)));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品 SPU 分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
|
||||
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductSpuRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get-count")
|
||||
@Operation(summary = "获得商品 SPU 分页 tab count")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<Map<Integer, Long>> getSpuCount() {
|
||||
return success(productSpuService.getTabsCount());
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出商品")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportSpuList(@Validated ProductSpuPageReqVO reqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
reqVO.setPageSize(PAGE_SIZE_NONE);
|
||||
List<ProductSpuDO> list = productSpuService.getSpuPage(reqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "商品列表.xls", "数据", ProductSpuRespVO.class,
|
||||
BeanUtils.toBean(list, ProductSpuRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SKU Response VO")
|
||||
@Data
|
||||
public class ProductSkuRespVO {
|
||||
|
||||
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SKU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "销售价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价", example = "2999")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "成本价", example = "19")
|
||||
private Integer costPrice;
|
||||
|
||||
@Schema(description = "条形码", example = "15156165456")
|
||||
private String barCode;
|
||||
|
||||
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "商品重量,单位:kg 千克", example = "1.2")
|
||||
private Double weight;
|
||||
|
||||
@Schema(description = "商品体积,单位:m^3 平米", example = "2.5")
|
||||
private Double volume;
|
||||
|
||||
@Schema(description = "一级分销的佣金,单位:分", example = "199")
|
||||
private Integer firstBrokeragePrice;
|
||||
|
||||
@Schema(description = "二级分销的佣金,单位:分", example = "19")
|
||||
private Integer secondBrokeragePrice;
|
||||
|
||||
@Schema(description = "属性数组")
|
||||
private List<ProductSkuSaveReqVO.Property> properties;
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SKU 创建/更新 Request VO")
|
||||
@Data
|
||||
public class ProductSkuSaveReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
|
||||
@NotEmpty(message = "商品 SKU 名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "销售价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
@NotNull(message = "销售价格,单位:分不能为空")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价", example = "2999")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "成本价", example = "19")
|
||||
private Integer costPrice;
|
||||
|
||||
@Schema(description = "条形码", example = "15156165456")
|
||||
private String barCode;
|
||||
|
||||
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
@NotNull(message = "图片地址不能为空")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
@NotNull(message = "库存不能为空")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "商品重量,单位:kg 千克", example = "1.2")
|
||||
private Double weight;
|
||||
|
||||
@Schema(description = "商品体积,单位:m^3 平米", example = "2.5")
|
||||
private Double volume;
|
||||
|
||||
@Schema(description = "一级分销的佣金,单位:分", example = "199")
|
||||
private Integer firstBrokeragePrice;
|
||||
|
||||
@Schema(description = "二级分销的佣金,单位:分", example = "19")
|
||||
private Integer secondBrokeragePrice;
|
||||
|
||||
@Schema(description = "属性数组")
|
||||
private List<Property> properties;
|
||||
|
||||
@Schema(description = "商品属性")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Property {
|
||||
|
||||
@Schema(description = "属性编号", example = "10")
|
||||
private Long propertyId;
|
||||
|
||||
@Schema(description = "属性名字", example = "颜色")
|
||||
private String propertyName;
|
||||
|
||||
@Schema(description = "属性值编号", example = "10")
|
||||
private Long valueId;
|
||||
|
||||
@Schema(description = "属性值名字", example = "红色")
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.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;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SPU 分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ProductSpuPageReqVO extends PageParam {
|
||||
|
||||
/**
|
||||
* 出售中商品
|
||||
*/
|
||||
public static final Integer FOR_SALE = 0;
|
||||
|
||||
/**
|
||||
* 仓库中商品
|
||||
*/
|
||||
public static final Integer IN_WAREHOUSE = 1;
|
||||
|
||||
/**
|
||||
* 已售空商品
|
||||
*/
|
||||
public static final Integer SOLD_OUT = 2;
|
||||
|
||||
/**
|
||||
* 警戒库存
|
||||
*/
|
||||
public static final Integer ALERT_STOCK = 3;
|
||||
|
||||
/**
|
||||
* 商品回收站
|
||||
*/
|
||||
public static final Integer RECYCLE_BIN = 4;
|
||||
|
||||
@Schema(description = "商品名称", example = "清凉小短袖")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "前端请求的tab类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer tabType;
|
||||
|
||||
@Schema(description = "商品分类编号", example = "1")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;
|
||||
import cn.iocoder.yudao.module.product.enums.DictTypeConstants;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SPU Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ProductSpuRespVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
@ExcelProperty("商品编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
|
||||
@ExcelProperty("商品名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关键字", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑不出汗")
|
||||
@ExcelProperty("关键字")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
|
||||
@ExcelProperty("商品简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖详情")
|
||||
@ExcelProperty("商品详情")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "商品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("商品分类编号")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("商品品牌编号")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
@ExcelProperty("商品封面图")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
@Schema(description = "排序字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("排序字段")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty(value = "商品状态", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.PRODUCT_SPU_STATUS)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "商品创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-05-24 00:00:00")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@ExcelProperty("规格类型")
|
||||
private Boolean specType;
|
||||
|
||||
@Schema(description = "商品价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
@ExcelProperty(value = "商品价格", converter = MoneyConvert.class)
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "199")
|
||||
@ExcelProperty(value = "市场价", converter = MoneyConvert.class)
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "成本价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
|
||||
@ExcelProperty(value = "成本价", converter = MoneyConvert.class)
|
||||
private Integer costPrice;
|
||||
|
||||
@Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
|
||||
@ExcelProperty("库存")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "SKU 数组")
|
||||
private List<ProductSkuRespVO> skus;
|
||||
|
||||
// ========== 物流相关字段 =========
|
||||
|
||||
@Schema(description = "配送方式数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private List<Integer> deliveryTypes;
|
||||
|
||||
@Schema(description = "物流配置模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
@ExcelProperty("物流配置模板编号")
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
@Schema(description = "赠送积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
@ExcelProperty("赠送积分")
|
||||
private Integer giveIntegral;
|
||||
|
||||
@Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@ExcelProperty("分销类型")
|
||||
private Boolean subCommissionType;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
|
||||
@ExcelProperty("商品销量")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "虚拟销量", example = "66")
|
||||
@ExcelProperty("虚拟销量")
|
||||
private Integer virtualSalesCount;
|
||||
|
||||
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
|
||||
@ExcelProperty("商品点击量")
|
||||
private Integer browseCount;
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SPU 新增/更新 Request VO")
|
||||
@Data
|
||||
public class ProductSpuSaveReqVO {
|
||||
|
||||
@Schema(description = "商品编号", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
|
||||
@NotEmpty(message = "商品名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关键字", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑不出汗")
|
||||
@NotEmpty(message = "商品关键字不能为空")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
|
||||
@NotEmpty(message = "商品简介不能为空")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖详情")
|
||||
@NotEmpty(message = "商品详情不能为空")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "商品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品分类不能为空")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品品牌不能为空")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
@NotEmpty(message = "商品封面图不能为空")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
@Schema(description = "排序字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品排序字段不能为空")
|
||||
private Integer sort;
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "商品规格类型不能为空")
|
||||
private Boolean specType;
|
||||
|
||||
// ========== 物流相关字段 =========
|
||||
|
||||
@Schema(description = "配送方式数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "配送方式不能为空")
|
||||
private List<Integer> deliveryTypes;
|
||||
|
||||
@Schema(description = "物流配置模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
@Schema(description = "赠送积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
|
||||
@NotNull(message = "商品赠送积分不能为空")
|
||||
private Integer giveIntegral;
|
||||
|
||||
@Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "商品分销类型不能为空")
|
||||
private Boolean subCommissionType;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "虚拟销量", example = "66")
|
||||
private Integer virtualSalesCount;
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
private Integer browseCount;
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "SKU 数组")
|
||||
@Valid
|
||||
private List<ProductSkuSaveReqVO> skus;
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SPU 精简 Response VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class ProductSpuSimpleRespVO {
|
||||
|
||||
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "213")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "199")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "商品成本价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
|
||||
private Integer costPrice;
|
||||
|
||||
@Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
|
||||
private Integer stock;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "商品虚拟销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20000")
|
||||
private Integer virtualSalesCount;
|
||||
|
||||
@Schema(description = "商品浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
|
||||
private Integer browseCount;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 商品 SPU Status 更新 Request VO")
|
||||
@Data
|
||||
public class ProductSpuUpdateStatusReqVO{
|
||||
|
||||
@Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "商品状态不能为空")
|
||||
@InEnum(ProductSpuStatusEnum.class)
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.category;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "用户 APP - 商品分类")
|
||||
@RestController
|
||||
@RequestMapping("/product/category")
|
||||
@Validated
|
||||
public class AppCategoryController {
|
||||
|
||||
@Resource
|
||||
private ProductCategoryService categoryService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得商品分类列表")
|
||||
@PermitAll
|
||||
public CommonResult<List<AppCategoryRespVO>> getProductCategoryList() {
|
||||
List<ProductCategoryDO> list = categoryService.getEnableCategoryList();
|
||||
list.sort(Comparator.comparing(ProductCategoryDO::getSort));
|
||||
return success(BeanUtils.toBean(list, AppCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-ids")
|
||||
@Operation(summary = "获得商品分类列表,指定编号")
|
||||
@Parameter(name = "ids", description = "商品分类编号数组", required = true)
|
||||
@PermitAll
|
||||
public CommonResult<List<AppCategoryRespVO>> getProductCategoryList(@RequestParam("ids") List<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
List<ProductCategoryDO> list = categoryService.getEnableCategoryList(ids);
|
||||
list.sort(Comparator.comparing(ProductCategoryDO::getSort));
|
||||
return success(BeanUtils.toBean(list, AppCategoryRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.category.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户 APP - 商品分类 Response VO")
|
||||
public class AppCategoryRespVO {
|
||||
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "父分类编号不能为空")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公文具")
|
||||
@NotBlank(message = "分类名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "分类图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "分类图片不能为空")
|
||||
private String picUrl;
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.comment;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
||||
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "用户 APP - 商品评价")
|
||||
@RestController
|
||||
@RequestMapping("/product/comment")
|
||||
@Validated
|
||||
public class AppProductCommentController {
|
||||
|
||||
@Resource
|
||||
private ProductCommentService productCommentService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品评价分页")
|
||||
@PermitAll
|
||||
public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
|
||||
// 查询评论分页
|
||||
PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// 拼接返回
|
||||
pageResult.getList().forEach(item -> {
|
||||
if (Boolean.TRUE.equals(item.getAnonymous())) {
|
||||
item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
|
||||
}
|
||||
});
|
||||
return success(BeanUtils.toBean(pageResult, AppProductCommentRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.comment.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "用户 App - 商品评价分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AppCommentPageReqVO extends PageParam {
|
||||
|
||||
/**
|
||||
* 好评
|
||||
*/
|
||||
public static final Integer GOOD_COMMENT = 1;
|
||||
/**
|
||||
* 中评
|
||||
*/
|
||||
public static final Integer MEDIOCRE_COMMENT = 2;
|
||||
/**
|
||||
* 差评
|
||||
*/
|
||||
public static final Integer NEGATIVE_COMMENT = 3;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", example = "29502")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "app 评论页 tab 类型 (0 全部、1 好评、2 中评、3 差评)", example = "0")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Integer type;
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.comment.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 商品评价详情 Response VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class AppProductCommentRespVO {
|
||||
|
||||
@Schema(description = "评价人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15721")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String userNickname;
|
||||
|
||||
@Schema(description = "评价人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
private String userAvatar;
|
||||
|
||||
@Schema(description = "订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24965")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否匿名", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean anonymous;
|
||||
|
||||
@Schema(description = "交易订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24428")
|
||||
private Long orderId;
|
||||
|
||||
@Schema(description = "交易订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8233")
|
||||
private Long orderItemId;
|
||||
|
||||
@Schema(description = "商家是否回复", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean replyStatus;
|
||||
|
||||
@Schema(description = "回复管理员编号", example = "22212")
|
||||
private Long replyUserId;
|
||||
|
||||
@Schema(description = "商家回复内容", example = "亲,你的好评就是我的动力(*^▽^*)")
|
||||
private String replyContent;
|
||||
|
||||
@Schema(description = "商家回复时间")
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
@Schema(description = "追加评价内容", example = "穿了很久都很丝滑诶")
|
||||
private String additionalContent;
|
||||
|
||||
@Schema(description = "追评评价图片地址数组,以逗号分隔最多上传 9 张", example = "[https://www.iocoder.cn/xx.png, https://www.iocoder.cn/xxx.png]")
|
||||
private List<String> additionalPicUrls;
|
||||
|
||||
@Schema(description = "追加评价时间")
|
||||
private LocalDateTime additionalTime;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "91192")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑小短袖")
|
||||
@NotNull(message = "商品 SPU 名称不能为空")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "81192")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品 SKU 属性", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<AppProductPropertyValueDetailRespVO> skuProperties;
|
||||
|
||||
@Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "评分星级 1-5 分不能为空")
|
||||
private Integer scores;
|
||||
|
||||
@Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "描述星级 1-5 分不能为空")
|
||||
private Integer descriptionScores;
|
||||
|
||||
@Schema(description = "服务星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "服务星级 1-5 分不能为空")
|
||||
private Integer benefitScores;
|
||||
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "哇,真的很丝滑凉快诶,好评")
|
||||
@NotNull(message = "评论内容不能为空")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "评论图片地址数组,以逗号分隔最多上传 9 张", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "[https://www.iocoder.cn/xx.png]")
|
||||
@Size(max = 9, message = "评论图片地址数组长度不能超过 9 张")
|
||||
private List<String> picUrls;
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.favorite;
|
||||
|
||||
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.app.favorite.vo.AppFavoritePageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
|
||||
import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 APP - 商品收藏")
|
||||
@RestController
|
||||
@RequestMapping("/product/favorite")
|
||||
public class AppFavoriteController {
|
||||
|
||||
@Resource
|
||||
private ProductFavoriteService productFavoriteService;
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
|
||||
@PostMapping(value = "/create")
|
||||
@Operation(summary = "添加商品收藏")
|
||||
public CommonResult<Long> createFavorite(@RequestBody @Valid AppFavoriteReqVO reqVO) {
|
||||
return success(productFavoriteService.createFavorite(getLoginUserId(), reqVO.getSpuId()));
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/delete")
|
||||
@Operation(summary = "取消单个商品收藏")
|
||||
public CommonResult<Boolean> deleteFavorite(@RequestBody @Valid AppFavoriteReqVO reqVO) {
|
||||
productFavoriteService.deleteFavorite(getLoginUserId(), reqVO.getSpuId());
|
||||
return success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
@Operation(summary = "获得商品收藏分页")
|
||||
public CommonResult<PageResult<AppFavoriteRespVO>> getFavoritePage(AppFavoritePageReqVO reqVO) {
|
||||
PageResult<ProductFavoriteDO> favoritePage = productFavoriteService.getFavoritePage(getLoginUserId(), reqVO);
|
||||
if (CollUtil.isEmpty(favoritePage.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 得到商品 spu 信息
|
||||
List<ProductFavoriteDO> favorites = favoritePage.getList();
|
||||
List<Long> spuIds = convertList(favorites, ProductFavoriteDO::getSpuId);
|
||||
List<ProductSpuDO> spus = productSpuService.getSpuList(spuIds);
|
||||
|
||||
// 转换 VO 结果
|
||||
PageResult<AppFavoriteRespVO> pageResult = new PageResult<>(favoritePage.getTotal());
|
||||
pageResult.setList(ProductFavoriteConvert.INSTANCE.convertList(favorites, spus));
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/exits")
|
||||
@Operation(summary = "检查是否收藏过商品")
|
||||
public CommonResult<Boolean> isFavoriteExists(AppFavoriteReqVO reqVO) {
|
||||
ProductFavoriteDO favorite = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());
|
||||
return success(favorite != null);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/get-count")
|
||||
@Operation(summary = "获得商品收藏数量")
|
||||
public CommonResult<Long> getFavoriteCount() {
|
||||
return success(productFavoriteService.getFavoriteCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "用户 APP - 商品收藏的批量 Request VO") // 用于收藏、取消收藏、获取收藏
|
||||
@Data
|
||||
public class AppFavoriteBatchReqVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号数组", requiredMode = REQUIRED, example = "29502")
|
||||
@NotEmpty(message = "商品 SPU 编号数组不能为空")
|
||||
private List<Long> spuIds;
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 商品收藏分页查询 Request VO")
|
||||
@Data
|
||||
public class AppFavoritePageReqVO extends PageParam {
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "用户 APP - 商品收藏的单个 Request VO") // 用于收藏、取消收藏、获取收藏
|
||||
@Data
|
||||
public class AppFavoriteReqVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "用户 App - 商品收藏 Response VO")
|
||||
@Data
|
||||
public class AppFavoriteRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502")
|
||||
private Long spuId;
|
||||
|
||||
// ========== 商品相关字段 ==========
|
||||
|
||||
@Schema(description = "商品 SPU 名称", example = "赵六")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "商品封面图", example = "https://domain/pic.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品单价", example = "100")
|
||||
private Integer price;
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.history;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryDeleteReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryRespVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
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.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 APP - 商品浏览记录")
|
||||
@RestController
|
||||
@RequestMapping("/product/browse-history")
|
||||
public class AppProductBrowseHistoryController {
|
||||
|
||||
@Resource
|
||||
private ProductBrowseHistoryService productBrowseHistoryService;
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
|
||||
@DeleteMapping(value = "/delete")
|
||||
@Operation(summary = "删除商品浏览记录")
|
||||
public CommonResult<Boolean> deleteBrowseHistory(@RequestBody @Valid AppProductBrowseHistoryDeleteReqVO reqVO) {
|
||||
productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), reqVO.getSpuIds());
|
||||
return success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/clean")
|
||||
@Operation(summary = "清空商品浏览记录")
|
||||
public CommonResult<Boolean> deleteBrowseHistory() {
|
||||
productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), null);
|
||||
return success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
@Operation(summary = "获得商品浏览记录分页")
|
||||
public CommonResult<PageResult<AppProductBrowseHistoryRespVO>> getBrowseHistoryPage(AppProductBrowseHistoryPageReqVO reqVO) {
|
||||
ProductBrowseHistoryPageReqVO pageReqVO = BeanUtils.toBean(reqVO, ProductBrowseHistoryPageReqVO.class)
|
||||
.setUserId(getLoginUserId())
|
||||
.setUserDeleted(false); // 排除用户已删除的(隐藏的)
|
||||
PageResult<ProductBrowseHistoryDO> pageResult = productBrowseHistoryService.getBrowseHistoryPage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 得到商品 spu 信息
|
||||
Set<Long> spuIds = convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId);
|
||||
Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId);
|
||||
return success(BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class,
|
||||
vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
|
||||
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
|
||||
.setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.history.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "用户 APP - 删除商品浏览记录的 Request VO")
|
||||
@Data
|
||||
public class AppProductBrowseHistoryDeleteReqVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号数组", requiredMode = REQUIRED, example = "29502")
|
||||
@NotEmpty(message = "商品 SPU 编号数组不能为空")
|
||||
private List<Long> spuIds;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.history.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;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "用户 APP - 商品浏览记录分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AppProductBrowseHistoryPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.history.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
|
||||
|
||||
@Schema(description = "用户 App - 商品浏览记录 Response VO")
|
||||
@Data
|
||||
public class AppProductBrowseHistoryRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502")
|
||||
private Long spuId;
|
||||
|
||||
// ========== 商品相关字段 ==========
|
||||
|
||||
@Schema(description = "商品 SPU 名称", requiredMode = REQUIRED, example = "赵六")
|
||||
private String spuName;
|
||||
|
||||
@Schema(description = "商品封面图", requiredMode = REQUIRED, example = "https://www.iocoder.cn/pic.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品单价", requiredMode = REQUIRED, example = "50")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = REQUIRED, example = "60")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "库存", requiredMode = REQUIRED, example = "80")
|
||||
private Integer stock;
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位符,无时间作用,避免 package 缩进
|
||||
*/
|
||||
package cn.iocoder.yudao.module.product.controller.app.property;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位符,无时间作用,避免 package 缩进
|
||||
*/
|
||||
package cn.iocoder.yudao.module.product.controller.app.property.vo.property;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.property.vo.value;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 商品属性值的明细 Response VO")
|
||||
@Data
|
||||
public class AppProductPropertyValueDetailRespVO {
|
||||
|
||||
@Schema(description = "属性的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long propertyId;
|
||||
|
||||
@Schema(description = "属性的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色")
|
||||
private String propertyName;
|
||||
|
||||
@Schema(description = "属性值的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long valueId;
|
||||
|
||||
@Schema(description = "属性值的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色")
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
### 获得订单交易的分页(默认)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
### 获得订单交易的分页(价格)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=price&sortAsc=true
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
### 获得订单交易的分页(销售)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=salesCount&sortAsc=true
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
### 获得商品 SPU 明细
|
||||
GET {{appApi}}/product/spu/get-detail?id=102
|
||||
tenant-id: {{appTenantId}}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.spu;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
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.AppProductSpuRespVO;
|
||||
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.history.ProductBrowseHistoryService;
|
||||
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
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")
|
||||
@RestController
|
||||
@RequestMapping("/product/spu")
|
||||
@Validated
|
||||
public class AppProductSpuController {
|
||||
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
@Resource
|
||||
private ProductSkuService productSkuService;
|
||||
@Resource
|
||||
private ProductBrowseHistoryService productBrowseHistoryService;
|
||||
|
||||
@GetMapping("/list-by-ids")
|
||||
@Operation(summary = "获得商品 SPU 列表")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PermitAll
|
||||
public CommonResult<List<AppProductSpuRespVO>> getSpuList(@RequestParam("ids") Set<Long> ids) {
|
||||
List<ProductSpuDO> list = productSpuService.getSpuList(ids);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 拼接返回
|
||||
list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
|
||||
List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
|
||||
return success(voList);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品 SPU 分页")
|
||||
@PermitAll
|
||||
public CommonResult<PageResult<AppProductSpuRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
|
||||
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// 拼接返回
|
||||
pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
|
||||
PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);
|
||||
return success(voPageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得商品 SPU 明细")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PermitAll
|
||||
public CommonResult<AppProductSpuDetailRespVO> 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, spu.getName());
|
||||
}
|
||||
// 获得商品 SKU
|
||||
List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
|
||||
|
||||
// 增加浏览量
|
||||
productSpuService.updateBrowseCount(id, 1);
|
||||
// 保存浏览记录
|
||||
productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);
|
||||
|
||||
// 拼接返回
|
||||
spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());
|
||||
AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
|
||||
.setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
|
||||
return success(spuVO);
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
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.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 商品 SPU 明细 Response VO")
|
||||
@Data
|
||||
public class AppProductSpuDetailRespVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
// ========== 基本信息 =========
|
||||
|
||||
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个快乐简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是商品描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "商品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean specType;
|
||||
|
||||
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
|
||||
private Integer stock;
|
||||
|
||||
/**
|
||||
* SKU 数组
|
||||
*/
|
||||
private List<Sku> skus;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer salesCount;
|
||||
|
||||
@Schema(description = "用户 App - 商品 SPU 明细的 SKU 信息")
|
||||
@Data
|
||||
public static class Sku {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", example = "1")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 商品属性数组
|
||||
*/
|
||||
private List<AppProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
@Schema(description = "销售价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格
|
||||
private Integer vipPrice;
|
||||
|
||||
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "商品重量", example = "1") // 单位:kg 千克
|
||||
private Double weight;
|
||||
|
||||
@Schema(description = "商品体积", example = "1024") // 单位:m^3 平米
|
||||
private Double volume;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
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.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 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";
|
||||
public static final String SORT_FIELD_CREATE_TIME = "createTime";
|
||||
|
||||
@Schema(description = "商品 SPU 编号数组", example = "1,3,5")
|
||||
private List<Long> ids;
|
||||
|
||||
@Schema(description = "分类编号", example = "1")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "分类编号数组", example = "1,2,3")
|
||||
private List<Long> categoryIds;
|
||||
|
||||
@Schema(description = "关键字", example = "好看")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "排序字段", example = "price") // 参见 AppProductSpuPageReqVO.SORT_FIELD_XXX 常量
|
||||
private String sortField;
|
||||
|
||||
@Schema(description = "排序方式", example = "true")
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.product.controller.app.spu.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 商品 SPU Response VO")
|
||||
@Data
|
||||
public class AppProductSpuRespVO {
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean specType;
|
||||
|
||||
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
|
||||
private Integer stock;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer salesCount;
|
||||
|
||||
// ========== 物流相关字段 =========
|
||||
|
||||
@Schema(description = "配送方式数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private List<Integer> deliveryTypes;
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.product.convert.brand;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandSimpleRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 品牌 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProductBrandConvert {
|
||||
|
||||
ProductBrandConvert INSTANCE = Mappers.getMapper(ProductBrandConvert.class);
|
||||
|
||||
ProductBrandDO convert(ProductBrandCreateReqVO bean);
|
||||
|
||||
ProductBrandDO convert(ProductBrandUpdateReqVO bean);
|
||||
|
||||
ProductBrandRespVO convert(ProductBrandDO bean);
|
||||
|
||||
List<ProductBrandSimpleRespVO> convertList1(List<ProductBrandDO> list);
|
||||
|
||||
List<ProductBrandRespVO> convertList(List<ProductBrandDO> list);
|
||||
|
||||
PageResult<ProductBrandRespVO> convertPage(PageResult<ProductBrandDO> page);
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package cn.iocoder.yudao.module.product.convert.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 商品评价 Convert
|
||||
*
|
||||
* @author wangzhs
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProductCommentConvert {
|
||||
|
||||
ProductCommentConvert INSTANCE = Mappers.getMapper(ProductCommentConvert.class);
|
||||
|
||||
default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO,
|
||||
ProductSpuDO spu, ProductSkuDO sku, MemberUserRespDTO user) {
|
||||
ProductCommentDO comment = BeanUtils.toBean(createReqDTO, ProductCommentDO.class)
|
||||
.setScores(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()));
|
||||
if (user != null) {
|
||||
comment.setUserId(user.getId()).setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar());
|
||||
}
|
||||
if (spu != null) {
|
||||
comment.setSpuId(spu.getId()).setSpuName(spu.getName());
|
||||
}
|
||||
if (sku != null) {
|
||||
comment.setSkuPicUrl(sku.getPicUrl()).setSkuProperties(sku.getProperties());
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
default ProductCommentDO convert(ProductCommentCreateReqVO createReq, ProductSpuDO spu, ProductSkuDO sku) {
|
||||
ProductCommentDO comment = BeanUtils.toBean(createReq, ProductCommentDO.class)
|
||||
.setVisible(true).setUserId(0L).setAnonymous(false)
|
||||
.setScores(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()));
|
||||
if (spu != null) {
|
||||
comment.setSpuId(spu.getId()).setSpuName(spu.getName());
|
||||
}
|
||||
if (sku != null) {
|
||||
comment.setSkuPicUrl(sku.getPicUrl()).setSkuProperties(sku.getProperties());
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
default Integer convertScores(Integer descriptionScores, Integer benefitScores) {
|
||||
// 计算评价最终综合评分 最终星数 = (商品评星 + 服务评星) / 2
|
||||
BigDecimal sumScore = new BigDecimal(descriptionScores + benefitScores);
|
||||
BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);
|
||||
return divide.intValue();
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.product.convert.favorite;
|
||||
|
||||
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.favorite.vo.ProductFavoriteRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
@Mapper
|
||||
public interface ProductFavoriteConvert {
|
||||
|
||||
ProductFavoriteConvert INSTANCE = Mappers.getMapper(ProductFavoriteConvert.class);
|
||||
|
||||
ProductFavoriteDO convert(Long userId, Long spuId);
|
||||
|
||||
@Mapping(target = "id", source = "favorite.id")
|
||||
@Mapping(target = "spuName", source = "spu.name")
|
||||
AppFavoriteRespVO convert(ProductSpuDO spu, ProductFavoriteDO favorite);
|
||||
|
||||
default List<AppFavoriteRespVO> convertList(List<ProductFavoriteDO> favorites, List<ProductSpuDO> spus) {
|
||||
List<AppFavoriteRespVO> resultList = new ArrayList<>(favorites.size());
|
||||
Map<Long, ProductSpuDO> spuMap = convertMap(spus, ProductSpuDO::getId);
|
||||
for (ProductFavoriteDO favorite : favorites) {
|
||||
ProductSpuDO spuDO = spuMap.get(favorite.getSpuId());
|
||||
resultList.add(convert(spuDO, favorite));
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
default PageResult<ProductFavoriteRespVO> convertPage(PageResult<ProductFavoriteDO> pageResult, List<ProductSpuDO> spuList) {
|
||||
Map<Long, ProductSpuDO> spuMap = convertMap(spuList, ProductSpuDO::getId);
|
||||
List<ProductFavoriteRespVO> voList = CollectionUtils.convertList(pageResult.getList(), favorite -> {
|
||||
ProductSpuDO spu = spuMap.get(favorite.getSpuId());
|
||||
return convert02(spu, favorite);
|
||||
});
|
||||
return new PageResult<>(voList, pageResult.getTotal());
|
||||
}
|
||||
@Mapping(target = "id", source = "favorite.id")
|
||||
@Mapping(target = "userId", source = "favorite.userId")
|
||||
@Mapping(target = "spuId", source = "favorite.spuId")
|
||||
@Mapping(target = "createTime", source = "favorite.createTime")
|
||||
ProductFavoriteRespVO convert02(ProductSpuDO spu, ProductFavoriteDO favorite);
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
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.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 商品 SKU Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProductSkuConvert {
|
||||
|
||||
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
|
||||
|
||||
/**
|
||||
* 获得 SPU 的库存变化 Map
|
||||
*
|
||||
* @param items SKU 库存变化
|
||||
* @param skus SKU 列表
|
||||
* @return SPU 的库存变化 Map
|
||||
*/
|
||||
default Map<Long, Integer> convertSpuStockMap(List<ProductSkuUpdateStockReqDTO.Item> items,
|
||||
List<ProductSkuDO> skus) {
|
||||
Map<Long, Long> skuIdAndSpuIdMap = convertMap(skus, ProductSkuDO::getId, ProductSkuDO::getSpuId); // SKU 与 SKU 编号的 Map 关系
|
||||
Map<Long, Integer> spuIdAndStockMap = new HashMap<>(); // SPU 的库存变化 Map 关系
|
||||
items.forEach(item -> {
|
||||
Long spuId = skuIdAndSpuIdMap.get(item.getId());
|
||||
if (spuId == null) {
|
||||
return;
|
||||
}
|
||||
Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncrCount();
|
||||
spuIdAndStockMap.put(spuId, stock);
|
||||
});
|
||||
return spuIdAndStockMap;
|
||||
}
|
||||
|
||||
default String buildPropertyKey(ProductSkuDO bean) {
|
||||
if (CollUtil.isEmpty(bean.getProperties())) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
List<ProductSkuDO.Property> properties = new ArrayList<>(bean.getProperties());
|
||||
properties.sort(Comparator.comparing(ProductSkuDO.Property::getValueId));
|
||||
return properties.stream().map(m -> String.valueOf(m.getValueId())).collect(Collectors.joining());
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.product.convert.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;
|
||||
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
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.convertMultiMap;
|
||||
|
||||
/**
|
||||
* 商品 SPU Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProductSpuConvert {
|
||||
|
||||
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
|
||||
|
||||
ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean);
|
||||
|
||||
default ProductSpuRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus) {
|
||||
ProductSpuRespVO spuVO = BeanUtils.toBean(spu, ProductSpuRespVO.class);
|
||||
spuVO.setSkus(BeanUtils.toBean(skus, ProductSkuRespVO.class));
|
||||
return spuVO;
|
||||
}
|
||||
|
||||
default List<ProductSpuRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
|
||||
Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);
|
||||
return CollectionUtils.convertList(spus, spu -> convert(spu, skuMultiMap.get(spu.getId())));
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.brand;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 商品品牌 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("product_brand")
|
||||
@KeySequence("product_brand_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductBrandDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 品牌编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 品牌名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 品牌图片
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 品牌排序
|
||||
*/
|
||||
private Integer sort;
|
||||
/**
|
||||
* 品牌描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user