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

area:增加地区列表界面
pull/2/head
YunaiV 2022-12-23 18:47:59 +08:00
parent 92dde0b48b
commit 7a4a6a3046
31 changed files with 401 additions and 339 deletions

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.framework.ip.core.utils; package cn.iocoder.yudao.framework.ip.core.utils;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.csv.CsvRow; import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil; import cn.hutool.core.text.csv.CsvUtil;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@ -50,6 +51,7 @@ public class AreaUtils {
for (CsvRow row : rows) { for (CsvRow row : rows) {
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己 Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
Area parent = areas.get(Integer.valueOf(row.get(3))); // 父 Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
area.setParent(parent); area.setParent(parent);
parent.getChildren().add(area); parent.getChildren().add(area);
} }

View File

@ -2567,8 +2567,6 @@ id,name,type,parentId
441826,连南瑶族自治县,4,441800 441826,连南瑶族自治县,4,441800
441881,英德市,4,441800 441881,英德市,4,441800
441882,连州市,4,441800 441882,连州市,4,441800
441900,东莞市,4,441900
442000,中山市,4,442000
445102,湘桥区,4,445100 445102,湘桥区,4,445100
445103,潮安区,4,445100 445103,潮安区,4,445100
445122,饶平县,4,445100 445122,饶平县,4,445100
@ -2704,7 +2702,6 @@ id,name,type,parentId
460321,西沙群岛,4,460300 460321,西沙群岛,4,460300
460322,南沙群岛,4,460300 460322,南沙群岛,4,460300
460323,中沙群岛的岛礁及其海域,4,460300 460323,中沙群岛的岛礁及其海域,4,460300
460400,儋州市,4,460400
469001,五指山市,4,469000 469001,五指山市,4,469000
469002,琼海市,4,469000 469002,琼海市,4,469000
469005,文昌市,4,469000 469005,文昌市,4,469000

1 id name type parentId
2567 441826 连南瑶族自治县 4 441800
2568 441881 英德市 4 441800
2569 441882 连州市 4 441800
441900 东莞市 4 441900
442000 中山市 4 442000
2570 445102 湘桥区 4 445100
2571 445103 潮安区 4 445100
2572 445122 饶平县 4 445100
2702 460321 西沙群岛 4 460300
2703 460322 南沙群岛 4 460300
2704 460323 中沙群岛的岛礁及其海域 4 460300
460400 儋州市 4 460400
2705 469001 五指山市 4 469000
2706 469002 琼海市 4 469000
2707 469005 文昌市 4 469000

View File

@ -33,6 +33,10 @@ public class AssertUtils {
public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) { public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) {
Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
Arrays.stream(expectedFields).forEach(expectedField -> { Arrays.stream(expectedFields).forEach(expectedField -> {
// 忽略 jacoco 自动生成的 $jacocoData 属性的情况
if (expectedField.isSynthetic()) {
return;
}
// 如果是忽略的属性,则不进行比对 // 如果是忽略的属性,则不进行比对
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {
return; return;

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.ToString;
import java.util.List;
// TODO 芋艿:看看怎么删除
@ApiModel("管理后台 - 商品属性详情展示 Request VO")
@Data
@ToString(callSuper = true)
public class ProductPropertyViewRespVO {
@ApiModelProperty(value = "属性项 id", example = "1")
public Long propertyId;
@ApiModelProperty(value = "属性项的名字", example = "内存")
public String name;
@ApiModelProperty(value = "属性值数组")
public List<Tuple2> propertyValues;
@Data
@ApiModel(value = "属性值元组")
public static class Tuple2 {
private final long id;
private final String name;
public Tuple2(Long id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.product.controller.admin.property.vo.value;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel("管理后台 - 商品属性值的明细 Response VO")
@Data
public class ProductPropertyValueDetailRespVO {
@ApiModelProperty(value = "属性的编号", required = true, example = "1")
private Long propertyId;
@ApiModelProperty(value = "属性的名称", required = true, example = "颜色")
private String propertyName;
@ApiModelProperty(value = "属性值的编号", required = true, example = "1024")
private Long valueId;
@ApiModelProperty(value = "属性值的名称", required = true, example = "红色")
private String valueName;
}

View File

@ -0,0 +1,4 @@
### 获得商品 SPU 明细
GET {{baseUrl}}/product/spu/get-detail?id=4
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@ -3,8 +3,13 @@ package cn.iocoder.yudao.module.product.controller.admin.spu;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
@ -15,10 +20,11 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
@Api(tags = "管理后台 - 商品 SPU") @Api(tags = "管理后台 - 商品 SPU")
@RestController @RestController
@ -28,6 +34,10 @@ public class ProductSpuController {
@Resource @Resource
private ProductSpuService productSpuService; private ProductSpuService productSpuService;
@Resource
private ProductSkuService productSkuService;
@Resource
private ProductPropertyValueService productPropertyValueService;
@PostMapping("/create") @PostMapping("/create")
@ApiOperation("创建商品 SPU") @ApiOperation("创建商品 SPU")
@ -53,31 +63,24 @@ public class ProductSpuController {
return success(true); return success(true);
} }
// TODO 芋艿:修改接口 @GetMapping("/get-detail")
@GetMapping("/get/detail") @ApiOperation("获得商品 SPU 明细")
@ApiOperation("获得商品 SPU")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('product:spu:query')") @PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<ProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) { public CommonResult<ProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
return success(productSpuService.getSpuDetail(id)); // 获得商品 SPU
ProductSpuDO spu = productSpuService.getSpu(id);
if (spu == null) {
throw exception(SPU_NOT_EXISTS);
} }
@GetMapping("/get") // 查询商品 SKU
@ApiOperation("获得商品 SPU") List<ProductSkuDO> skus = productSkuService.getSkuListBySpuIdAndStatus(spu.getId(), null);
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) // 查询商品属性
@PreAuthorize("@ss.hasPermission('product:spu:query')") List<ProductPropertyValueDetailRespBO> propertyValues = productPropertyValueService
public CommonResult<ProductSpuDO> getSpu(@RequestParam("id") Long id) { .getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
return success(productSpuService.getSpu(id)); // 拼接
} return success(ProductSpuConvert.INSTANCE.convert03(spu, skus, propertyValues));
@GetMapping("/list")
@ApiOperation("获得商品 SPU 列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<List<ProductSpuDO>> getSpuList(@RequestParam("ids") Collection<Long> ids) {
List<ProductSpuDO> list = productSpuService.getSpuList(ids);
return success(ProductSpuConvert.INSTANCE.convertList(list));
} }
@GetMapping("/get-simple-list") @GetMapping("/get-simple-list")
@ -92,7 +95,7 @@ public class ProductSpuController {
@ApiOperation("获得商品 SPU 分页") @ApiOperation("获得商品 SPU 分页")
@PreAuthorize("@ss.hasPermission('product:spu:query')") @PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) { public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
return success(productSpuService.getSpuPage(pageVO)); return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO)));
} }
} }

View File

@ -1,52 +1,27 @@
package cn.iocoder.yudao.module.product.controller.admin.spu.vo; package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@ApiModel(value = "管理后台 - 商品 SPU 详细 Response VO", description = "包括关联的 SKU 等信息") @ApiModel(value = "管理后台 - 商品 SPU 详细 Response VO", description = "包括关联的 SKU 等信息")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class ProductSpuDetailRespVO extends ProductSpuBaseVO { public class ProductSpuDetailRespVO extends ProductSpuRespVO {
@ApiModelProperty(value = "主键", required = true, example = "1")
private Long id;
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
// ========== SKU 相关字段 ========= // ========== SKU 相关字段 =========
@ApiModelProperty(value = "库存", required = true, example = "true")
private Integer totalStock;
@ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
private Integer minPrice;
@ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
private Integer maxPrice;
@ApiModelProperty(value = "商品销量", example = "1024")
private Integer salesCount;
/** /**
* SKU * SKU
*/ */
private List<Sku> skus; private List<Sku> skus;
// ========== 统计相关字段 =========
@ApiModelProperty(value = "点击量", example = "1024")
private Integer clickCount;
@ApiModel(value = "管理后台 - 商品 SKU 详细 Response VO") @ApiModel(value = "管理后台 - 商品 SKU 详细 Response VO")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -56,29 +31,8 @@ public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
/** /**
* *
*/ */
private List<ProductSpuDetailRespVO.Property> properties; private List<ProductPropertyValueDetailRespVO> properties;
} }
@ApiModel(value = "管理后台 - 商品属性的详细 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public static class Property extends ProductSkuBaseVO.Property {
@ApiModelProperty(value = "属性项的名字", required = true, example = "颜色")
private String propertyName;
@ApiModelProperty(value = "属性值的名称", required = true, example = "蓝色")
private String valueName;
}
@ApiModelProperty(value = "分类 id 数组,一直递归到一级父节点", example = "4")
private Long categoryId;
// TODO @芋艿:在瞅瞅~
@ApiModelProperty(value = "属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]")
private List<ProductPropertyViewRespVO> productPropertyViews;
} }

View File

@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuDetailRespVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageItemRespVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; 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.sku.ProductSkuDO;
@ -48,7 +48,7 @@ public class AppProductSpuController {
@GetMapping("/page") @GetMapping("/page")
@ApiOperation("获得商品 SPU 分页") @ApiOperation("获得商品 SPU 分页")
public CommonResult<PageResult<AppSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { public CommonResult<PageResult<AppProductSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus()); PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus());
return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult)); return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult));
} }
@ -56,7 +56,7 @@ public class AppProductSpuController {
@GetMapping("/get-detail") @GetMapping("/get-detail")
@ApiOperation("获得商品 SPU 明细") @ApiOperation("获得商品 SPU 明细")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
public CommonResult<AppSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) { public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
// 获得商品 SPU // 获得商品 SPU
ProductSpuDO spu = productSpuService.getSpu(id); ProductSpuDO spu = productSpuService.getSpu(id);
if (spu == null) { if (spu == null) {

View File

@ -9,7 +9,7 @@ import java.util.List;
@ApiModel("用户 App - 商品 SPU 明细 Response VO") @ApiModel("用户 App - 商品 SPU 明细 Response VO")
@Data @Data
public class AppSpuDetailRespVO { public class AppProductSpuDetailRespVO {
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1") @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
private Long id; private Long id;

View File

@ -10,7 +10,7 @@ import java.util.List;
@ApiModel("用户 App - 商品 SPU 分页项 Response VO") @ApiModel("用户 App - 商品 SPU 分页项 Response VO")
@Data @Data
public class AppSpuPageItemRespVO { public class AppProductSpuPageItemRespVO {
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1") @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
private Long id; private Long id;

View File

@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.product.convert.spu;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO; import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuDetailRespVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageItemRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
@ -45,9 +46,9 @@ public interface ProductSpuConvert {
List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list); List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list);
default AppSpuDetailRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus, default AppProductSpuDetailRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus,
List<ProductPropertyValueDetailRespBO> propertyValues) { List<ProductPropertyValueDetailRespBO> propertyValues) {
AppSpuDetailRespVO spuVO = convert02(spu) AppProductSpuDetailRespVO spuVO = convert02(spu)
.setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0)); .setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0));
spuVO.setSkus(convertList03(skus)); spuVO.setSkus(convertList03(skus));
// 处理商品属性 // 处理商品属性
@ -57,7 +58,7 @@ public interface ProductSpuConvert {
if (CollUtil.isEmpty(properties)) { if (CollUtil.isEmpty(properties)) {
continue; continue;
} }
AppSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i); AppProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
sku.setProperties(new ArrayList<>(properties.size())); sku.setProperties(new ArrayList<>(properties.size()));
// 遍历每个 properties设置到 AppSpuDetailRespVO.Sku 中 // 遍历每个 properties设置到 AppSpuDetailRespVO.Sku 中
properties.forEach(property -> { properties.forEach(property -> {
@ -70,10 +71,38 @@ public interface ProductSpuConvert {
} }
return spuVO; return spuVO;
} }
AppSpuDetailRespVO convert02(ProductSpuDO spu); AppProductSpuDetailRespVO convert02(ProductSpuDO spu);
List<AppSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus); List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue); AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
PageResult<AppSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page); PageResult<AppProductSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);
default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
List<ProductPropertyValueDetailRespBO> propertyValues) {
ProductSpuDetailRespVO spuVO = convert03(spu);
spuVO.setSkus(convertList04(skus));
// 处理商品属性
Map<Long, ProductPropertyValueDetailRespBO> propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
for (int i = 0; i < skus.size(); i++) {
List<ProductSkuDO.Property> properties = skus.get(i).getProperties();
if (CollUtil.isEmpty(properties)) {
continue;
}
ProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
sku.setProperties(new ArrayList<>(properties.size()));
// 遍历每个 properties设置到 AppSpuDetailRespVO.Sku 中
properties.forEach(property -> {
ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
if (propertyValue == null) {
return;
}
sku.getProperties().add(convert04(propertyValue));
});
}
return spuVO;
}
ProductSpuDetailRespVO convert03(ProductSpuDO spu);
List<ProductSpuDetailRespVO.Sku> convertList04(List<ProductSkuDO> skus);
ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
} }

View File

@ -21,8 +21,7 @@ public interface ProductPropertyMapper extends BaseMapperX<ProductPropertyDO> {
} }
default ProductPropertyDO selectByName(String name) { default ProductPropertyDO selectByName(String name) {
return selectOne(new LambdaQueryWrapperX<ProductPropertyDO>() return selectOne(ProductPropertyDO::getName, name);
.eqIfPresent(ProductPropertyDO::getName, name));
} }
default List<ProductPropertyDO> selectList(ProductPropertyListReqVO listReqVO) { default List<ProductPropertyDO> selectList(ProductPropertyListReqVO listReqVO) {

View File

@ -17,6 +17,7 @@ public interface ProductPropertyService {
/** /**
* *
*
* *
* @param createReqVO * @param createReqVO
* @return * @return

View File

@ -40,9 +40,10 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Long createProperty(ProductPropertyCreateReqVO createReqVO) { public Long createProperty(ProductPropertyCreateReqVO createReqVO) {
// 校验名字重复 // 如果已经添加过该属性项,直接返回
if (productPropertyMapper.selectByName(createReqVO.getName()) != null) { ProductPropertyDO dbProperty = productPropertyMapper.selectByName(createReqVO.getName());
throw exception(PROPERTY_EXISTS); if (dbProperty != null) {
return dbProperty.getId();
} }
// 插入 // 插入

View File

@ -19,6 +19,7 @@ public interface ProductPropertyValueService {
/** /**
* *
*
* *
* @param createReqVO * @param createReqVO
* @return * @return

View File

@ -42,9 +42,11 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
@Override @Override
public Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO) { public Long createPropertyValue(ProductPropertyValueCreateReqVO createReqVO) {
// 校验名字唯一 // 如果已经添加过该属性值,直接返回
if (productPropertyValueMapper.selectByName(createReqVO.getPropertyId(), createReqVO.getName()) != null) { ProductPropertyValueDO dbValue = productPropertyValueMapper.selectByName(
throw exception(PROPERTY_VALUE_EXISTS); createReqVO.getPropertyId(), createReqVO.getName());
if (dbValue != null) {
return dbValue.getId();
} }
// 新增 // 新增

View File

@ -41,14 +41,6 @@ public interface ProductSpuService {
*/ */
void deleteSpu(Long id); void deleteSpu(Long id);
/**
* SPU
*
* @param id
* @return SPU
*/
ProductSpuDetailRespVO getSpuDetail(Long id);
/** /**
* SPU * SPU
* *
@ -88,7 +80,7 @@ public interface ProductSpuService {
* @param pageReqVO * @param pageReqVO
* @return spu * @return spu
*/ */
PageResult<ProductSpuRespVO> getSpuPage(ProductSpuPageReqVO pageReqVO); PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);
/** /**
* SPU * SPU

View File

@ -1,27 +1,19 @@
package cn.iocoder.yudao.module.product.service.spu; package cn.iocoder.yudao.module.product.service.spu;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -29,8 +21,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.Collection;
import java.util.stream.Collectors; import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@ -49,18 +43,13 @@ public class ProductSpuServiceImpl implements ProductSpuService {
@Resource @Resource
private ProductSpuMapper productSpuMapper; private ProductSpuMapper productSpuMapper;
@Resource
private ProductCategoryService categoryService;
@Resource @Resource
@Lazy // 循环依赖,避免报错 @Lazy // 循环依赖,避免报错
private ProductSkuService productSkuService; private ProductSkuService productSkuService;
@Resource @Resource
private ProductPropertyService productPropertyService;
@Resource
private ProductPropertyValueService productPropertyValueService;
@Resource
private ProductBrandService brandService; private ProductBrandService brandService;
@Resource
private ProductCategoryService categoryService;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -148,46 +137,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
} }
} }
@Override
// TODO @芋艿:需要再 review 下
public ProductSpuDetailRespVO getSpuDetail(Long id) {
ProductSpuDO spu = productSpuMapper.selectById(id);
ProductSpuDetailRespVO respVO = BeanUtil.copyProperties(spu, ProductSpuDetailRespVO.class);
if (null != spu) {
List<ProductSpuDetailRespVO.Sku> skuReqs = ProductSkuConvert.INSTANCE.convertList03(productSkuService.getSkuListBySpuId(id));
respVO.setSkus(skuReqs);
// 组合 sku 属性
if (spu.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) {
List<ProductSkuRespVO.Property> properties = new ArrayList<>();
for (ProductSpuDetailRespVO.Sku productSkuRespVO : skuReqs) {
properties.addAll(productSkuRespVO.getProperties());
}
Map<Long, List<ProductSkuBaseVO.Property>> propertyMaps = properties.stream().collect(Collectors.groupingBy(ProductSkuBaseVO.Property::getPropertyId));
List<ProductPropertyValueDO> propertyValueList = productPropertyValueService.getPropertyValueListByPropertyId(propertyMaps.keySet());
List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyMaps.keySet());
// 装载组装过后的属性
List<ProductPropertyViewRespVO> productPropertyViews = new ArrayList<>();
propertyList.forEach(p -> {
ProductPropertyViewRespVO productPropertyViewRespVO = new ProductPropertyViewRespVO();
productPropertyViewRespVO.setPropertyId(p.getId());
productPropertyViewRespVO.setName(p.getName());
List<ProductPropertyViewRespVO.Tuple2> propertyValues = new ArrayList<>();
// 转换成map是为了能快速获取
Map<Long, ProductPropertyValueDO> propertyValueMaps = convertMap(propertyValueList, ProductPropertyValueDO::getId);
propertyMaps.get(p.getId()).forEach(pv -> {
ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName());
propertyValues.add(tuple2);
});
productPropertyViewRespVO.setPropertyValues(propertyValues.stream().distinct().collect(Collectors.toList()));
productPropertyViews.add(productPropertyViewRespVO);
});
respVO.setProductPropertyViews(productPropertyViews);
}
}
return respVO;
}
@Override @Override
public ProductSpuDO getSpu(Long id) { public ProductSpuDO getSpu(Long id) {
return productSpuMapper.selectById(id); return productSpuMapper.selectById(id);
@ -203,9 +152,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return productSpuMapper.selectList(); return productSpuMapper.selectList();
} }
// TODO 芋艿:改成 DO 返回
@Override @Override
public PageResult<ProductSpuRespVO> getSpuPage(ProductSpuPageReqVO pageReqVO) { public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
// 库存告警的 SPU 编号的集合 // 库存告警的 SPU 编号的集合
Set<Long> alarmStockSpuIds = null; Set<Long> alarmStockSpuIds = null;
if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) { if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) {
@ -215,7 +163,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
} }
} }
// 分页查询 // 分页查询
return ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds)); return productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds);
} }
@Override @Override

View File

@ -19,11 +19,12 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -145,4 +146,26 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
SKU_STOCK_NOT_ENOUGH); SKU_STOCK_NOT_ENOUGH);
} }
@Test
public void testDeleteSku_success() {
// mock 数据
ProductSkuDO dbSku = randomPojo(ProductSkuDO.class);
productSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSku.getId();
// 调用
productSkuService.deleteSku(id);
// 校验数据不存在了
assertNull(productSkuMapper.selectById(id));
}
@Test
public void testDeleteSku_notExists() {
// 准备参数
Long id = 1L;
// 调用, 并断言异常
assertServiceException(() -> productSkuService.deleteSku(id), SKU_NOT_EXISTS);
}
} }

View File

@ -1,56 +0,0 @@
package cn.iocoder.yudao.module.product.service.sku;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertNull;
// TODO 芋艿:整合到 {@link ProductSkuServiceTest} 中
/**
* {@link ProductSkuServiceImpl}
*
* @author
*/
@Import(ProductSkuServiceImpl.class)
@Disabled // TODO 芋艿:临时去掉
public class SkuServiceImplTest extends BaseDbUnitTest {
@Resource
private ProductSkuServiceImpl ProductSkuService;
@Resource
private ProductSkuMapper ProductSkuMapper;
@Test
public void testDeleteSku_success() {
// mock 数据
ProductSkuDO dbSku = randomPojo(ProductSkuDO.class);
ProductSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSku.getId();
// 调用
ProductSkuService.deleteSku(id);
// 校验数据不存在了
assertNull(ProductSkuMapper.selectById(id));
}
@Test
public void testDeleteSku_notExists() {
// 准备参数
Long id = 1L;
// 调用, 并断言异常
assertServiceException(() -> ProductSkuService.deleteSku(id), SKU_NOT_EXISTS);
}
}

View File

@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyRespVO;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; 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.sku.ProductSkuDO;
@ -148,53 +149,6 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId())); Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId()));
} }
@Test
void getSpuDetail() {
// 准备spu参数
ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> {
o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
});
productSpuMapper.insert(createReqVO);
// 创建两个属性
ArrayList<ProductPropertyRespVO> productPropertyRespVOS = Lists.newArrayList(
randomPojo(ProductPropertyRespVO.class),
randomPojo(ProductPropertyRespVO.class));
// 所有属性值
ArrayList<ProductPropertyValueRespVO> productPropertyValueRespVO = new ArrayList<>();
// 每个属性创建属性值
productPropertyRespVOS.forEach(v -> {
ProductPropertyValueRespVO productPropertyValueRespVO1 = randomPojo(ProductPropertyValueRespVO.class, o -> o.setPropertyId(v.getId()));
productPropertyValueRespVO.add(productPropertyValueRespVO1);
});
// 属性值建立笛卡尔积
Map<Long, List<ProductPropertyValueRespVO>> collect = productPropertyValueRespVO.stream().collect(Collectors.groupingBy(ProductPropertyValueRespVO::getPropertyId));
List<List<ProductPropertyValueRespVO>> lists = cartesianProduct(Lists.newArrayList(collect.values()));
// 准备sku参数
ArrayList<ProductSkuDO> productSkuDOS = Lists.newArrayList();
lists.forEach(pp -> {
List<ProductSkuDO.Property> property = pp.stream().map(ppv -> new ProductSkuDO.Property(ppv.getPropertyId(), ppv.getId())).collect(Collectors.toList());
ProductSkuDO productSkuDO = randomPojo(ProductSkuDO.class, o -> {
o.setProperties(property);
});
productSkuDOS.add(productSkuDO);
});
Mockito.when(productSkuService.getSkuListBySpuId(createReqVO.getId())).thenReturn(productSkuDOS);
// Mockito.when(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyValueRespVO);
// Mockito.when(productPropertyService.getPropertyVOList(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyRespVOS);
//
// 调用
ProductSpuDetailRespVO spuDetail = productSpuService.getSpuDetail(createReqVO.getId());
assertPojoEquals(createReqVO, spuDetail);
}
@Test @Test
void getSpu() { void getSpu() {
// 准备参数 // 准备参数
@ -222,7 +176,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
productSpuPageReqVO.setAlarmStock(true); productSpuPageReqVO.setAlarmStock(true);
PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
PageResult<Object> result = PageResult.empty(); PageResult<Object> result = PageResult.empty();
Assertions.assertIterableEquals(result.getList(), spuPage.getList()); Assertions.assertIterableEquals(result.getList(), spuPage.getList());
@ -271,7 +225,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
// 调用 // 调用
ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
productSpuPageReqVO.setAlarmStock(true); productSpuPageReqVO.setAlarmStock(true);
PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds)); PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds));
Assertions.assertIterableEquals(result.getList(), spuPage.getList()); Assertions.assertIterableEquals(result.getList(), spuPage.getList());
@ -323,7 +277,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
productSpuPageReqVO.setCategoryId(categoryId); productSpuPageReqVO.setCategoryId(categoryId);
PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set<Long>) null)); PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set<Long>) null));
assertEquals(result, spuPage); assertEquals(result, spuPage);

View File

@ -54,6 +54,10 @@
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
</dependency>
<!-- Web 相关 --> <!-- Web 相关 -->
<dependency> <dependency>

View File

@ -0,0 +1,5 @@
### 获得地区树
GET {{baseUrl}}/system/area/tree
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.system.controller.admin.ip;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.framework.ip.core.utils.IPUtils;
import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
import cn.iocoder.yudao.module.system.convert.ip.AreaConvert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 地区")
@RestController
@RequestMapping("/system/area")
@Validated
public class AreaController {
@GetMapping("/tree")
@ApiOperation("获得地区树")
public CommonResult<List<AreaNodeRespVO>> getAreaTree() {
Area area = AreaUtils.getArea(Area.ID_CHINA);
Assert.notNull(area, "获取不到中国");
return success(AreaConvert.INSTANCE.convertList(area.getChildren()));
}
@GetMapping("/get-by-ip")
@ApiOperation("获得 IP 对应的地区名")
@ApiImplicitParam(name = "ip", value = "IP", required = true, dataTypeClass = String.class)
public CommonResult<String> getAreaByIp(@RequestParam("ip") String ip) {
// 获得城市
Area area = IPUtils.getArea(ip);
if (area == null) {
return success("未知");
}
// 格式化返回
return success(AreaUtils.format(area.getId()));
}
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.system.controller.admin.ip.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@ApiModel("管理后台 - 地区节点 Response VO")
@Data
public class AreaNodeRespVO {
@ApiModelProperty(value = "编号", required = true, example = "110000")
private Integer id;
@ApiModelProperty(value = "名字", required = true, example = "北京")
private String name;
/**
*
*/
private List<AreaNodeRespVO> children;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.convert.ip;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface AreaConvert {
AreaConvert INSTANCE = Mappers.getMapper(AreaConvert.class);
List<AreaNodeRespVO> convertList(List<Area> list);
}

View File

@ -26,18 +26,10 @@ export function deleteSpu(id) {
}) })
} }
// 获得商品spu
export function getSpu(id) {
return request({
url: '/product/spu/get?id=' + id,
method: 'get'
})
}
// 获得商品 SPU 详情 // 获得商品 SPU 详情
export function getSpuDetail(id) { export function getSpuDetail(id) {
return request({ return request({
url: '/product/spu/get/detail?id=' + id, url: '/product/spu/get-detail?id=' + id,
method: 'get' method: 'get'
}) })
} }

View File

@ -0,0 +1,17 @@
import request from '@/utils/request'
// 获得地区树
export function getAreaTree() {
return request({
url: '/system/area/tree',
method: 'get'
})
}
// 获得 IP 对应的地区名
export function getAreaByIp(ip) {
return request({
url: '/system/area/get-by-ip?ip=' + ip,
method: 'get'
})
}

View File

@ -4,9 +4,8 @@
<el-tabs v-model="activeName" class="tabs"> <el-tabs v-model="activeName" class="tabs">
<!-- 基础设置 --> <!-- 基础设置 -->
<!-- TODO @luowenfeng基础设置分成基础信息配送信息 --> <!-- TODO @luowenfeng基础设置分成基础信息配送信息 -->
<!-- TODO @luowenfengbase=basic 会更好哈 --> <el-tab-pane label="基础设置" name="basic">
<el-tab-pane label="基础设置" name="base"> <el-form ref="basic" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form ref="base" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="商品名称" prop="name"> <el-form-item label="商品名称" prop="name">
<el-input v-model="baseForm.name" placeholder="请输入商品名称" /> <el-input v-model="baseForm.name" placeholder="请输入商品名称" />
</el-form-item> </el-form-item>
@ -155,9 +154,8 @@
</el-tab-pane> </el-tab-pane>
<!-- 商品详情 --> <!-- 商品详情 -->
<!-- TODO @luowenfengthird=detail 会更好哈 --> <el-tab-pane label="商品详情" name="detail">
<el-tab-pane label="商品详情" name="third"> <el-form ref="detail" :model="baseForm" :rules="rules">
<el-form ref="third" :model="baseForm" :rules="rules">
<el-form-item prop="description"> <el-form-item prop="description">
<editor v-model="baseForm.description" :min-height="380"/> <editor v-model="baseForm.description" :min-height="380"/>
</el-form-item> </el-form-item>
@ -165,9 +163,8 @@
</el-tab-pane> </el-tab-pane>
<!-- 销售设置 --> <!-- 销售设置 -->
<!-- TODO @luowenfengfourth=senior 会更好哈 --> <el-tab-pane label="高级设置" name="senior">
<el-tab-pane label="高级设置" name="fourth"> <el-form ref="senior" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form ref="fourth" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="排序字段"> <el-form-item label="排序字段">
<el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/> <el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item> </el-form-item>
@ -208,7 +205,7 @@ export default {
data() { data() {
return { return {
specSwitch: false, specSwitch: false,
activeName: "base", activeName: "basic",
propName: { propName: {
checkStrictly: true, checkStrictly: true,
label: "name", label: "name",

View File

@ -0,0 +1,114 @@
<template>
<div class="app-container">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">IP
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column label="编号" prop="id"/>
<el-table-column label="名字" prop="name"/>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog title="IP 查询" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="IP" prop="ip">
<el-input v-model="form.ip" placeholder="请输入 IP 地址"/>
</el-form-item>
<el-form-item label="地址" prop="result">
<el-input v-model="form.result" readonly placeholder="展示查询 IP 结果" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {getAreaByIp, getAreaTree} from "@/api/system/area";
export default {
name: "Area",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
list: [],
//
title: "",
//
open: false,
//
refreshTable: true,
//
form: {
ip: undefined,
result: undefined,
},
//
rules: {
ip: [{required: true, message: "IP 地址不能为控", trigger: "blur"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
getAreaTree().then(response => {
this.list = response.data;
this.loading = false;
})
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
ip: undefined,
result: undefined,
};
this.resetForm("form");
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
getAreaByIp(this.form.ip).then(response => {
this.$modal.msgSuccess("查询成功");
this.form.result = response.data
});
});
}
}
};
</script>