Commit 7a4a6a3046191d4420415c248fb273ddd21852f8

Authored by YunaiV
1 parent 92dde0b4

product:优化管理后台相关的接口

area:增加地区列表界面
Showing 31 changed files with 401 additions and 339 deletions
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java
1 1 package cn.iocoder.yudao.framework.ip.core.utils;
2 2  
3 3 import cn.hutool.core.io.resource.ResourceUtil;
  4 +import cn.hutool.core.lang.Assert;
4 5 import cn.hutool.core.text.csv.CsvRow;
5 6 import cn.hutool.core.text.csv.CsvUtil;
6 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
... ... @@ -50,6 +51,7 @@ public class AreaUtils {
50 51 for (CsvRow row : rows) {
51 52 Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
52 53 Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
  54 + Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
53 55 area.setParent(parent);
54 56 parent.getChildren().add(area);
55 57 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/resources/area.csv
... ... @@ -2567,8 +2567,6 @@ id,name,type,parentId
2567 2567 441826,连南瑶族自治县,4,441800
2568 2568 441881,英德市,4,441800
2569 2569 441882,连州市,4,441800
2570   -441900,东莞市,4,441900
2571   -442000,中山市,4,442000
2572 2570 445102,湘桥区,4,445100
2573 2571 445103,潮安区,4,445100
2574 2572 445122,饶平县,4,445100
... ... @@ -2704,7 +2702,6 @@ id,name,type,parentId
2704 2702 460321,西沙群岛,4,460300
2705 2703 460322,南沙群岛,4,460300
2706 2704 460323,中沙群岛的岛礁及其海域,4,460300
2707   -460400,儋州市,4,460400
2708 2705 469001,五指山市,4,469000
2709 2706 469002,琼海市,4,469000
2710 2707 469005,文昌市,4,469000
... ...
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/AssertUtils.java
... ... @@ -33,6 +33,10 @@ public class AssertUtils {
33 33 public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) {
34 34 Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
35 35 Arrays.stream(expectedFields).forEach(expectedField -> {
  36 + // 忽略 jacoco 自动生成的 $jacocoData 属性的情况
  37 + if (expectedField.isSynthetic()) {
  38 + return;
  39 + }
36 40 // 如果是忽略的属性,则不进行比对
37 41 if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {
38 42 return;
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java deleted
1   -package cn.iocoder.yudao.module.product.controller.admin.property.vo;
2   -
3   -import io.swagger.annotations.ApiModel;
4   -import io.swagger.annotations.ApiModelProperty;
5   -import lombok.Data;
6   -import lombok.ToString;
7   -
8   -import java.util.List;
9   -
10   -// TODO 芋艿:看看怎么删除
11   -@ApiModel("管理后台 - 商品属性详情展示 Request VO")
12   -@Data
13   -@ToString(callSuper = true)
14   -public class ProductPropertyViewRespVO {
15   -
16   - @ApiModelProperty(value = "属性项 id", example = "1")
17   - public Long propertyId;
18   -
19   - @ApiModelProperty(value = "属性项的名字", example = "内存")
20   - public String name;
21   -
22   - @ApiModelProperty(value = "属性值数组")
23   - public List<Tuple2> propertyValues;
24   -
25   - @Data
26   - @ApiModel(value = "属性值元组")
27   - public static class Tuple2 {
28   - private final long id;
29   - private final String name;
30   -
31   - public Tuple2(Long id, String name) {
32   - this.id = id;
33   - this.name = name;
34   - }
35   -
36   - }
37   -
38   -
39   -}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel("管理后台 - 商品属性值的明细 Response VO")
  8 +@Data
  9 +public class ProductPropertyValueDetailRespVO {
  10 +
  11 + @ApiModelProperty(value = "属性的编号", required = true, example = "1")
  12 + private Long propertyId;
  13 +
  14 + @ApiModelProperty(value = "属性的名称", required = true, example = "颜色")
  15 + private String propertyName;
  16 +
  17 + @ApiModelProperty(value = "属性值的编号", required = true, example = "1024")
  18 + private Long valueId;
  19 +
  20 + @ApiModelProperty(value = "属性值的名称", required = true, example = "红色")
  21 + private String valueName;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http 0 → 100644
  1 +### 获得商品 SPU 明细
  2 +GET {{baseUrl}}/product/spu/get-detail?id=4
  3 +Authorization: Bearer {{token}}
  4 +tenant-id: {{adminTenentId}}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
... ... @@ -3,8 +3,13 @@ package cn.iocoder.yudao.module.product.controller.admin.spu;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  6 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
6 7 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
  8 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
7 9 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  10 +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
  11 +import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
  12 +import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
8 13 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
9 14 import io.swagger.annotations.Api;
10 15 import io.swagger.annotations.ApiImplicitParam;
... ... @@ -15,10 +20,11 @@ import org.springframework.web.bind.annotation.*;
15 20  
16 21 import javax.annotation.Resource;
17 22 import javax.validation.Valid;
18   -import java.util.Collection;
19 23 import java.util.List;
20 24  
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
22 28  
23 29 @Api(tags = "管理后台 - 商品 SPU")
24 30 @RestController
... ... @@ -28,6 +34,10 @@ public class ProductSpuController {
28 34  
29 35 @Resource
30 36 private ProductSpuService productSpuService;
  37 + @Resource
  38 + private ProductSkuService productSkuService;
  39 + @Resource
  40 + private ProductPropertyValueService productPropertyValueService;
31 41  
32 42 @PostMapping("/create")
33 43 @ApiOperation("创建商品 SPU")
... ... @@ -53,31 +63,24 @@ public class ProductSpuController {
53 63 return success(true);
54 64 }
55 65  
56   - // TODO 芋艿:修改接口
57   - @GetMapping("/get/detail")
58   - @ApiOperation("获得商品 SPU")
  66 + @GetMapping("/get-detail")
  67 + @ApiOperation("获得商品 SPU 明细")
59 68 @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
60 69 @PreAuthorize("@ss.hasPermission('product:spu:query')")
61 70 public CommonResult<ProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
62   - return success(productSpuService.getSpuDetail(id));
63   - }
  71 + // 获得商品 SPU
  72 + ProductSpuDO spu = productSpuService.getSpu(id);
  73 + if (spu == null) {
  74 + throw exception(SPU_NOT_EXISTS);
  75 + }
64 76  
65   - @GetMapping("/get")
66   - @ApiOperation("获得商品 SPU")
67   - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
68   - @PreAuthorize("@ss.hasPermission('product:spu:query')")
69   - public CommonResult<ProductSpuDO> getSpu(@RequestParam("id") Long id) {
70   - return success(productSpuService.getSpu(id));
71   - }
72   -
73   -
74   - @GetMapping("/list")
75   - @ApiOperation("获得商品 SPU 列表")
76   - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
77   - @PreAuthorize("@ss.hasPermission('product:spu:query')")
78   - public CommonResult<List<ProductSpuDO>> getSpuList(@RequestParam("ids") Collection<Long> ids) {
79   - List<ProductSpuDO> list = productSpuService.getSpuList(ids);
80   - return success(ProductSpuConvert.INSTANCE.convertList(list));
  77 + // 查询商品 SKU
  78 + List<ProductSkuDO> skus = productSkuService.getSkuListBySpuIdAndStatus(spu.getId(), null);
  79 + // 查询商品属性
  80 + List<ProductPropertyValueDetailRespBO> propertyValues = productPropertyValueService
  81 + .getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
  82 + // 拼接
  83 + return success(ProductSpuConvert.INSTANCE.convert03(spu, skus, propertyValues));
81 84 }
82 85  
83 86 @GetMapping("/get-simple-list")
... ... @@ -92,7 +95,7 @@ public class ProductSpuController {
92 95 @ApiOperation("获得商品 SPU 分页")
93 96 @PreAuthorize("@ss.hasPermission('product:spu:query')")
94 97 public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
95   - return success(productSpuService.getSpuPage(pageVO));
  98 + return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO)));
96 99 }
97 100  
98 101 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java
1 1 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
2 2  
3   -import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
  3 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueDetailRespVO;
4 4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
5 5 import io.swagger.annotations.ApiModel;
6   -import io.swagger.annotations.ApiModelProperty;
7 6 import lombok.Data;
8 7 import lombok.EqualsAndHashCode;
9 8 import lombok.ToString;
10 9  
11   -import java.time.LocalDateTime;
12 10 import java.util.List;
13 11  
14 12 @ApiModel(value = "管理后台 - 商品 SPU 详细 Response VO", description = "包括关联的 SKU 等信息")
15 13 @Data
16 14 @EqualsAndHashCode(callSuper = true)
17 15 @ToString(callSuper = true)
18   -public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
19   -
20   - @ApiModelProperty(value = "主键", required = true, example = "1")
21   - private Long id;
22   -
23   - @ApiModelProperty(value = "创建时间")
24   - private LocalDateTime createTime;
  16 +public class ProductSpuDetailRespVO extends ProductSpuRespVO {
25 17  
26 18 // ========== SKU 相关字段 =========
27 19  
28   - @ApiModelProperty(value = "库存", required = true, example = "true")
29   - private Integer totalStock;
30   -
31   - @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
32   - private Integer minPrice;
33   -
34   - @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
35   - private Integer maxPrice;
36   -
37   - @ApiModelProperty(value = "商品销量", example = "1024")
38   - private Integer salesCount;
39   -
40 20 /**
41 21 * SKU 数组
42 22 */
43 23 private List<Sku> skus;
44 24  
45   - // ========== 统计相关字段 =========
46   -
47   - @ApiModelProperty(value = "点击量", example = "1024")
48   - private Integer clickCount;
49   -
50 25 @ApiModel(value = "管理后台 - 商品 SKU 详细 Response VO")
51 26 @Data
52 27 @EqualsAndHashCode(callSuper = true)
... ... @@ -56,29 +31,8 @@ public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
56 31 /**
57 32 * 属性数组
58 33 */
59   - private List<ProductSpuDetailRespVO.Property> properties;
60   -
61   - }
62   -
63   - @ApiModel(value = "管理后台 - 商品属性的详细 Response VO")
64   - @Data
65   - @EqualsAndHashCode(callSuper = true)
66   - @ToString(callSuper = true)
67   - public static class Property extends ProductSkuBaseVO.Property {
68   -
69   - @ApiModelProperty(value = "属性项的名字", required = true, example = "颜色")
70   - private String propertyName;
71   -
72   - @ApiModelProperty(value = "属性值的名称", required = true, example = "蓝色")
73   - private String valueName;
  34 + private List<ProductPropertyValueDetailRespVO> properties;
74 35  
75 36 }
76 37  
77   - @ApiModelProperty(value = "分类 id 数组,一直递归到一级父节点", example = "4")
78   - private Long categoryId;
79   -
80   - // TODO @芋艿:在瞅瞅~
81   - @ApiModelProperty(value = "属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]")
82   - private List<ProductPropertyViewRespVO> productPropertyViews;
83   -
84 38 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
... ... @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
7   -import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuDetailRespVO;
8   -import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageItemRespVO;
  7 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
  8 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
9 9 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
10 10 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
11 11 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
... ... @@ -48,7 +48,7 @@ public class AppProductSpuController {
48 48  
49 49 @GetMapping("/page")
50 50 @ApiOperation("获得商品 SPU 分页")
51   - public CommonResult<PageResult<AppSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
  51 + public CommonResult<PageResult<AppProductSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
52 52 PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus());
53 53 return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult));
54 54 }
... ... @@ -56,7 +56,7 @@ public class AppProductSpuController {
56 56 @GetMapping("/get-detail")
57 57 @ApiOperation("获得商品 SPU 明细")
58 58 @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
59   - public CommonResult<AppSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
  59 + public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
60 60 // 获得商品 SPU
61 61 ProductSpuDO spu = productSpuService.getSpu(id);
62 62 if (spu == null) {
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuDetailRespVO.java renamed to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java
... ... @@ -9,7 +9,7 @@ import java.util.List;
9 9  
10 10 @ApiModel("用户 App - 商品 SPU 明细 Response VO")
11 11 @Data
12   -public class AppSpuDetailRespVO {
  12 +public class AppProductSpuDetailRespVO {
13 13  
14 14 @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
15 15 private Long id;
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageItemRespVO.java renamed to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java
... ... @@ -10,7 +10,7 @@ import java.util.List;
10 10  
11 11 @ApiModel("用户 App - 商品 SPU 分页项 Response VO")
12 12 @Data
13   -public class AppSpuPageItemRespVO {
  13 +public class AppProductSpuPageItemRespVO {
14 14  
15 15 @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
16 16 private Long id;
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
... ... @@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.product.convert.spu;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueDetailRespVO;
6 7 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
7 8 import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
  9 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
8 10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
9   -import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuDetailRespVO;
10   -import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageItemRespVO;
  11 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
11 12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
12 13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
13 14 import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
... ... @@ -45,9 +46,9 @@ public interface ProductSpuConvert {
45 46  
46 47 List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list);
47 48  
48   - default AppSpuDetailRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus,
49   - List<ProductPropertyValueDetailRespBO> propertyValues) {
50   - AppSpuDetailRespVO spuVO = convert02(spu)
  49 + default AppProductSpuDetailRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus,
  50 + List<ProductPropertyValueDetailRespBO> propertyValues) {
  51 + AppProductSpuDetailRespVO spuVO = convert02(spu)
51 52 .setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0));
52 53 spuVO.setSkus(convertList03(skus));
53 54 // 处理商品属性
... ... @@ -57,7 +58,7 @@ public interface ProductSpuConvert {
57 58 if (CollUtil.isEmpty(properties)) {
58 59 continue;
59 60 }
60   - AppSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
  61 + AppProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
61 62 sku.setProperties(new ArrayList<>(properties.size()));
62 63 // 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
63 64 properties.forEach(property -> {
... ... @@ -70,10 +71,38 @@ public interface ProductSpuConvert {
70 71 }
71 72 return spuVO;
72 73 }
73   - AppSpuDetailRespVO convert02(ProductSpuDO spu);
74   - List<AppSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
  74 + AppProductSpuDetailRespVO convert02(ProductSpuDO spu);
  75 + List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
75 76 AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
76 77  
77   - PageResult<AppSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);
  78 + PageResult<AppProductSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);
  79 +
  80 + default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
  81 + List<ProductPropertyValueDetailRespBO> propertyValues) {
  82 + ProductSpuDetailRespVO spuVO = convert03(spu);
  83 + spuVO.setSkus(convertList04(skus));
  84 + // 处理商品属性
  85 + Map<Long, ProductPropertyValueDetailRespBO> propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
  86 + for (int i = 0; i < skus.size(); i++) {
  87 + List<ProductSkuDO.Property> properties = skus.get(i).getProperties();
  88 + if (CollUtil.isEmpty(properties)) {
  89 + continue;
  90 + }
  91 + ProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
  92 + sku.setProperties(new ArrayList<>(properties.size()));
  93 + // 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
  94 + properties.forEach(property -> {
  95 + ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
  96 + if (propertyValue == null) {
  97 + return;
  98 + }
  99 + sku.getProperties().add(convert04(propertyValue));
  100 + });
  101 + }
  102 + return spuVO;
  103 + }
  104 + ProductSpuDetailRespVO convert03(ProductSpuDO spu);
  105 + List<ProductSpuDetailRespVO.Sku> convertList04(List<ProductSkuDO> skus);
  106 + ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
78 107  
79 108 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java
... ... @@ -21,8 +21,7 @@ public interface ProductPropertyMapper extends BaseMapperX&lt;ProductPropertyDO&gt; {
21 21 }
22 22  
23 23 default ProductPropertyDO selectByName(String name) {
24   - return selectOne(new LambdaQueryWrapperX<ProductPropertyDO>()
25   - .eqIfPresent(ProductPropertyDO::getName, name));
  24 + return selectOne(ProductPropertyDO::getName, name);
26 25 }
27 26  
28 27 default List<ProductPropertyDO> selectList(ProductPropertyListReqVO listReqVO) {
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java
... ... @@ -17,6 +17,7 @@ public interface ProductPropertyService {
17 17  
18 18 /**
19 19 * 创建属性项
  20 + * 注意,如果已经存在该属性项,直接返回它的编号即可
20 21 *
21 22 * @param createReqVO 创建信息
22 23 * @return 编号
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java
... ... @@ -40,9 +40,10 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
40 40 @Override
41 41 @Transactional(rollbackFor = Exception.class)
42 42 public Long createProperty(ProductPropertyCreateReqVO createReqVO) {
43   - // 校验名字重复
44   - if (productPropertyMapper.selectByName(createReqVO.getName()) != null) {
45   - throw exception(PROPERTY_EXISTS);
  43 + // 如果已经添加过该属性项,直接返回
  44 + ProductPropertyDO dbProperty = productPropertyMapper.selectByName(createReqVO.getName());
  45 + if (dbProperty != null) {
  46 + return dbProperty.getId();
46 47 }
47 48  
48 49 // 插入
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueService.java
... ... @@ -19,6 +19,7 @@ public interface ProductPropertyValueService {
19 19  
20 20 /**
21 21 * 创建属性值
  22 + * 注意,如果已经存在该属性值,直接返回它的编号即可
22 23 *
23 24 * @param createReqVO 创建信息
24 25 * @return 编号
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
... ... @@ -42,9 +42,11 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
42 42  
43 43 @Override
44 44 public Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO) {
45   - // 校验名字唯一
46   - if (productPropertyValueMapper.selectByName(createReqVO.getPropertyId(), createReqVO.getName()) != null) {
47   - throw exception(PROPERTY_VALUE_EXISTS);
  45 + // 如果已经添加过该属性值,直接返回
  46 + ProductPropertyValueDO dbValue = productPropertyValueMapper.selectByName(
  47 + createReqVO.getPropertyId(), createReqVO.getName());
  48 + if (dbValue != null) {
  49 + return dbValue.getId();
48 50 }
49 51  
50 52 // 新增
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
... ... @@ -42,14 +42,6 @@ public interface ProductSpuService {
42 42 void deleteSpu(Long id);
43 43  
44 44 /**
45   - * 获得商品 SPU 详情
46   - *
47   - * @param id 编号
48   - * @return 商品 SPU
49   - */
50   - ProductSpuDetailRespVO getSpuDetail(Long id);
51   -
52   - /**
53 45 * 获得商品 SPU
54 46 *
55 47 * @param id 编号
... ... @@ -88,7 +80,7 @@ public interface ProductSpuService {
88 80 * @param pageReqVO 分页查询
89 81 * @return 商品spu分页
90 82 */
91   - PageResult<ProductSpuRespVO> getSpuPage(ProductSpuPageReqVO pageReqVO);
  83 + PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);
92 84  
93 85 /**
94 86 * 获得商品 SPU 分页
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 1 package cn.iocoder.yudao.module.product.service.spu;
2 2  
3   -import cn.hutool.core.bean.BeanUtil;
4 3 import cn.hutool.core.collection.CollUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7   -import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
8   -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
9 6 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
10   -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
11   -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  7 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
  8 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
  9 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
12 10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
13   -import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
14 11 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
15   -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
16   -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
17 12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
18 13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
19 14 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
20   -import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
21 15 import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
22 16 import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
23   -import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
24   -import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
25 17 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
26 18 import org.springframework.context.annotation.Lazy;
27 19 import org.springframework.stereotype.Service;
... ... @@ -29,8 +21,10 @@ import org.springframework.transaction.annotation.Transactional;
29 21 import org.springframework.validation.annotation.Validated;
30 22  
31 23 import javax.annotation.Resource;
32   -import java.util.*;
33   -import java.util.stream.Collectors;
  24 +import java.util.Collection;
  25 +import java.util.List;
  26 +import java.util.Map;
  27 +import java.util.Set;
34 28  
35 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36 30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
... ... @@ -50,17 +44,12 @@ public class ProductSpuServiceImpl implements ProductSpuService {
50 44 private ProductSpuMapper productSpuMapper;
51 45  
52 46 @Resource
53   - private ProductCategoryService categoryService;
54   -
55   - @Resource
56 47 @Lazy // 循环依赖,避免报错
57 48 private ProductSkuService productSkuService;
58 49 @Resource
59   - private ProductPropertyService productPropertyService;
60   - @Resource
61   - private ProductPropertyValueService productPropertyValueService;
62   - @Resource
63 50 private ProductBrandService brandService;
  51 + @Resource
  52 + private ProductCategoryService categoryService;
64 53  
65 54 @Override
66 55 @Transactional(rollbackFor = Exception.class)
... ... @@ -149,46 +138,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
149 138 }
150 139  
151 140 @Override
152   - // TODO @芋艿:需要再 review 下
153   - public ProductSpuDetailRespVO getSpuDetail(Long id) {
154   - ProductSpuDO spu = productSpuMapper.selectById(id);
155   - ProductSpuDetailRespVO respVO = BeanUtil.copyProperties(spu, ProductSpuDetailRespVO.class);
156   - if (null != spu) {
157   - List<ProductSpuDetailRespVO.Sku> skuReqs = ProductSkuConvert.INSTANCE.convertList03(productSkuService.getSkuListBySpuId(id));
158   - respVO.setSkus(skuReqs);
159   - // 组合 sku 属性
160   - if (spu.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) {
161   - List<ProductSkuRespVO.Property> properties = new ArrayList<>();
162   - for (ProductSpuDetailRespVO.Sku productSkuRespVO : skuReqs) {
163   - properties.addAll(productSkuRespVO.getProperties());
164   - }
165   - Map<Long, List<ProductSkuBaseVO.Property>> propertyMaps = properties.stream().collect(Collectors.groupingBy(ProductSkuBaseVO.Property::getPropertyId));
166   -
167   - List<ProductPropertyValueDO> propertyValueList = productPropertyValueService.getPropertyValueListByPropertyId(propertyMaps.keySet());
168   - List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyMaps.keySet());
169   - // 装载组装过后的属性
170   - List<ProductPropertyViewRespVO> productPropertyViews = new ArrayList<>();
171   - propertyList.forEach(p -> {
172   - ProductPropertyViewRespVO productPropertyViewRespVO = new ProductPropertyViewRespVO();
173   - productPropertyViewRespVO.setPropertyId(p.getId());
174   - productPropertyViewRespVO.setName(p.getName());
175   - List<ProductPropertyViewRespVO.Tuple2> propertyValues = new ArrayList<>();
176   - // 转换成map是为了能快速获取
177   - Map<Long, ProductPropertyValueDO> propertyValueMaps = convertMap(propertyValueList, ProductPropertyValueDO::getId);
178   - propertyMaps.get(p.getId()).forEach(pv -> {
179   - ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName());
180   - propertyValues.add(tuple2);
181   - });
182   - productPropertyViewRespVO.setPropertyValues(propertyValues.stream().distinct().collect(Collectors.toList()));
183   - productPropertyViews.add(productPropertyViewRespVO);
184   - });
185   - respVO.setProductPropertyViews(productPropertyViews);
186   - }
187   - }
188   - return respVO;
189   - }
190   -
191   - @Override
192 141 public ProductSpuDO getSpu(Long id) {
193 142 return productSpuMapper.selectById(id);
194 143 }
... ... @@ -203,9 +152,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
203 152 return productSpuMapper.selectList();
204 153 }
205 154  
206   - // TODO 芋艿:改成 DO 返回
207 155 @Override
208   - public PageResult<ProductSpuRespVO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
  156 + public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
209 157 // 库存告警的 SPU 编号的集合
210 158 Set<Long> alarmStockSpuIds = null;
211 159 if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) {
... ... @@ -215,7 +163,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
215 163 }
216 164 }
217 165 // 分页查询
218   - return ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds));
  166 + return productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds);
219 167 }
220 168  
221 169 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java
... ... @@ -19,11 +19,12 @@ import java.util.Arrays;
19 19 import java.util.List;
20 20  
21 21 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  22 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
22 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  24 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
23 25 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
24 26 import static java.util.Collections.singletonList;
25   -import static org.junit.jupiter.api.Assertions.assertEquals;
26   -import static org.junit.jupiter.api.Assertions.assertNotEquals;
  27 +import static org.junit.jupiter.api.Assertions.*;
27 28 import static org.mockito.ArgumentMatchers.argThat;
28 29 import static org.mockito.Mockito.verify;
29 30  
... ... @@ -145,4 +146,26 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
145 146 SKU_STOCK_NOT_ENOUGH);
146 147 }
147 148  
  149 + @Test
  150 + public void testDeleteSku_success() {
  151 + // mock 数据
  152 + ProductSkuDO dbSku = randomPojo(ProductSkuDO.class);
  153 + productSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据
  154 + // 准备参数
  155 + Long id = dbSku.getId();
  156 +
  157 + // 调用
  158 + productSkuService.deleteSku(id);
  159 + // 校验数据不存在了
  160 + assertNull(productSkuMapper.selectById(id));
  161 + }
  162 +
  163 + @Test
  164 + public void testDeleteSku_notExists() {
  165 + // 准备参数
  166 + Long id = 1L;
  167 +
  168 + // 调用, 并断言异常
  169 + assertServiceException(() -> productSkuService.deleteSku(id), SKU_NOT_EXISTS);
  170 + }
148 171 }
... ...
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.product.service.sku;
2   -
3   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
4   -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
5   -import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
6   -import org.junit.jupiter.api.Disabled;
7   -import org.junit.jupiter.api.Test;
8   -import org.springframework.context.annotation.Import;
9   -
10   -import javax.annotation.Resource;
11   -
12   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
13   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
14   -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
15   -import static org.junit.jupiter.api.Assertions.assertNull;
16   -
17   -// TODO 芋艿:整合到 {@link ProductSkuServiceTest} 中
18   -/**
19   -* {@link ProductSkuServiceImpl} 的单元测试类
20   -*
21   -* @author 芋道源码
22   -*/
23   -@Import(ProductSkuServiceImpl.class)
24   -@Disabled // TODO 芋艿:临时去掉
25   -public class SkuServiceImplTest extends BaseDbUnitTest {
26   -
27   - @Resource
28   - private ProductSkuServiceImpl ProductSkuService;
29   -
30   - @Resource
31   - private ProductSkuMapper ProductSkuMapper;
32   -
33   - @Test
34   - public void testDeleteSku_success() {
35   - // mock 数据
36   - ProductSkuDO dbSku = randomPojo(ProductSkuDO.class);
37   - ProductSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据
38   - // 准备参数
39   - Long id = dbSku.getId();
40   -
41   - // 调用
42   - ProductSkuService.deleteSku(id);
43   - // 校验数据不存在了
44   - assertNull(ProductSkuMapper.selectById(id));
45   - }
46   -
47   - @Test
48   - public void testDeleteSku_notExists() {
49   - // 准备参数
50   - Long id = 1L;
51   -
52   - // 调用, 并断言异常
53   - assertServiceException(() -> ProductSkuService.deleteSku(id), SKU_NOT_EXISTS);
54   - }
55   -
56   -}
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
... ... @@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 9 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
10 10 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
11   -import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO;
12   -import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO;
13 11 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
14   -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  12 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
  13 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
  14 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;
  15 +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
15 16 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
16 17 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
17 18 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
... ... @@ -149,53 +150,6 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
149 150 }
150 151  
151 152 @Test
152   - void getSpuDetail() {
153   - // 准备spu参数
154   - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> {
155   - o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
156   - });
157   - productSpuMapper.insert(createReqVO);
158   -
159   - // 创建两个属性
160   - ArrayList<ProductPropertyRespVO> productPropertyRespVOS = Lists.newArrayList(
161   - randomPojo(ProductPropertyRespVO.class),
162   - randomPojo(ProductPropertyRespVO.class));
163   -
164   - // 所有属性值
165   - ArrayList<ProductPropertyValueRespVO> productPropertyValueRespVO = new ArrayList<>();
166   -
167   - // 每个属性创建属性值
168   - productPropertyRespVOS.forEach(v -> {
169   - ProductPropertyValueRespVO productPropertyValueRespVO1 = randomPojo(ProductPropertyValueRespVO.class, o -> o.setPropertyId(v.getId()));
170   - productPropertyValueRespVO.add(productPropertyValueRespVO1);
171   - });
172   -
173   - // 属性值建立笛卡尔积
174   - Map<Long, List<ProductPropertyValueRespVO>> collect = productPropertyValueRespVO.stream().collect(Collectors.groupingBy(ProductPropertyValueRespVO::getPropertyId));
175   - List<List<ProductPropertyValueRespVO>> lists = cartesianProduct(Lists.newArrayList(collect.values()));
176   -
177   - // 准备sku参数
178   - ArrayList<ProductSkuDO> productSkuDOS = Lists.newArrayList();
179   - lists.forEach(pp -> {
180   - List<ProductSkuDO.Property> property = pp.stream().map(ppv -> new ProductSkuDO.Property(ppv.getPropertyId(), ppv.getId())).collect(Collectors.toList());
181   - ProductSkuDO productSkuDO = randomPojo(ProductSkuDO.class, o -> {
182   - o.setProperties(property);
183   - });
184   - productSkuDOS.add(productSkuDO);
185   -
186   - });
187   -
188   - Mockito.when(productSkuService.getSkuListBySpuId(createReqVO.getId())).thenReturn(productSkuDOS);
189   -// Mockito.when(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyValueRespVO);
190   -// Mockito.when(productPropertyService.getPropertyVOList(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyRespVOS);
191   -//
192   - // 调用
193   - ProductSpuDetailRespVO spuDetail = productSpuService.getSpuDetail(createReqVO.getId());
194   -
195   - assertPojoEquals(createReqVO, spuDetail);
196   - }
197   -
198   - @Test
199 153 void getSpu() {
200 154 // 准备参数
201 155 ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
... ... @@ -222,7 +176,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
222 176 ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
223 177 productSpuPageReqVO.setAlarmStock(true);
224 178  
225   - PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
  179 + PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
226 180  
227 181 PageResult<Object> result = PageResult.empty();
228 182 Assertions.assertIterableEquals(result.getList(), spuPage.getList());
... ... @@ -271,7 +225,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
271 225 // 调用
272 226 ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
273 227 productSpuPageReqVO.setAlarmStock(true);
274   - PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
  228 + PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
275 229  
276 230 PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds));
277 231 Assertions.assertIterableEquals(result.getList(), spuPage.getList());
... ... @@ -323,7 +277,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
323 277 productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
324 278 productSpuPageReqVO.setCategoryId(categoryId);
325 279  
326   - PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
  280 + PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
327 281  
328 282 PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set<Long>) null));
329 283 assertEquals(result, spuPage);
... ...
yudao-module-system/yudao-module-system-biz/pom.xml
... ... @@ -54,6 +54,10 @@
54 54 <groupId>cn.iocoder.boot</groupId>
55 55 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
56 56 </dependency>
  57 + <dependency>
  58 + <groupId>cn.iocoder.boot</groupId>
  59 + <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
  60 + </dependency>
57 61  
58 62 <!-- Web 相关 -->
59 63 <dependency>
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http 0 → 100644
  1 +### 获得地区树
  2 +GET {{baseUrl}}/system/area/tree
  3 +Authorization: Bearer {{token}}
  4 +tenant-id: {{adminTenentId}}
  5 +
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.ip;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.ip.core.Area;
  6 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
  7 +import cn.iocoder.yudao.framework.ip.core.utils.IPUtils;
  8 +import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
  9 +import cn.iocoder.yudao.module.system.convert.ip.AreaConvert;
  10 +import io.swagger.annotations.Api;
  11 +import io.swagger.annotations.ApiImplicitParam;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.validation.annotation.Validated;
  14 +import org.springframework.web.bind.annotation.GetMapping;
  15 +import org.springframework.web.bind.annotation.RequestMapping;
  16 +import org.springframework.web.bind.annotation.RequestParam;
  17 +import org.springframework.web.bind.annotation.RestController;
  18 +
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - 地区")
  24 +@RestController
  25 +@RequestMapping("/system/area")
  26 +@Validated
  27 +public class AreaController {
  28 +
  29 + @GetMapping("/tree")
  30 + @ApiOperation("获得地区树")
  31 + public CommonResult<List<AreaNodeRespVO>> getAreaTree() {
  32 + Area area = AreaUtils.getArea(Area.ID_CHINA);
  33 + Assert.notNull(area, "获取不到中国");
  34 + return success(AreaConvert.INSTANCE.convertList(area.getChildren()));
  35 + }
  36 +
  37 + @GetMapping("/get-by-ip")
  38 + @ApiOperation("获得 IP 对应的地区名")
  39 + @ApiImplicitParam(name = "ip", value = "IP", required = true, dataTypeClass = String.class)
  40 + public CommonResult<String> getAreaByIp(@RequestParam("ip") String ip) {
  41 + // 获得城市
  42 + Area area = IPUtils.getArea(ip);
  43 + if (area == null) {
  44 + return success("未知");
  45 + }
  46 + // 格式化返回
  47 + return success(AreaUtils.format(area.getId()));
  48 + }
  49 +
  50 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.ip.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +@ApiModel("管理后台 - 地区节点 Response VO")
  10 +@Data
  11 +public class AreaNodeRespVO {
  12 +
  13 + @ApiModelProperty(value = "编号", required = true, example = "110000")
  14 + private Integer id;
  15 +
  16 + @ApiModelProperty(value = "名字", required = true, example = "北京")
  17 + private String name;
  18 +
  19 + /**
  20 + * 子节点
  21 + */
  22 + private List<AreaNodeRespVO> children;
  23 +
  24 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/ip/AreaConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.ip;
  2 +
  3 +import cn.iocoder.yudao.framework.ip.core.Area;
  4 +import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
  5 +import org.mapstruct.Mapper;
  6 +import org.mapstruct.factory.Mappers;
  7 +
  8 +import java.util.List;
  9 +
  10 +@Mapper
  11 +public interface AreaConvert {
  12 +
  13 + AreaConvert INSTANCE = Mappers.getMapper(AreaConvert.class);
  14 +
  15 + List<AreaNodeRespVO> convertList(List<Area> list);
  16 +
  17 +}
... ...
yudao-ui-admin/src/api/mall/product/spu.js
... ... @@ -26,18 +26,10 @@ export function deleteSpu(id) {
26 26 })
27 27 }
28 28  
29   -// 获得商品spu
30   -export function getSpu(id) {
31   - return request({
32   - url: '/product/spu/get?id=' + id,
33   - method: 'get'
34   - })
35   -}
36   -
37 29 // 获得商品 SPU 详情
38 30 export function getSpuDetail(id) {
39 31 return request({
40   - url: '/product/spu/get/detail?id=' + id,
  32 + url: '/product/spu/get-detail?id=' + id,
41 33 method: 'get'
42 34 })
43 35 }
... ...
yudao-ui-admin/src/api/system/area.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得地区树
  4 +export function getAreaTree() {
  5 + return request({
  6 + url: '/system/area/tree',
  7 + method: 'get'
  8 + })
  9 +}
  10 +
  11 +// 获得 IP 对应的地区名
  12 +export function getAreaByIp(ip) {
  13 + return request({
  14 + url: '/system/area/get-by-ip?ip=' + ip,
  15 + method: 'get'
  16 + })
  17 +}
... ...
yudao-ui-admin/src/views/mall/product/spu/save.vue
... ... @@ -4,9 +4,8 @@
4 4 <el-tabs v-model="activeName" class="tabs">
5 5 <!-- 基础设置 -->
6 6 <!-- TODO @luowenfeng:基础设置,分成基础信息、配送信息 -->
7   - <!-- TODO @luowenfeng:base=》basic 会更好哈 -->
8   - <el-tab-pane label="基础设置" name="base">
9   - <el-form ref="base" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
  7 + <el-tab-pane label="基础设置" name="basic">
  8 + <el-form ref="basic" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
10 9 <el-form-item label="商品名称" prop="name">
11 10 <el-input v-model="baseForm.name" placeholder="请输入商品名称" />
12 11 </el-form-item>
... ... @@ -155,9 +154,8 @@
155 154 </el-tab-pane>
156 155  
157 156 <!-- 商品详情 -->
158   - <!-- TODO @luowenfeng:third=》detail 会更好哈 -->
159   - <el-tab-pane label="商品详情" name="third">
160   - <el-form ref="third" :model="baseForm" :rules="rules">
  157 + <el-tab-pane label="商品详情" name="detail">
  158 + <el-form ref="detail" :model="baseForm" :rules="rules">
161 159 <el-form-item prop="description">
162 160 <editor v-model="baseForm.description" :min-height="380"/>
163 161 </el-form-item>
... ... @@ -165,9 +163,8 @@
165 163 </el-tab-pane>
166 164  
167 165 <!-- 销售设置 -->
168   - <!-- TODO @luowenfeng:fourth=》senior 会更好哈 -->
169   - <el-tab-pane label="高级设置" name="fourth">
170   - <el-form ref="fourth" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
  166 + <el-tab-pane label="高级设置" name="senior">
  167 + <el-form ref="senior" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
171 168 <el-form-item label="排序字段">
172 169 <el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
173 170 </el-form-item>
... ... @@ -208,7 +205,7 @@ export default {
208 205 data() {
209 206 return {
210 207 specSwitch: false,
211   - activeName: "base",
  208 + activeName: "basic",
212 209 propName: {
213 210 checkStrictly: true,
214 211 label: "name",
... ...
yudao-ui-admin/src/views/system/area/index.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 操作工具栏 -->
  5 + <el-row :gutter="10" class="mb8">
  6 + <el-col :span="1.5">
  7 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">IP 查询
  8 + </el-button>
  9 + </el-col>
  10 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  11 + </el-row>
  12 +
  13 + <!-- 列表 -->
  14 + <el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id"
  15 + :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
  16 + <el-table-column label="编号" prop="id"/>
  17 + <el-table-column label="名字" prop="name"/>
  18 + </el-table>
  19 +
  20 + <!-- 对话框(添加 / 修改) -->
  21 + <el-dialog title="IP 查询" :visible.sync="open" width="500px" append-to-body>
  22 + <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  23 + <el-form-item label="IP" prop="ip">
  24 + <el-input v-model="form.ip" placeholder="请输入 IP 地址"/>
  25 + </el-form-item>
  26 + <el-form-item label="地址" prop="result">
  27 + <el-input v-model="form.result" readonly placeholder="展示查询 IP 结果" />
  28 + </el-form-item>
  29 + </el-form>
  30 + <div slot="footer" class="dialog-footer">
  31 + <el-button type="primary" @click="submitForm">查 询</el-button>
  32 + <el-button @click="cancel">取 消</el-button>
  33 + </div>
  34 + </el-dialog>
  35 + </div>
  36 +</template>
  37 +
  38 +<script>
  39 +import {getAreaByIp, getAreaTree} from "@/api/system/area";
  40 +
  41 +export default {
  42 + name: "Area",
  43 + components: {
  44 + },
  45 + data() {
  46 + return {
  47 + // 遮罩层
  48 + loading: true,
  49 + // 显示搜索条件
  50 + showSearch: true,
  51 + // 地区列表
  52 + list: [],
  53 + // 弹出层标题
  54 + title: "",
  55 + // 是否显示弹出层
  56 + open: false,
  57 + // 重新渲染表格状态
  58 + refreshTable: true,
  59 + // 表单参数
  60 + form: {
  61 + ip: undefined,
  62 + result: undefined,
  63 + },
  64 + // 表单校验
  65 + rules: {
  66 + ip: [{required: true, message: "IP 地址不能为控", trigger: "blur"}],
  67 + }
  68 + };
  69 + },
  70 + created() {
  71 + this.getList();
  72 + },
  73 + methods: {
  74 + /** 查询列表 */
  75 + getList() {
  76 + this.loading = true;
  77 + getAreaTree().then(response => {
  78 + this.list = response.data;
  79 + this.loading = false;
  80 + })
  81 + },
  82 + /** 取消按钮 */
  83 + cancel() {
  84 + this.open = false;
  85 + this.reset();
  86 + },
  87 + /** 表单重置 */
  88 + reset() {
  89 + this.form = {
  90 + ip: undefined,
  91 + result: undefined,
  92 + };
  93 + this.resetForm("form");
  94 + },
  95 + /** 新增按钮操作 */
  96 + handleAdd() {
  97 + this.reset();
  98 + this.open = true;
  99 + },
  100 + /** 提交按钮 */
  101 + submitForm() {
  102 + this.$refs["form"].validate(valid => {
  103 + if (!valid) {
  104 + return;
  105 + }
  106 + getAreaByIp(this.form.ip).then(response => {
  107 + this.$modal.msgSuccess("查询成功");
  108 + this.form.result = response.data
  109 + });
  110 + });
  111 + }
  112 + }
  113 +};
  114 +</script>
... ...