refactor(goods): 优化商品 SKU 处理逻辑

- 新增 SkuBo 主键字段,用于更新操作
- 优化商品保存逻辑,支持新增和更新 SKU
- 新增 SKU 获取接口,按商品 ID 查询
-调整商品查询结果,使用别名区分 SKU 字段
This commit is contained in:
huk 2025-09-19 14:53:58 +08:00
parent 1a859a9337
commit 1ee5c31022
6 changed files with 83 additions and 22 deletions

View File

@ -6,6 +6,7 @@ import com.wzj.soopin.goods.domain.bo.ProductCategoryBo;
import com.wzj.soopin.goods.domain.entity.ProductCategory; import com.wzj.soopin.goods.domain.entity.ProductCategory;
import com.wzj.soopin.goods.domain.vo.ProductCategoryVO; import com.wzj.soopin.goods.domain.vo.ProductCategoryVO;
import com.wzj.soopin.goods.service.impl.ProductCategoryServiceImpl; import com.wzj.soopin.goods.service.impl.ProductCategoryServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
@ -16,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@Tag(name ="商品分类接口列表") @Tag(name ="商品分类")
@RestController @RestController
@RequestMapping("/app/product/category") @RequestMapping("/app/product/category")
@RequiredArgsConstructor @RequiredArgsConstructor
@ -26,13 +27,13 @@ public class AppProductCategoryController {
private final ProductCategoryServiceImpl service; private final ProductCategoryServiceImpl service;
private final ProductCategoryConvert convert; private final ProductCategoryConvert convert;
@Tag(name ="查询商品分类列表") @Operation(summary = "查询商品分类列表")
@PostMapping("page") @PostMapping("page")
public R<Page<ProductCategoryVO>> page(@RequestBody ProductCategoryBo query, Page<ProductCategory> page) { public R<Page<ProductCategoryVO>> page(@RequestBody ProductCategoryBo query, Page<ProductCategory> page) {
Page<ProductCategory> productCategoryPage = service.page(page,query.toWrapper()); Page<ProductCategory> productCategoryPage = service.page(page,query.toWrapper());
return R.ok(convert.toVO(productCategoryPage)); return R.ok(convert.toVO(productCategoryPage));
} }
@Tag(name = "查询列表") @Operation(summary = "查询列表")
@PostMapping("/tree") @PostMapping("/tree")
public R<List<ProductCategoryVO>> tree(@RequestBody ProductCategoryBo bo ) { public R<List<ProductCategoryVO>> tree(@RequestBody ProductCategoryBo bo ) {
List<ProductCategoryVO> articleList = service.tree( bo.toWrapper()); List<ProductCategoryVO> articleList = service.tree( bo.toWrapper());

View File

@ -1,12 +1,12 @@
package com.wzj.soopin.goods.business; package com.wzj.soopin.goods.business;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.goods.convert.ProductConvert; import com.wzj.soopin.goods.convert.ProductConvert;
import com.wzj.soopin.goods.convert.SkuConvert; import com.wzj.soopin.goods.convert.SkuConvert;
import com.wzj.soopin.goods.domain.bo.ProductBo; import com.wzj.soopin.goods.domain.bo.ProductBo;
import com.wzj.soopin.goods.domain.bo.SkuBo;
import com.wzj.soopin.goods.domain.entity.Product; import com.wzj.soopin.goods.domain.entity.Product;
import com.wzj.soopin.goods.domain.entity.Sku; import com.wzj.soopin.goods.domain.entity.Sku;
import com.wzj.soopin.goods.domain.vo.ProductVO; import com.wzj.soopin.goods.domain.vo.ProductVO;
@ -20,6 +20,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -59,11 +60,34 @@ public class ProductBusinessImpl extends BusinessImpl<ProductService, ProductCon
product.setSales("0"); product.setSales("0");
product.setTenantId(TenantHelper.getTenantId()); product.setTenantId(TenantHelper.getTenantId());
boolean result = productService.saveOrUpdate(product); boolean result = productService.saveOrUpdate(product);
//清理掉旧的SKU // 新增则直接保存sku信息
skuService.remove(new LambdaQueryWrapper<Sku>().eq(Sku::getProductId, product.getId())); if (bo.getId() == null) {
// 2. 保存SKU列表 List<Sku> skus = bo.getSkuList().stream().map(skuBO -> {
if (CollectionUtils.isNotEmpty(bo.getSkuList())) { Sku sku = skuConvert.toPo(skuBO);
List<Sku> skus = bo.getSkuList().stream() sku.setProductId(product.getId());
sku.setTenantId(TenantHelper.getTenantId());
return sku;
}).collect(Collectors.toList());
result = skuService.saveBatch(skus);
} else {
// 更新需按照新sku信息和旧sku信息对比id一致的数据更新新sku信息中id缺失的要删除没有id的要新增
List<SkuBo> skuList = bo.getSkuList();
List<Sku> oldSkus = skuService.getByProductId(bo.getId());
// 分离需要更新新增和删除的SKU
List<Sku> skusToUpdate = skuList.stream()
.filter(skuBo -> skuBo.getId() != null)
.map(skuBO -> {
Sku sku = skuConvert.toPo(skuBO);
sku.setId(skuBO.getId());
sku.setProductId(product.getId());
sku.setTenantId(TenantHelper.getTenantId());
return sku;
})
.collect(Collectors.toList());
List<Sku> skusToSave = skuList.stream()
.filter(skuBo -> skuBo.getId() == null)
.map(skuBO -> { .map(skuBO -> {
Sku sku = skuConvert.toPo(skuBO); Sku sku = skuConvert.toPo(skuBO);
sku.setProductId(product.getId()); sku.setProductId(product.getId());
@ -71,9 +95,36 @@ public class ProductBusinessImpl extends BusinessImpl<ProductService, ProductCon
return sku; return sku;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
result = skuService.saveBatch(skus);
// 获取旧SKU ID列表和当前提交的SKU ID列表
List<Long> oldSkuIds = oldSkus.stream()
.map(Sku::getId)
.collect(Collectors.toList());
List<Long> currentSkuIds = skuList.stream()
.map(SkuBo::getId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 找出需要删除的SKU ID在旧列表中但不在当前列表中的
List<Long> skusToDelete = oldSkuIds.stream()
.filter(id -> !currentSkuIds.contains(id))
.collect(Collectors.toList());
// 执行更新操作
if (CollectionUtils.isNotEmpty(skusToUpdate)) {
result = skuService.updateBatchById(skusToUpdate) && result;
} }
// 执行新增操作
if (CollectionUtils.isNotEmpty(skusToSave)) {
result = skuService.saveBatch(skusToSave) && result;
}
// 执行删除操作
if (CollectionUtils.isNotEmpty(skusToDelete)) {
result = skuService.removeByIds(skusToDelete) && result;
}
}
return result; return result;
} }

View File

@ -12,6 +12,9 @@ import java.math.BigDecimal;
@Schema(description = "SKU信息 查询 对象") @Schema(description = "SKU信息 查询 对象")
public class SkuBo { public class SkuBo {
@Schema(description = "主键")
private Long id;
@Schema(description = "PRODUCT_ID 精确匹配") @Schema(description = "PRODUCT_ID 精确匹配")
private Long productId; private Long productId;

View File

@ -3,7 +3,11 @@ package com.wzj.soopin.goods.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.goods.domain.entity.Sku; import com.wzj.soopin.goods.domain.entity.Sku;
import java.util.List;
public interface SkuService extends IService<Sku> { public interface SkuService extends IService<Sku> {
void add(Sku sku); void add(Sku sku);
List<Sku> getByProductId(Long productId);
} }

View File

@ -2,6 +2,7 @@ package com.wzj.soopin.goods.service.impl;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.goods.domain.bo.SkuBo; import com.wzj.soopin.goods.domain.bo.SkuBo;
@ -13,10 +14,11 @@ import com.wzj.soopin.goods.mapper.SkuMapper;
import com.wzj.soopin.goods.service.SkuService; import com.wzj.soopin.goods.service.SkuService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
/** /**
* sku信息Service业务层处理 * sku信息Service业务层处理
@ -49,4 +51,9 @@ public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuSe
sku.setTenantId(product.getTenantId()); sku.setTenantId(product.getTenantId());
skuMapper.insert(sku); skuMapper.insert(sku);
} }
@Override
public List<Sku> getByProductId(Long productId) {
return skuMapper.selectList(Wrappers.lambdaQuery(Sku.class).eq(Sku::getProductId, productId));
}
} }

View File

@ -63,8 +63,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
p.*, p.*,
s.id AS sku_id, s.id AS sku_id,
s.out_sku_id, s.out_sku_id,
s.price, s.price as skuPrice,
s.pic, s.pic as skuPic,
s.stock, s.stock,
s.sp_data s.sp_data
FROM FROM
@ -89,9 +89,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.nameLike != null and query.nameLike != ''"> <if test="query.nameLike != null and query.nameLike != ''">
AND p.name LIKE CONCAT('%', #{query.nameLike}, '%') AND p.name LIKE CONCAT('%', #{query.nameLike}, '%')
</if> </if>
<!-- <if test="query.categoryId != null">-->
<!-- AND p.category_id = #{query.categoryId}-->
<!-- </if>-->
<if test="query.categoryIds != null and query.categoryIds.size() > 0"> <if test="query.categoryIds != null and query.categoryIds.size() > 0">
AND p.category_id IN AND p.category_id IN
<foreach collection="query.categoryIds" item="categoryId" open="(" separator="," close=")"> <foreach collection="query.categoryIds" item="categoryId" open="(" separator="," close=")">
@ -104,13 +101,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<resultMap id="ProductWithSkusResultMap" type="ProductVO"> <resultMap id="ProductWithSkusResultMap" type="ProductVO">
<id property="id" column="id"/> <id property="id" column="id"/>
<result property="name" column="name"/> <result property="name" column="name"/>
<result property="status" column="status"/>
<collection property="skuList" ofType="SkuVO"> <collection property="skuList" ofType="SkuVO">
<id property="id" column="sku_id"/> <id property="id" column="sku_id"/>
<result property="outSkuId" column="out_sku_id"/> <result property="outSkuId" column="out_sku_id"/>
<result property="price" column="price"/> <result property="price" column="skuPrice"/>
<result property="pic" column="pic"/> <result property="pic" column="skuPic"/>
<result property="stock" column="stock"/> <result property="stock" column="stock"/>
<result property="spData" column="sp_data"/> <result property="spData" column="sp_data"/>
</collection> </collection>