jinshihui 2 днів тому
батько
коміт
02dde12b82

+ 11 - 6
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductController.java

@@ -13,6 +13,7 @@ import com.ylx.massage.domain.Product;
 import com.ylx.massage.domain.ProductCategory;
 import com.ylx.massage.domain.dto.ProductCreateDTO;
 import com.ylx.massage.domain.dto.ProductSpecSetupDTO;
+import com.ylx.massage.domain.dto.ProductUpdateDTO;
 import com.ylx.massage.domain.vo.CategoryTreeVO;
 import com.ylx.massage.domain.vo.ProductDetailsVo;
 import com.ylx.massage.domain.vo.SpecComboVO;
@@ -86,7 +87,10 @@ public class ProductController extends BaseController {
     public R selectOne(@RequestParam Long id) {
         try {
             Product productServiceById = this.productService.getById(id);
-            //根据商品id查询商品的规格
+            // 根据分类ID查询分类的名称
+            ProductCategory productCategory = this.productCategoryService.getById(productServiceById.getCategoryId());
+            productServiceById.setCategoryName(productCategory.getName());
+            //根据商品编号查询商品的规格
             List<SpecComboVO> specComboVOList = this.productService.getSpecCombinations(productServiceById.getProductNo());
             ProductDetailsVo productDetailsVo = new ProductDetailsVo();
             BeanUtil.copyProperties(productServiceById, productDetailsVo);
@@ -119,17 +123,18 @@ public class ProductController extends BaseController {
     /**
      * 修改商品
      *
-     * @param product 商品实体对象
+     * @param dto 商品实体对象
      * @return R 修改结果
      */
     @PostMapping("update")
     @ApiOperation("修改商品")
-    public R update(@RequestBody Product product) {
+    public R update(@Valid @RequestBody ProductUpdateDTO dto) {
         try {
-            return R.ok(this.productService.updateById(product));
+            productService.updateProduct(dto);
+            return R.ok("商品修改成功");
         } catch (Exception e) {
-            e.printStackTrace();
-            throw new RuntimeException(e);
+            log.error("商品修改失败", e);
+            return R.fail("商品修改失败:" + e.getMessage());
         }
     }
 

+ 0 - 6
nightFragrance-massage/src/main/java/com/ylx/massage/domain/Product.java

@@ -147,12 +147,6 @@ public class Product implements Serializable {
     @TableField("status")
     private Integer status;
 
-    /**
-     * 是否有规格:1是 0否
-     */
-    @TableField("has_spec")
-    private Integer hasSpec;
-
     /**
      * 创建时间
      */

+ 32 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductUpdateDTO.java

@@ -0,0 +1,32 @@
+package com.ylx.massage.domain.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 新增商品请求DTO
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@ApiModel("新增商品请求")
+public class ProductUpdateDTO extends ProductCreateDTO {
+
+    /**
+     * 商品ID
+     */
+    @ApiModelProperty("商品ID")
+    @NotNull(message = "商品ID不能为空")
+    private Long id;
+}

+ 22 - 10
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/SpecComboVO.java

@@ -1,9 +1,11 @@
 package com.ylx.massage.domain.vo;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -28,11 +30,6 @@ public class SpecComboVO {
     @ApiModelProperty("规格值组合")
     private List<String> specValues;
 
-    /**
-     * 规格值ID组合(如:[1, 3])
-     */
-    /*@ApiModelProperty("规格值ID组合")
-    private List<Long> specValueIds;*/
 
     /**
      * 组合显示文本(如:"白色,加粗")
@@ -45,6 +42,26 @@ public class SpecComboVO {
      */
     private String image;
 
+    /**
+     * 现金价格
+     */
+    private BigDecimal priceMoney;
+
+    /**
+     * 积分价格
+     */
+    private Integer pricePoint;
+
+    /**
+     * 原价
+     */
+    private BigDecimal originPrice;
+
+    /**
+     * 库存
+     */
+    private Integer stock;
+
     /**
      * 规格名对应关系(如:[{"specName":"颜色","specValue":"白色"},{"specName":"型号","specValue":"加粗"}])
      */
@@ -70,11 +87,6 @@ public class SpecComboVO {
         @ApiModelProperty("规格名")
         private String specName;
 
-        /**
-         * 规格值ID
-         */
-        /*@ApiModelProperty("规格值ID")
-        private Long specValueId;*/
 
         /**
          * 规格值

+ 8 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/ProductService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.ylx.massage.domain.Product;
 import com.ylx.massage.domain.dto.ProductCreateDTO;
 import com.ylx.massage.domain.dto.ProductSpecSetupDTO;
+import com.ylx.massage.domain.dto.ProductUpdateDTO;
 import com.ylx.massage.domain.vo.SpecComboVO;
 
 import java.util.List;
@@ -24,6 +25,13 @@ public interface ProductService extends IService<Product> {
      */
     Long createProduct(ProductCreateDTO dto);
 
+    /**
+     * 修改商品
+     *
+     * @param dto 修改商品请求DTO
+     */
+    void updateProduct(ProductUpdateDTO dto);
+
     /**
      * 设置商品规格(新增或更新规格名和规格值)
      *

+ 188 - 25
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/ProductServiceImpl.java

@@ -1,13 +1,10 @@
 package com.ylx.massage.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ylx.massage.domain.*;
-import com.ylx.massage.domain.dto.ProductCreateDTO;
-import com.ylx.massage.domain.dto.ProductSkuDTO;
-import com.ylx.massage.domain.dto.ProductSpecDTO;
-import com.ylx.massage.domain.dto.ProductSpecSetupDTO;
-import com.ylx.massage.domain.dto.ProductSpecValueDTO;
+import com.ylx.massage.domain.dto.*;
 import com.ylx.massage.domain.vo.SpecComboVO;
 import com.ylx.massage.mapper.*;
 import com.ylx.massage.service.ProductService;
@@ -18,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
 import javax.annotation.Resource;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -61,10 +59,20 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
             throw new RuntimeException("商品分类不存在");
         }
 
+        // 校验规格列表是否为空
+        if (CollectionUtils.isEmpty(dto.getSpecList())) {
+            throw new RuntimeException("商品规格不能为空");
+        }
+
+        // 校验SKU列表是否为空
+        if (CollectionUtils.isEmpty(dto.getSkuList())) {
+            throw new RuntimeException("商品SKU不能为空");
+        }
+
         // 2. 保存商品基本信息
         Product product = new Product();
-        product.setProductNo(dto.getProductNo());
         product.setCategoryId(dto.getCategoryId());
+        product.setProductNo(dto.getProductNo());
         product.setName(dto.getName());
         product.setProductMainImage(dto.getProductMainImage());
         product.setProductImage(dto.getProductImage());
@@ -75,9 +83,44 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
         product.setServicePromise(dto.getServicePromise());
         product.setSaleStartTime(dto.getSaleStartTime());
         product.setSaleEndTime(dto.getSaleEndTime());
-        //product.setHasSpec(dto.getHasSpec());
+        //判断付款方式
+        Integer paymentType = dto.getPaymentType();
+        List<ProductSkuDTO> skuList = dto.getSkuList();
+        switch (paymentType) {
+            // 积分支付
+            case 1:
+                Integer pricePoint = skuList.get(0).getPricePoint();
+                //积分价格不能为空
+                if (pricePoint == null) {
+                    throw new RuntimeException("积分价格不能为空");
+                }
+                break;
+            // 金额支付
+            case 2:
+                BigDecimal priceMoney = skuList.get(0).getPriceMoney();
+                //金额价格不能为空
+                if (priceMoney == null) {
+                    throw new RuntimeException("金额价格不能为空");
+                }
+                break;
+            // 积分+金额支付
+            case 3:
+                Integer pricePoint1 = skuList.get(0).getPricePoint();
+                BigDecimal priceMoney1 = skuList.get(0).getPriceMoney();
+                //积分价格不能为空
+                if (pricePoint1 == null) {
+                    throw new RuntimeException("积分价格不能为空");
+                }
+                //金额价格不能为空
+                if (priceMoney1 == null) {
+                    throw new RuntimeException("金额价格不能为空");
+                }
+                break;
+            default:
+                throw new RuntimeException("付款方式错误");
+        }
         // 设置付款方式
-        product.setPaymentType(dto.getPaymentType());
+        product.setPaymentType(paymentType);
         product.setStatus(0); // 默认下架
         product.setSales(0); // 初始销量为0
         product.setDeleted(0); // 未删除
@@ -91,15 +134,125 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
         this.save(product);
         Long productId = product.getId();
 
-        // 3. 如果有规格,保存规格和SKU
-        if (!CollectionUtils.isEmpty(dto.getSpecList())) {
-            saveProductSpecsAndSkus(productId, dto);
-        }
-
+        // 3. 保存规格和SKU
+        saveProductSpecsAndSkus(productId, dto);
         log.info("商品创建成功,商品ID:{},商品编号:{}", productId, dto.getProductNo());
         return productId;
     }
 
+    /**
+     * 修改商品
+     *
+     * @param dto 修改商品请求DTO
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateProduct(ProductUpdateDTO dto) {
+        // 1. 校验商品是否存在
+        Product product = this.getById(dto.getId());
+        if (product == null) {
+            throw new RuntimeException("商品不存在");
+        }
+        // 2. 校验分类是否存在
+        ProductCategory category = productCategoryMapper.selectById(dto.getCategoryId());
+        if (category == null) {
+            throw new RuntimeException("商品分类不存在");
+        }
+        // 3. 校验规格列表是否为空
+        if (CollectionUtils.isEmpty(dto.getSpecList())) {
+            throw new RuntimeException("商品规格不能为空");
+        }
+
+        // 4. 校验SKU列表是否为空
+        if (CollectionUtils.isEmpty(dto.getSkuList())) {
+            throw new RuntimeException("商品SKU不能为空");
+        }
+
+        //更新商品的信息
+        product.setCategoryId(dto.getCategoryId());
+        product.setName(dto.getName());
+        product.setProductMainImage(dto.getProductMainImage());
+        product.setProductImage(dto.getProductImage());
+        product.setSummary(dto.getSummary());
+        product.setDetail(dto.getDetail());
+        product.setPaymentType(dto.getPaymentType());
+        product.setFreight(dto.getFreight());
+        product.setDeliveryTime(dto.getDeliveryTime());
+        product.setServicePromise(dto.getServicePromise());
+        product.setSaleStartTime(dto.getSaleStartTime());
+        product.setSaleEndTime(dto.getSaleEndTime());
+        this.updateById(product);
+        //更新规格
+        updateProductSpecs(product.getId(), dto.getSpecList());
+        //更新SKU
+        updateProductSkus(product.getId(), dto.getSkuList());
+    }
+
+    /**
+     * 更新商品规格
+     *
+     * @param productId 商品ID
+     * @param specList  商品规格列表
+     */
+    public void updateProductSpecs(Long productId, List<ProductSpecDTO> specList) {
+        //删除商品规格
+        productSpecMapper.delete(new LambdaQueryWrapper<ProductSpec>().eq(ProductSpec::getProductId, productId));
+        //删除商品规格值
+        productSpecValueMapper.delete(new LambdaQueryWrapper<ProductSpecValue>().eq(ProductSpecValue::getProductId, productId));
+        //添加商品规格
+        for (ProductSpecDTO spec : specList) {
+            ProductSpec productSpec = new ProductSpec();
+            productSpec.setProductId(productId);
+            productSpec.setSpecName(spec.getSpecName());
+            productSpec.setSort(spec.getSort());
+            productSpecMapper.insert(productSpec);
+            //添加商品规格值
+            for (ProductSpecValueDTO value : spec.getSpecValues()) {
+                ProductSpecValue productSpecValue = new ProductSpecValue();
+                productSpecValue.setSpecId(productSpec.getId());
+                productSpecValue.setProductId(productId);
+                productSpecValue.setSpecValue(value.getSpecValue());
+                productSpecValue.setSort(value.getSort());
+                productSpecValueMapper.insert(productSpecValue);
+            }
+        }
+        /*for (ProductSpecDTO spec : specList) {
+            ProductSpec productSpec = new ProductSpec();
+            productSpec.setSpecName(spec.getSpecName());
+            productSpec.setSort(spec.getSort());
+            productSpecMapper.update(productSpec, new LambdaUpdateWrapper<ProductSpec>().eq(ProductSpec::getProductId, productId));
+
+            // 更新商品规格值
+            for (ProductSpecValueDTO value : spec.getSpecValues()) {
+                ProductSpecValue productSpecValue = new ProductSpecValue();
+                productSpecValue.setSpecValue(value.getSpecValue());
+                productSpecValue.setSort(value.getSort());
+                productSpecValueMapper.update(productSpecValue, new LambdaUpdateWrapper<ProductSpecValue>().eq(ProductSpecValue::getProductId, productId));
+            }
+        }*/
+    }
+
+    /**
+     * 更新商品SKU
+     */
+    public void updateProductSkus(Long productId, List<ProductSkuDTO> skuList) {
+        //删除商品SKU
+        productSkuMapper.delete(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, productId));
+        //添加商品SKU
+        for (ProductSkuDTO sku : skuList) {
+            ProductSku productSku = new ProductSku();
+            productSku.setProductId(productId);
+            productSku.setSkuNo(generateSkuNo(productId));
+            productSku.setSpecCombo(sku.getSpecCombo());
+            productSku.setImage(sku.getImage());
+            productSku.setPriceMoney(sku.getPriceMoney());
+            productSku.setPricePoint(sku.getPricePoint());
+            productSku.setOriginPrice(sku.getOriginPrice());
+            productSku.setStock(sku.getStock());
+            productSkuMapper.insert(productSku);
+        }
+    }
+
     /**
      * 生成商品编号
      * 规则:分类编码 + 当前日期(yyyyMMdd) + 序列号(3位)
@@ -172,7 +325,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
      * 保存商品规格和SKU
      *
      * @param productId 商品ID
-     * @param dto 商品创建DTO
+     * @param dto       商品创建DTO
      */
     private void saveProductSpecsAndSkus(Long productId, ProductCreateDTO dto) {
         List<ProductSpecDTO> specList = dto.getSpecList();
@@ -182,15 +335,17 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
             throw new RuntimeException("商品必须包含规格和SKU信息");
         }
 
-        // 保存规格名和规格值,并记录ID映射
+        // 保存规格名和规格值
         AtomicInteger specIndex = new AtomicInteger(0);
 
         for (ProductSpecDTO specDTO : specList) {
             // 保存规格名
             ProductSpec spec = new ProductSpec();
             spec.setProductId(productId);
+            spec.setProductNo(dto.getProductNo());
             spec.setSpecName(specDTO.getSpecName());
             spec.setSort(specDTO.getSort() != null ? specDTO.getSort() : specIndex.get() + 1);
+            // 插入product_spec表
             productSpecMapper.insert(spec);
             Long specId = spec.getId();
 
@@ -201,9 +356,10 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
                     ProductSpecValue specValue = new ProductSpecValue();
                     specValue.setSpecId(specId);
                     specValue.setProductId(productId);
+                    specValue.setProductNo(dto.getProductNo());
                     specValue.setSpecValue(valueDTO.getSpecValue());
                     specValue.setSort(valueDTO.getSort() != null ? valueDTO.getSort() : valueIndex.get() + 1);
-                    specValue.setCreateTime(LocalDateTime.now());
+                    // 插入product_spec_value表
                     productSpecValueMapper.insert(specValue);
                     valueIndex.getAndIncrement();
                 }
@@ -218,7 +374,6 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
             sku.setSkuNo(generateSkuNo(productId));
             sku.setSpecCombo(skuDTO.getSpecCombo());
             sku.setImage(skuDTO.getImage());
-            //sku.setSpecValueIds(skuDTO.getSpecValueIndexCombo());
             sku.setPriceMoney(skuDTO.getPriceMoney());
             sku.setPricePoint(skuDTO.getPricePoint());
             sku.setOriginPrice(skuDTO.getOriginPrice());
@@ -231,6 +386,9 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
 
     /**
      * 生成SKU编号
+     *
+     * @param productId 商品ID
+     * @return 生成的SKU编号
      */
     private String generateSkuNo(Long productId) {
         return "SKU" + productId + System.currentTimeMillis();
@@ -239,7 +397,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
     /**
      * 新增商品规格
      *
-      @param dto 规格设置请求DTO
+     * @param dto 规格设置请求DTO
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -285,7 +443,8 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
     }
 
     /**
-     * 获取商品规格组合(笛卡尔积)
+     * 获取商品规格组合
+     *
      * @param productId 商品ID
      * @return List<SpecComboVO> 规格组合列表
      */
@@ -300,6 +459,9 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
             return Collections.emptyList();
         }
 
+        // 获取商品ID
+        Long productId = specList.get(0).getProductId();
+
         // 查询所有规格值
         LambdaQueryWrapper<ProductSpecValue> valueWrapper = new LambdaQueryWrapper<>();
         valueWrapper.eq(ProductSpecValue::getProductNo, productNo).orderByAsc(ProductSpecValue::getSort);
@@ -335,23 +497,24 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
         for (List<SpecComboVO.SpecNameValue> combo : cartesianProduct) {
             SpecComboVO vo = new SpecComboVO();
             vo.setIndex(index++);
-
             List<String> specValues = combo.stream().map(SpecComboVO.SpecNameValue::getSpecValue).collect(Collectors.toList());
             vo.setSpecValues(specValues);
 
-            /*List<Long> specValueIds = combo.stream().map(SpecComboVO.SpecNameValue::getSpecValueId).collect(Collectors.toList());
-            vo.setSpecValueIds(specValueIds);*/
             String join = String.join(",", specValues);
             log.info("规格值文本:{}", join);
             vo.setSpecValueText(join);
             //根据规格值查询规格的图片
             LambdaQueryWrapper<ProductSku> eq = new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getSpecCombo, join)
-                    .orderByDesc(ProductSku::getCreateTime).last("limit 1");
+                    .eq(ProductSku::getProductId, productId).orderByDesc(ProductSku::getCreateTime).last("limit 1");
             ProductSku sku = productSkuMapper.selectOne(eq);
             if (sku == null) {
                 vo.setImage("");
-            }else{
+            } else {
                 vo.setImage(sku.getImage());
+                vo.setPriceMoney(sku.getPriceMoney());
+                vo.setPricePoint(sku.getPricePoint());
+                vo.setOriginPrice(sku.getOriginPrice());
+                vo.setStock(sku.getStock());
             }
             vo.setSpecNameValueList(combo);
             result.add(vo);
@@ -363,7 +526,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
      * 计算笛卡尔积
      *
      * @param lists 规格值列表列表
-     * @return List<List<T>> 笛卡尔积结果
+     * @return List<List < T>> 笛卡尔积结果
      */
     private <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
         if (lists.isEmpty()) {