咖猫 發表於 2025-4-29 11:10:00

通用型产品发布解决方案(SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven)03

<h1 id="通用型产品发布解决方案基于分布式微服务技术栈springbootspringcloudspring-cloudalibabavueelementuimybatis-plusmysqlgitmavenlinuxdockernginx---03">通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven+Linux+Docker+Nginx - 《03》</h1>
<ul>
<li>GitHub:https://github.com/China-Rainbow-sea/RainbowSealiving</li>
<li>Gitee:https://gitee.com/Rainbow--Sea/rainbow-sealiving/tree/develop/</li>
</ul>
<p>@</p><div class="toc"><div class="toc-container-header">目录</div><ul><li>通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven+Linux+Docker+Nginx - 《03》</li><li>Mybatis-plus 分页插件</li><li>层级定位</li><li>前端搜索检索功能</li><li>关联表-而不是使用外键</li><li>BeanUtils 的使用</li><li>SPU 和 SKU 的概念和实现<ul><li>null</li><li>JSON 转换为 Java 实体类</li><li>保存 SKU 的图片信息表的设置</li><li>保存 SKU 的基本信息</li><li><font style="color: rgba(0, 0, 0, 1)">保存 spu 和 sku 的 图片信息</font></li></ul></li><li>使用订阅发布功能的实现</li></ul></div><p></p>
<h1 id="mybatis-plus-分页插件">Mybatis-plus 分页插件</h1>
<p>补充:renren 的分页插件</p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356731-1033249213.png"></p>
<pre><code class="language-java">    /**
   * 列表
   * 说明:
   * 1. 根据业务需求,增加根据分类(第3级分类) + 查询条件+ 分页的API接口/方法
   */
    @RequestMapping("/list/{categoryId}")
    //@RequiresPermissions("commodity:attrgroup:list")
    public R list(@RequestParam Map&lt;String, Object&gt; params, @PathVariable("categoryId") Long categoryId){
      PageUtils page = attrgroupService.queryPage(params,categoryId);

      return R.ok().put("page", page);
    }
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356762-1526549752.png"></p>
<pre><code class="language-java">/**
   * 增加根据家居分类(第三级的)+查询条件+分页 API接口
   * 可以通过 debug来查看后台的 SQL语句,一目了然
   *
   * @param params
   * @param categoryId
   * @return
   */
    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params, Long categoryId) {
      // 获取用户进行查询时的关键字
      String key = (String) params.get("key");
      //QueryWrapper是 renren相关提供的,是用于封装查询条件 /参数
      QueryWrapper&lt;AttrgroupEntity&gt; wrapper =
                new QueryWrapper&lt;AttrgroupEntity&gt;();
      //如果是带条件查询,将条件分组到 wrapper,
      //这里的 id和 name是指的 commodity_attrgroup表的字段
      // 判断 key 是否携带的有查询条件
      //if (!StringUtils.isEmpty(key)) {
      //    wrapper.and((obj) -&gt; {
      //      obj.eq("id", key).or().like("name", key);
      //    });
      //}

      // 判断 key 是否携带的有查询添加-希望他是一组独立检索条件。
      if (!StringUtils.isEmpty(key)) {
            wrapper.and((obj) -&gt; {
                obj.eq("id", key).or().like("name", key);
            });
      }
      // IPage是 renren提供的工具类,用于分页查询
      //categoryId为 0表示,查询分类属性组时,不加入 categoryId
      //(是我设置的业务逻辑,目前调用,传的 categoryId都不为 0)
      // 否则就加入 And categoryId = xxx
      if (categoryId != 0) {
            wrapper.eq("category_id", categoryId);
      }

      IPage&lt;AttrgroupEntity&gt; page = this.page(
                new Query&lt;AttrgroupEntity&gt;().getPage(params),
                wrapper
      );

      return new PageUtils(page);

    }
</code></pre>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.alibaba.cloud.context.utils.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.AttrgroupDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.AttrgroupEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrgroupService;


@Service("attrgroupService")
public class AttrgroupServiceImpl extends ServiceImpl&lt;AttrgroupDao, AttrgroupEntity&gt; implements AttrgroupService {

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;AttrgroupEntity&gt; page = this.page(
                new Query&lt;AttrgroupEntity&gt;().getPage(params),
                new QueryWrapper&lt;AttrgroupEntity&gt;()
      );

      return new PageUtils(page);
    }

    /**
   * 增加根据家居分类(第三级的)+查询条件+分页 API接口
   * 可以通过 debug来查看后台的 SQL语句,一目了然
   *
   * @param params
   * @param categoryId
   * @return
   */
    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params, Long categoryId) {
      // 获取用户进行查询时的关键字
      String key = (String) params.get("key");
      //QueryWrapper是 renren相关提供的,是用于封装查询条件 /参数
      QueryWrapper&lt;AttrgroupEntity&gt; wrapper =
                new QueryWrapper&lt;AttrgroupEntity&gt;();
      //如果是带条件查询,将条件分组到 wrapper,
      //这里的 id和 name是指的 commodity_attrgroup表的字段
      // 判断 key 是否携带的有查询条件
      //if (!StringUtils.isEmpty(key)) {
      //    wrapper.and((obj) -&gt; {
      //      obj.eq("id", key).or().like("name", key);
      //    });
      //}

      // 判断 key 是否携带的有查询添加-希望他是一组独立检索条件。
      if (!StringUtils.isEmpty(key)) {
            wrapper.and((obj) -&gt; {
                obj.eq("id", key).or().like("name", key);
            });
      }
      // IPage是 renren提供的工具类,用于分页查询
      //categoryId为 0表示,查询分类属性组时,不加入 categoryId
      //(是我设置的业务逻辑,目前调用,传的 categoryId都不为 0)
      // 否则就加入 And categoryId = xxx
      if (categoryId != 0) {
            wrapper.eq("category_id", categoryId);
      }

      IPage&lt;AttrgroupEntity&gt; page = this.page(
                new Query&lt;AttrgroupEntity&gt;().getPage(params),
                wrapper
      );

      return new PageUtils(page);

    }
}
</code></pre>
<p>打括号的查询提供,分组,括号查询</p>
<pre><code class="language-java">       if (!StringUtils.isEmpty(key)) {
            wrapper.and((obj) -&gt; {
                obj.eq("id", key).or().like("name", key);
            });
      }
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356764-1887397632.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356687-425171668.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.alibaba.cloud.context.utils.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.AttrgroupDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.AttrgroupEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrgroupService;


@Service("attrgroupService")
public class AttrgroupServiceImpl extends ServiceImpl&lt;AttrgroupDao, AttrgroupEntity&gt; implements AttrgroupService {

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;AttrgroupEntity&gt; page = this.page(
                new Query&lt;AttrgroupEntity&gt;().getPage(params),
                new QueryWrapper&lt;AttrgroupEntity&gt;()
      );

      return new PageUtils(page);
    }

    /**
   * 增加根据家居分类(第三级的)+查询条件+分页 API接口
   * 可以通过 debug来查看后台的 SQL语句,一目了然
   *
   * @param params
   * @param categoryId
   * @return
   */
    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params, Long categoryId) {
      // 获取用户进行查询时的关键字
      String key = (String) params.get("key");
      //QueryWrapper是 renren相关提供的,是用于封装查询条件 /参数
      QueryWrapper&lt;AttrgroupEntity&gt; wrapper =
                new QueryWrapper&lt;AttrgroupEntity&gt;();
      //如果是带条件查询,将条件分组到 wrapper,
      //这里的 id和 name是指的 commodity_attrgroup表的字段
      // 判断 key 是否携带的有查询条件
      //if (!StringUtils.isEmpty(key)) {
      //    wrapper.and((obj) -&gt; {
      //      obj.eq("id", key).or().like("name", key);
      //    });
      //}
      if (!StringUtils.isEmpty(key)) {
            wrapper.and((obj) -&gt; {
                obj.eq("id", key).or().like("name", key);
            });
      }
      // IPage是 renren提供的工具类,用于分页查询
      //categoryId为 0表示,查询分类属性组时,不加入 categoryId
      //(是我设置的业务逻辑,目前调用,传的 categoryId都不为 0)
      // 否则就加入 And categoryId = xxx
      if (categoryId != 0) {
            wrapper.eq("category_id", categoryId);
      }

      IPage&lt;AttrgroupEntity&gt; page = this.page(
                new Query&lt;AttrgroupEntity&gt;().getPage(params),
                wrapper
      );

      return new PageUtils(page);

    }
}
</code></pre>
<hr>
<p>配置类(引入分页插件)</p>
<p>在对应项目,或者微服务当中的 config 配置目录下创建,MyB</p>
<p>atisConfig.java,MyBatis-plus 的分页插件的配置类</p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356800-796639045.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.config;


import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration// 标识是一个配置类
@EnableTransactionManagement //开启事务
@MapperScan("com.rainbowsea.rainbowsealiving.commodity.dao")// 扫描的 dao 目录包位置
public class MyBatisConfig {

    // 引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
      PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
      // 设置请求的页面大于最大页后操作,
      // true 调回到首页,false 继续请求 默认 false
      paginationInterceptor.setOverflow(true);
      //设置最大单页限制数量,默认 500条,-1不受限制
      paginationInterceptor.setLimit(100);
      return paginationInterceptor;
    }
}

</code></pre>
<hr>
<h1 id="层级定位">层级定位</h1>
<p>可以省略</p>
<h1 id="前端搜索检索功能">前端搜索检索功能</h1>
<p>可以省略</p>
<h1 id="关联表-而不是使用外键">关联表-而不是使用外键</h1>
<p>写</p>
<h1 id="beanutils-的使用">BeanUtils 的使用</h1>
<pre><code class="language-java">BeanUtils.copyProperties(test1,test2); // 将 test1对象属性拷贝大宋test2 当中
</code></pre>
<hr>
<h1 id="spu-和-sku-的概念和实现">SPU 和 SKU 的概念和实现</h1>
<p><strong>SPU 的概述</strong></p>
<ol>
<li>SPU(<strong><font style="color: rgba(0, 0, 0, 1)">(</font></strong><font style="color: rgba(0, 0, 0, 1)">Stock keeping Unit</font>):标准化产品单元</li>
<li>用简单的话来说就是一类商品,比如:手机里的一种牌子,如:小米,苹果,都是一类。然后,加入具体的类型,如:小米 15,苹果 16 等等。那么它就是一个 spu</li>
</ol>
<p><strong>SKU 的概述:</strong></p>
<ol>
<li><font style="color: rgba(0, 0, 0, 1)">SKU(Stock keeping Unit):库存保有单位。</font></li>
<li><font style="color: rgba(0, 0, 0, 1)">SKU 简单的来说就是在 spu 原有的基础上加入具体的类型,如(小米 15,白色,8+252G) 。组合起来就是一个完整地 SKU,所以 SKU 就是一类商品的各种样式的组合。</font></li>
<li><font style="color: rgba(0, 0, 0, 1)">SKU 的组合会影响该商品的价格。</font></li>
</ol>
<p><font style="color: rgba(0, 0, 0, 1)"></font></p>
<p><font style="color: rgba(0, 0, 0, 1)"></font></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)">SPU 和 SKU 的关系:</font></strong></p>
<ol>
<li>SPU 和 SKU 就是上下级关系,没有 SPU 就没有 SKU,因为假如没有这一类商品,就没法谈这件商品具体的颜色尺寸。</li>
<li>如下图:假如没有选择任何类型那么他就是一个单独的 SPU,但是当它选择了具体的颜色,版本,购买方式等等,那么他就是一个 SKU。</li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356774-1425790932.png"></p>
<blockquote>
<ul>
<li>保存商品spu 信息</li>
<li>1 个 spu 信息可能对于多个 sku</li>
<li>1 个 spu + 1 个 sku 就是一个商品的组合关系()</li>
</ul>
</blockquote>
<h2 id="_"></h2>
<h2 id="json-转换为-java-实体类">JSON 转换为 Java 实体类</h2>
<p>写,特别的</p>
<p>P550</p>
<p>P564</p>
<ol>
<li>当目前的实体类/对象,格式不能满足我们的需求时,可以使用上 <strong>VO</strong><code>**ViewObject**</code>** <strong>对象。VO 可以根据需求组合</strong>已有**的实体类的字段,或者增加,或者删除一些字段。</li>
<li>如果返回的 JSON 数据的当前实体类,不能满足需求,则定义一个 VO(View Object) 对象,在该对象,可以根据需求组合实体类字段,或者增加,或者删除一些字段。</li>
</ol>
<pre><code class="language-vue">submitSkus() {
console.log("要提交保存的商品信息(spu=) " , JSON.stringify(this.spu)); //将 json 对象转
成字符串输出
this.$confirm("将要提交商品数据, 是否继续?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消",
type: "warning"
})//其它
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356796-115366297.png"></p>
<pre><code class="language-vue"> &lt;el-button type="success" @click="submitSkus"&gt;下一步:保存商品信息&lt;/el-button&gt;


submitSkus() {
      console.log("要提交保存的商品信息(spu=) " , JSON.stringify(this.spu))
      this.$confirm("将要提交商品数据,需要一小段时间,是否继续?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning"
      })
      .then(() =&gt; {
          this.$http({
            //url: "http://localhost:9090/commodity/spuinfo/save",
            url: this.$http.adornUrl("/commodity/spuinfo/save"),
            method: "post",
            data: this.$http.adornData(this.spu, false)
          }).then(({data}) =&gt; {
            if (data.code == 0) {
            this.$message({
                type: "success",
                message: "新增商品成功!"
            });
            this.step = 4;
            } else {
            this.$message({
                type: "error",
                message: "保存失败,原因【" + data.msg + "】"
            });
            }
          });
      })
      .catch(e =&gt; {
          console.log(e);
          this.$message({
            type: "info",
            message: "已取消"
          });
      });
    },
</code></pre>
<p>分析一个 JSON 格式内容,将 console 输出的 json 字符串,格式化一下好看: https://www.json.cn/</p>
<p><strong>为了能够在服务端接收提交的数据,我们使用 json 在线工具,转成 entity 对象(实际是一组),作为 VO实体类</strong>。</p>
<ol>
<li>点击 JSON 生成 Java 实体</li>
<li>把 console 输出的 JSON 字符串拷贝到左侧。</li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356757-2057306710.png"></p>
<ol start="3">
<li>指定类名,包名,点击生成即可。</li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356688-686976416.png"></p>
<p><font style="color: rgba(0, 0, 0, 1)">把生成好的 VO 实体类(</font><font style="color: rgba(174, 18, 17, 1)">我已经改好了, 可以直接使用(在资料文件夹)</font><font style="color: rgba(0, 0, 0, 1)">)</font></p>
<p>注意:编写对应的 Cotroller 当中的编码信息。</p>
<p>分 析 一 下 json 格 式 内 容 ,将 console 输 出 的 json 字 符 串 , 格 式 化 一 下 好 看 https://www.json.cn/</p>
<ul>
<li>为了能够在服务端接收提交的数据,我们使用 json 在线工具,转成 entity 对象(实际是 一组),作为 VO 实体类</li>
<li>点击 JSON生成 JAVA 实体</li>
</ul>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356688-752164811.png"> <img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356782-197582656.png"></p>
<p><font style="color: rgba(0, 0, 0, 1)">对生成好的 VO 实体类进行简化和定制,比如 int 改成 Long , double 改成 BigDecimal </font></p>
<p><font style="color: rgba(0, 0, 0, 1)">这样更符合我们的要求</font><font style="color: rgba(0, 0, 0, 1)">, </font><font style="color: rgba(174, 18, 17, 1)">我已经改好了</font><font style="color: rgba(174, 18, 17, 1)">, </font><font style="color: rgba(174, 18, 17, 1)">可以直接使用</font><font style="color: rgba(174, 18, 17, 1)">(</font><font style="color: rgba(174, 18, 17, 1)">在资料文件夹</font><font style="color: rgba(174, 18, 17, 1)">)</font><font style="color: rgba(0, 0, 0, 1)">,并增加了两个 </font><font style="color: rgba(0, 0, 0, 1)">VO </font></p>
<p><font style="color: rgba(0, 0, 0, 1)">类 , Images.java 和 MemberPrice.java, 直接替换一下即可</font></p>
<ol>
<li>增加 Images.java 和 MemberPrice.java</li>
<li>修改 int —&gt; long,double ——&gt;BigDecimal</li>
<li>使用 <code>@Data 注解 </code> 简化 entity 类</li>
</ol>
<p><font style="color: rgba(0, 0, 0, 1)">梳理 VO 实体类和提交的 Json 数据关系</font></p>
<blockquote>
<ul>
<li>保存商品spu 信息</li>
<li>1 个 spu 信息可能对于多个 sku</li>
<li>1 个 spu + 1 个 sku 就是一个商品的组合关系()</li>
</ul>
</blockquote>
<p>保存商品信息,涉及到的表非常多,不是一个 SpulnfoEntityh 实体类能包括的,将 SpulnfoEntity 改成我们前面生成的 SpuSaveVo 包含了 JSON 提交所有信息。</p>
<p>我们这里是通过一个定制的方法saveSpulnfo 来完成。</p>
<h2 id="保存-sku-的图片信息表的设置">保存 SKU 的图片信息表的设置</h2>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356763-299826758.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
* 商品 spu 信息介绍
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 20:46:13
*/
@Data
@TableName("commodity_spu_info_desc")
public class SpuInfoDescEntity implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
       * 商品 id
       * 因为 commodity_spu_info_desc 表的 id 不是自增长的,而是我们指定的
       * 因此,我们这里给 spuId 标识上 @TableId(type = IdType.INPUT)
       * , 否则底层的 sql 语句时不会生成添加 supId 的 sql 语句(可以通过日志输出看看)
       */
        @TableId(type = IdType.INPUT)
        private Long spuId;
        /**
       * 商品介绍图片
       */
        private String decript;

}

</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356709-1832434594.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;

import java.util.Map;

/**
* 商品 spu 信息
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 20:31:43
*/
public interface SpuInfoService extends IService&lt;SpuInfoEntity&gt; {

    PageUtils queryPage(Map&lt;String, Object&gt; params);

    void saveSpuInfo(SpuSaveVO spuSaveVO);

    void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity);
}


</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356773-2049434588.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import org.springframework.stereotype.Service;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDescDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;


@Service("spuInfoDescService")
public class SpuInfoDescServiceImpl extends ServiceImpl&lt;SpuInfoDescDao, SpuInfoDescEntity&gt; implements SpuInfoDescService {

    @Override
    public void saveSpuInfoDesc(SpuInfoDescEntity spuInfoDescEntity) {
      this.baseMapper.insert(spuInfoDescEntity);
    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoDescEntity&gt; page = this.page(
                new Query&lt;SpuInfoDescEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoDescEntity&gt;()
      );

      return new PageUtils(page);
    }

}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356785-684603115.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356769-1219656143.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.controller;

import java.util.Arrays;
import java.util.Map;

//import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.R;



/**
* 商品 spu 信息介绍
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 20:46:13
*/
@RestController
@RequestMapping("commodity/spuinfodesc")
public class SpuInfoDescController {
    @Autowired
    private SpuInfoDescService spuInfoDescService;

    /**
   * 列表
   */
    @RequestMapping("/list")
    //@RequiresPermissions("commodity:spuinfodesc:list")
    public R list(@RequestParam Map&lt;String, Object&gt; params){
      PageUtils page = spuInfoDescService.queryPage(params);

      return R.ok().put("page", page);
    }


    /**
   * 信息
   */
    @RequestMapping("/info/{spuId}")
    //@RequiresPermissions("commodity:spuinfodesc:info")
    public R info(@PathVariable("spuId") Long spuId){
                SpuInfoDescEntity spuInfoDesc = spuInfoDescService.getById(spuId);

      return R.ok().put("spuInfoDesc", spuInfoDesc);
    }

    /**
   * 保存
   */
    @RequestMapping("/save")
    //@RequiresPermissions("commodity:spuinfodesc:save")
    public R save(@RequestBody SpuInfoDescEntity spuInfoDesc){
                spuInfoDescService.save(spuInfoDesc);

      return R.ok();
    }

    /**
   * 修改
   */
    @RequestMapping("/update")
    //@RequiresPermissions("commodity:spuinfodesc:update")
    public R update(@RequestBody SpuInfoDescEntity spuInfoDesc){
                spuInfoDescService.updateById(spuInfoDesc);

      return R.ok();
    }

    /**
   * 删除
   */
    @RequestMapping("/delete")
    //@RequiresPermissions("commodity:spuinfodesc:delete")
    public R delete(@RequestBody Long[] spuIds){
                spuInfoDescService.removeByIds(Arrays.asList(spuIds));

      return R.ok();
    }

}

</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356785-88240741.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {

    @Resource
    SpuInfoDescService spuInfoDescService;

    //因为有多个添加操作,使用事务控制
    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
//2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if(decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      }else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoEntity&gt; page = this.page(
                new Query&lt;SpuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }


    /**
   * 保存 spu 的基本信息到 commodity_spu_info
   * @param spuInfoEntity
   */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
      this.baseMapper.insert(spuInfoEntity);
    }

//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////2. 将 SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
//    }

}
</code></pre>
<p><font style="color: rgba(0, 0, 0, 1)">完成功能说明: </font><font style="color: rgba(174, 18, 17, 1)">提示</font><font style="color: rgba(0, 0, 0, 1)">: 这里我们先暂时不选择图片, 主要测试 保存默认图片信息 </font></p>
<p><font style="color: rgba(0, 0, 0, 1)">是否能记录到 commodity_spu_images 表(</font><font style="color: rgba(174, 18, 17, 1)">后面操作批量保存图片到阿里云</font><font style="color: rgba(0, 0, 0, 1)">)</font></p>
<p><strong>保存到阿里云操作:具体代码实现:</strong></p>
<pre><code class="language-sql">/*======================================================*/
/* 1. 保存商品 spu 的介绍图片集, 就是商品最前面的按一组图片来展示图片的集合 , 点
击可以切换图片
/*======================================================*/
USE hspliving_commodity
CREATE TABLE commodity_spu_images
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id', spu_id BIGINT COMMENT 'spu_id',
img_name VARCHAR(200) COMMENT '图片名',
img_url VARCHAR(255) COMMENT '图片地址',
img_sort INT COMMENT '顺序', default_img TINYINT COMMENT '是否默认图', PRIMARY KEY (id)
)CHARSET=utf8mb4 COMMENT='spu 图片集';
SELECT * FROM commodity_spu_images
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356774-1345910416.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {


    @Resource
    SpuImagesService imagesService;



    @Resource
    SpuInfoDescService spuInfoDescService;

    //因为有多个添加操作,使用事务控制
    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
//2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if(decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      }else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

      //3、保存 spu 的图片集 commodity_spu_images
      List&lt;String&gt; images = spuSaveVO.getImages();
//获取到刚刚保存的 spu 基本信息对应的 id
      imagesService.saveImages(spuInfoEntity.getId(), images);
    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoEntity&gt; page = this.page(
                new Query&lt;SpuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }


    /**
   * 保存 spu 的基本信息到 commodity_spu_info
   * @param spuInfoEntity
   */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
      this.baseMapper.insert(spuInfoEntity);
    }


}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356784-147880939.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuImagesEntity;

import java.util.List;
import java.util.Map;

/**
* spu 图片集
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 21:02:53
*/
public interface SpuImagesService extends IService&lt;SpuImagesEntity&gt; {

    PageUtils queryPage(Map&lt;String, Object&gt; params);

    void saveImages(Long id, List&lt;String&gt; images);
}


</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356781-44392436.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuImagesDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuImagesEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;

import javax.annotation.Resource;


@Service("spuImagesService")
public class SpuImagesServiceImpl extends ServiceImpl&lt;SpuImagesDao, SpuImagesEntity&gt; implements SpuImagesService {


    @Resource
    SpuImagesService imagesService;
    /**
   * 保存某个 spu 的图片集,就是商品最前面的按一组图片来展示图片的集合
   * @param id
   * @param images
   */
    @Override
    public void saveImages(Long id, List&lt;String&gt; images) {
      System.out.println("saveImage...");
      if(images == null || images.size() == 0){//设置默认图片集
            SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
            spuImagesEntity.setSpuId(id);
            spuImagesEntity.setImgUrl("default1.jpg");
            spuImagesEntity.setDefaultImg(1);
            this.save(spuImagesEntity);
      } else { //如果有,就遍历,批量添加即可
            List&lt;SpuImagesEntity&gt; collect = images.stream().map(img -&gt; {
                SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
                spuImagesEntity.setSpuId(id);
                spuImagesEntity.setImgUrl(img);
                return spuImagesEntity;
            }).collect(Collectors.toList());
            this.saveBatch(collect);
      }
    }





    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuImagesEntity&gt; page = this.page(
                new Query&lt;SpuImagesEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuImagesEntity&gt;()
      );

      return new PageUtils(page);
    }

}
</code></pre>
<p><strong><font style="color: rgba(0, 0, 0, 1)">保存 spu 的基本属性/规格参数</font></strong></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356791-898020507.png"></p>
<p><font style="color: rgba(0, 0, 0, 1)">创建保存 spu 的基本属性/规格参数的表</font></p>
<pre><code class="language-sql">/*====================================================*/
/* 1. 保存商品 spu 基本属性值, 有多个
/*====================================================*/
USE hspliving_commodity
CREATE TABLE commodity_product_attr_value
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
spu_id BIGINT COMMENT '商品 id',
attr_id BIGINT COMMENT '属性 id',
attr_name VARCHAR(200) COMMENT '属性名',
attr_value VARCHAR(200) COMMENT '属性值',
attr_sort INT COMMENT '顺序',
quick_show TINYINT COMMENT '快速展示【是否展示在介绍上;0-否 1-是】',
PRIMARY KEY (id)
)CHARSET=utf8mb4 COMMENT='spu 基本属性值';
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356771-1164610937.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;

import java.util.List;
import java.util.Map;

/**
* spu 基本属性值
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 21:40:59
*/
public interface ProductAttrValueService extends IService&lt;ProductAttrValueEntity&gt; {

    PageUtils queryPage(Map&lt;String, Object&gt; params);

    void saveProductAttr(List&lt;ProductAttrValueEntity&gt; collect);
}


</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356780-332635279.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
* spu 基本属性值
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 21:40:59
*/
@Data
@TableName("commodity_product_attr_value")
public class ProductAttrValueEntity implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
       * id
       */
        @TableId
        private Long id;
        /**
       * 商品 id
       */
        private Long spuId;
        /**
       * 属性 id
       */
        private Long attrId;
        /**
       * 属性名
       */
        private String attrName;
        /**
       * 属性值
       */
        private String attrValue;
        /**
       * 顺序
       */
        private Integer attrSort;
        /**
       * 快速展示【是否展示在介绍上;0-否 1-是】
       */
        private Integer quickShow;

}

</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356763-1297180641.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.ProductAttrValueDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.ProductAttrValueService;


@Service("productAttrValueService")
public class ProductAttrValueServiceImpl extends ServiceImpl&lt;ProductAttrValueDao, ProductAttrValueEntity&gt; implements ProductAttrValueService {

    @Override
    public void saveProductAttr(List&lt;ProductAttrValueEntity&gt; collect) {
      this.saveBatch(collect);
    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;ProductAttrValueEntity&gt; page = this.page(
                new Query&lt;ProductAttrValueEntity&gt;().getPage(params),
                new QueryWrapper&lt;ProductAttrValueEntity&gt;()
      );

      return new PageUtils(page);
    }

}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356785-2098595842.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.AttrEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrService;
import com.rainbowsea.rainbowsealiving.commodity.service.ProductAttrValueService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.BaseAttrs;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {


    @Resource
    AttrService attrService;

    @Resource
    ProductAttrValueService productAttrValueService;

    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
//2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if (decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      } else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

      //3、保存 spu 的图片集 commodity_spu_images
      List&lt;String&gt; images = spuSaveVO.getImages();
//获取到刚刚保存的 spu 基本信息对应的 id
      imagesService.saveImages(spuInfoEntity.getId(), images);
//4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
// commodity_product_attr_value
      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());
            AttrEntity id = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(id.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(spuInfoEntity.getId());
            return valueEntity;
      }).collect(Collectors.toList());
      productAttrValueService.saveProductAttr(collect);
    }


    @Resource
    SpuImagesService imagesService;



    @Resource
    SpuInfoDescService spuInfoDescService;

    //因为有多个添加操作,使用事务控制
//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if(decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      }else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
//    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoEntity&gt; page = this.page(
                new Query&lt;SpuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }


    /**
   * 保存 spu 的基本信息到 commodity_spu_info
   * @param spuInfoEntity
   */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
      this.baseMapper.insert(spuInfoEntity);
    }

//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////2. 将 SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
//    }

}
</code></pre>
<h2 id="保存-sku-的基本信息">保存 SKU 的基本信息</h2>
<p><font style="color: rgba(0, 0, 0, 1)">完成保存商品信息功能-有难度-比较综合-涉及解决方案</font></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)">解决方案分析:</font></strong></p>
<ol>
<li>因为在保存商品信息时,涉及到的信息很多。</li>
<li>可以通过<strong>拆解</strong>,创建多个 VO 层的 JavaBean,将商品信息保存到对应的各个表中。</li>
<li>梳理下(重要!开发中,前端和后端是配合的,有时我们后端人员需要根据前端的业务需求来设计程序)</li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356686-1394195826.png"> 整理上面的图:</p>
<p>分析前端发送的 JSON(很复杂)—&gt; 创建 VO 对象/类(多个,可能还有组合/包含关系)——&gt; 创建对应的数据表,保存数据(入库)。</p>
<ul>
<li>可以理解成是一种通用的设计和解决方案。</li>
<li>这个功能的业务相对比较繁琐,这个需要耐心。</li>
</ul>
<p><strong><font style="color: rgba(0, 0, 0, 1)">先分析要提交的 json 数据</font></strong></p>
<p><font style="color: rgba(0, 0, 0, 1)">创建数据表-根据自己的业务设计[考虑前端的要求] </font></p>
<pre><code class="language-sql">USE hspliving_commodity
CREATE TABLE commodity_sku_info
(
sku_id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'skuId',
spu_id BIGINT COMMENT 'spuId',
sku_name VARCHAR(255) COMMENT 'sku 名称',
sku_desc VARCHAR(2000) COMMENT 'sku 介绍描述',
catalog_id BIGINT COMMENT '所属分类 id',
brand_id BIGINT COMMENT '品牌 id',
sku_default_img VARCHAR(255) COMMENT '默认图片',
sku_title VARCHAR(255) COMMENT '标题',
sku_subtitle VARCHAR(2000) COMMENT '副标题',
price DECIMAL(18,4) COMMENT '价格',
sale_count BIGINT COMMENT '销量', PRIMARY KEY (sku_id)
)CHARSET=utf8mb4 COMMENT='sku 信息';


SELECT * FROM commodity_sku_info
</code></pre>
<p><strong><font style="color: rgba(0, 0, 0, 1)">仍 然 使 用 </font><strong><strong><font style="color: rgba(0, 0, 0, 1)">renren-generator </font></strong></strong><font style="color: rgba(0, 0, 0, 1)">逆 向 工 程 生 成 对 应 的 代 码 </font><strong><strong><font style="color: rgba(0, 0, 0, 1)">(CRUD) </font></strong></strong><font style="color: rgba(0, 0, 0, 1)">, 并 拷 贝 到 </font></strong></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)">hspliving-commodity, 这个我们已经做过好几次了,请自己完成</font></strong></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356770-818293802.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356720-427605013.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuInfoEntity;

import java.util.Map;

/**
* sku 信息
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-24 22:01:45
*/
public interface SkuInfoService extends IService&lt;SkuInfoEntity&gt; {

    PageUtils queryPage(Map&lt;String, Object&gt; params);

    void saveSkuInfo(SkuInfoEntity skuInfoEntity);
}


</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356695-1253533507.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import org.springframework.stereotype.Service;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SkuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuInfoService;


@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl&lt;SkuInfoDao, SkuInfoEntity&gt; implements SkuInfoService {

    @Override
    public void saveSkuInfo(SkuInfoEntity skuInfoEntity) {
      this.baseMapper.insert(skuInfoEntity);
    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SkuInfoEntity&gt; page = this.page(
                new Query&lt;SkuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SkuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }

}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356772-789099085.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.AttrEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrService;
import com.rainbowsea.rainbowsealiving.commodity.service.ProductAttrValueService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuInfoService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.BaseAttrs;
import com.rainbowsea.rainbowsealiving.commodity.vo.Images;
import com.rainbowsea.rainbowsealiving.commodity.vo.Skus;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {


    @Resource
    AttrService attrService;

    @Resource
    ProductAttrValueService productAttrValueService;

    @Resource
    SkuInfoService skuInfoService;


    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
//2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if (decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      } else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

      //3、保存 spu 的图片集 commodity_spu_images
      List&lt;String&gt; images = spuSaveVO.getImages();
//获取到刚刚保存的 spu 基本信息对应的 id
      imagesService.saveImages(spuInfoEntity.getId(), images);
//4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
// commodity_product_attr_value
      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());
            AttrEntity id = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(id.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(spuInfoEntity.getId());
            return valueEntity;
      }).collect(Collectors.toList());
      productAttrValueService.saveProductAttr(collect);

      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku,
// 组成一个可以销售的商品信息
//注意 Images 类在 vo 包下, 确保有@Data 修饰
      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
      if(skus!=null &amp;&amp; skus.size()&gt;0){
            skus.forEach(item-&gt;{
                String defaultImg = "default.jpg";
//json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
                for (Images image : item.getImages()) {
                  if(image.getDefaultImg() == 1){
                        defaultImg = image.getImgUrl();
                  }
                }
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
//把 item 中的信息拷贝给 skuInfoEntity
                BeanUtils.copyProperties(item,skuInfoEntity);
//item 中没有信息,但是 skuInfoEntity 需要的信息,
//可以从 spuInfoEntity 中获取
                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
                skuInfoEntity.setSpuId(spuInfoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
//1)、保存 sku 的基本信息;到 commodity_sku_info
                skuInfoService.saveSkuInfo(skuInfoEntity);
            });
      }
    }



//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
//// commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//    }


    @Resource
    SpuImagesService imagesService;



    @Resource
    SpuInfoDescService spuInfoDescService;

    //因为有多个添加操作,使用事务控制
//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if(decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      }else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
//    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoEntity&gt; page = this.page(
                new Query&lt;SpuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }


    /**
   * 保存 spu 的基本信息到 commodity_spu_info
   * @param spuInfoEntity
   */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
      this.baseMapper.insert(spuInfoEntity);
    }

//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////2. 将 SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
//    }

}
</code></pre>
<h2 id="保存-spu-和-sku-的-图片信息"><font style="color: rgba(0, 0, 0, 1)">保存 spu 和 sku 的 图片信息</font></h2>
<p><font style="color: rgba(0, 0, 0, 1)">可 以 添 加 spu 和 sku 图 片 信 息 保 存 到 了 对 应 的 表 中 , 比 如 : </font></p>
<p><font style="color: rgba(0, 0, 0, 1)">commodity_sku_images(一步一步来)</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356757-1594462248.png"></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)">完成功能</font><strong><strong><font style="color: rgba(0, 0, 0, 1)">: </font></strong></strong><font style="color: rgba(0, 0, 0, 1)">只测试是否可以添加 </font><strong><strong><font style="color: rgba(0, 0, 0, 1)">spu </font></strong></strong><font style="color: rgba(0, 0, 0, 1)">的基本信息到 </font><strong><strong><font style="color: rgba(0, 0, 0, 1)">commodity_spu_info (</font></strong></strong><font style="color: rgba(0, 0, 0, 1)">一步一步来</font>****<font style="color: rgba(0, 0, 0, 1)">), </font></strong></p>
<p><strong><font style="color: rgba(174, 18, 17, 1)">提示</font>****<font style="color: rgba(0, 0, 0, 1)">: 如果运行过程中, 提示找不到 VO, Rebuild Project 即可</font></strong></p>
<pre><code class="language-sql">/*==============================================================*/
/* 1. 保存 某一个 sku 对应的图片
/*=====================================================*/
USE hspliving_commodity
CREATE TABLE commodity_sku_images
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
sku_id BIGINT COMMENT 'sku_id',
img_url VARCHAR(255) COMMENT '图片地址',
img_sort INT COMMENT '排序',
default_img INT COMMENT '默认图',
PRIMARY KEY (id)
)CHARSET=utf8mb4 COMMENT='sku 图片';
SELECT * FROM commodity_sku_images
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356756-431373970.png"></p>
<pre><code class="language-vue">&lt;el-upload
      action="http://hspliving-10000.oss-cn-beijing.aliyuncs.com"
      :data="dataObj"
      :list-type="listType"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview"
      :limit="maxCount"
      :on-exceed="handleExceed"
      :show-file-list="showFile"
    &gt;
      &lt;i class="el-icon-plus"&gt;&lt;/i&gt;
    &lt;/el-upload&gt;
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356711-697537046.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.AttrEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuImagesEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrService;
import com.rainbowsea.rainbowsealiving.commodity.service.ProductAttrValueService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuInfoService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.BaseAttrs;
import com.rainbowsea.rainbowsealiving.commodity.vo.Images;
import com.rainbowsea.rainbowsealiving.commodity.vo.Skus;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {


    @Resource
    AttrService attrService;

    @Resource
    ProductAttrValueService productAttrValueService;

    @Resource
    SkuInfoService skuInfoService;


    @Resource
    SkuImagesService skuImagesService;
    @Resource
    SpuImagesService imagesService;

    @Resource
    SpuInfoDescService spuInfoDescService;

    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
//2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if (decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      } else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
      //3、保存 spu 的图片集 commodity_spu_images
      List&lt;String&gt; images = spuSaveVO.getImages();
//获取到刚刚保存的 spu 基本信息对应的 id
      imagesService.saveImages(spuInfoEntity.getId(), images);
//4 、 保 存 spu 的 基 本 属 性 ( 一 个 spu 可 以 有 多 个 基 本 属 性 / 规 格 参 数 ):commodity_product_attr_value
      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());
            AttrEntity id = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(id.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(spuInfoEntity.getId());
            return valueEntity;
      }).collect(Collectors.toList());
      productAttrValueService.saveProductAttr(collect);

      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku, 组成一个可以销售的商品信息.
      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
      if (skus != null &amp;&amp; skus.size() &gt; 0) {
            skus.forEach(item -&gt; {
                String defaultImg = "default.jpg";
//json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
                for (Images image : item.getImages()) {
                  if (image.getDefaultImg() == 1) {
                        defaultImg = image.getImgUrl();
                  }
                }
//保存 sku 的基本信息;到 commodity_sku_info start
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
//把 item 中的信息拷贝给 skuInfoEntity
                BeanUtils.copyProperties(item, skuInfoEntity);
//item 中没有信息,但是 skuInfoEntity 需要的信息,可以从 spuInfoEntity 中获取
                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
                skuInfoEntity.setSpuId(spuInfoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
                skuInfoService.saveSkuInfo(skuInfoEntity);
//保存 sku 的基本信息;到 commodity_sku_info end
//保存 sku 的图片信息;到 commodity_sku_images start
//一个 sku 可以有多张图片
                Long skuId = skuInfoEntity.getSkuId();
                List&lt;SkuImagesEntity&gt; imagesEntities =
                        item.getImages().stream().map(img -&gt; {
//取出当前 item (就是一个 spk)的信息,组装成 一个 SkuImagesEntity, 进行保存

                            SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                            skuImagesEntity.setSkuId(skuId);
                            skuImagesEntity.setImgUrl(img.getImgUrl());
                            skuImagesEntity.setDefaultImg(img.getDefaultImg());
                            return skuImagesEntity;
                        }).filter(entity -&gt; {
//如果 image 为 empty ,就不过滤掉, 返回 true 就是需要,false 就是剔除
                            return !StringUtils.isEmpty(entity.getImgUrl());
                        }).collect(Collectors.toList());
                skuImagesService.saveBatch(imagesEntities);
//保存 sku 的图片信息;到 commodity_sku_images end
            });
      }
    }



//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
//// commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//
//      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku,
//// 组成一个可以销售的商品信息
////注意 Images 类在 vo 包下, 确保有@Data 修饰
//      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
//      if(skus!=null &amp;&amp; skus.size()&gt;0){
//            skus.forEach(item-&gt;{
//                String defaultImg = "default.jpg";
////json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
//                for (Images image : item.getImages()) {
//                  if(image.getDefaultImg() == 1){
//                        defaultImg = image.getImgUrl();
//                  }
//                }
//                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
////把 item 中的信息拷贝给 skuInfoEntity
//                BeanUtils.copyProperties(item,skuInfoEntity);
////item 中没有信息,但是 skuInfoEntity 需要的信息,
////可以从 spuInfoEntity 中获取
//                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
//                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
//                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
//                skuInfoEntity.setSpuId(spuInfoEntity.getId());
//                skuInfoEntity.setSkuDefaultImg(defaultImg);
////1)、保存 sku 的基本信息;到 commodity_sku_info
//                skuInfoService.saveSkuInfo(skuInfoEntity);
//            });
//      }
//    }


//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
//// commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//    }

    //因为有多个添加操作,使用事务控制
//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if(decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      }else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
//    }

    @Override
    public PageUtils queryPage(Map&lt;String, Object&gt; params) {
      IPage&lt;SpuInfoEntity&gt; page = this.page(
                new Query&lt;SpuInfoEntity&gt;().getPage(params),
                new QueryWrapper&lt;SpuInfoEntity&gt;()
      );

      return new PageUtils(page);
    }


    /**
   * 保存 spu 的基本信息到 commodity_spu_info
   *
   * @param spuInfoEntity
   */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
      this.baseMapper.insert(spuInfoEntity);
    }

//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////2. 将 SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
//    }

}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356730-177818897.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356740-1025478629.png"></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)">保存 sku 的销售属性</font></strong></p>
<p><font style="color: rgba(0, 0, 0, 1)">对照 JSON 数据分析</font></p>
<p><strong><font style="color: rgba(0, 0, 0, 1)"></font></strong></p>
<pre><code class="language-sql"># 创建保存 sku 的销售属性表
/*====================================================*/
/* 1.保存 sku 的销售属性/值, 一个 sku 可以有多个销售属性/值
/* 2.比如 1 个 sku 有颜色(黑色)和尺寸(100*300)两个销售属性
/*=====================================================*/
USE hspliving_commodity
CREATE TABLE commodity_sku_sale_attr_value
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
sku_id BIGINT COMMENT 'sku_id',
attr_id BIGINT COMMENT 'attr_id',
attr_name VARCHAR(200) COMMENT '销售属性名',
attr_value VARCHAR(200) COMMENT '销售属性值',
attr_sort INT COMMENT '顺序', PRIMARY KEY (id)
)CHARSET=utf8mb4 COMMENT='sku 的销售属性/值表';

SELECT * FROM commodity_sku_sale_attr_value
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356795-1446756936.png"></p>
<pre><code class="language-java">package com.rainbowsea.rainbowsealiving.commodity.service.impl;

import com.rainbowsea.rainbowsealiving.commodity.entity.AttrEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.ProductAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuImagesEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SkuSaleAttrValueEntity;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoDescEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.AttrService;
import com.rainbowsea.rainbowsealiving.commodity.service.ProductAttrValueService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuInfoService;
import com.rainbowsea.rainbowsealiving.commodity.service.SkuSaleAttrValueService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuImagesService;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoDescService;
import com.rainbowsea.rainbowsealiving.commodity.vo.Attr;
import com.rainbowsea.rainbowsealiving.commodity.vo.BaseAttrs;
import com.rainbowsea.rainbowsealiving.commodity.vo.Images;
import com.rainbowsea.rainbowsealiving.commodity.vo.Skus;
import com.rainbowsea.rainbowsealiving.commodity.vo.SpuSaveVO;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.common.utils.PageUtils;
import com.rainbowsea.common.utils.Query;

import com.rainbowsea.rainbowsealiving.commodity.dao.SpuInfoDao;
import com.rainbowsea.rainbowsealiving.commodity.entity.SpuInfoEntity;
import com.rainbowsea.rainbowsealiving.commodity.service.SpuInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl&lt;SpuInfoDao, SpuInfoEntity&gt; implements SpuInfoService {


    @Resource
    AttrService attrService;

    @Resource
    ProductAttrValueService productAttrValueService;

    @Resource
    SkuInfoService skuInfoService;


    @Resource
    SkuImagesService skuImagesService;
    @Resource
    SpuImagesService imagesService;

    @Resource
    SpuInfoDescService spuInfoDescService;


    @Resource
    SkuSaleAttrValueService skuSaleAttrValueService;

    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
      spuInfoEntity.setCreateTime(new Date());
      spuInfoEntity.setUpdateTime(new Date());
//SpuInfoEntity 信息保存到 commodity_spu_info
      this.saveBaseSpuInfo(spuInfoEntity);
      //2、保存 Spu 的描述图片路径 commodity_spu_info_desc
      List&lt;String&gt; decript = spuSaveVO.getDecript();
      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
//获取到刚刚保存的 spu 基本信息对应的 id
      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
//注意:这里有可能没有图片, 可以设置一个默认图片
      if (decript.size() == 0) {
            spuInfoDescEntity.setDecript("default.jpg");
      } else {
            spuInfoDescEntity.setDecript(String.join(",", decript));
      }
//保存到 commodity_spu_info_desc, 这个方法时我们完成
      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//3、保存 spu 的图片集 commodity_spu_images
      List&lt;String&gt; images = spuSaveVO.getImages();
//获取到刚刚保存的 spu 基本信息对应的 id
      imagesService.saveImages(spuInfoEntity.getId(), images);
//4 、 保 存 spu 的 基 本 属 性 ( 一 个 spu 可 以 有 多 个 基 本 属 性 / 规 格 参 数 ):commodity_product_attr_value
      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());
            AttrEntity id = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(id.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(spuInfoEntity.getId());
            return valueEntity;
      }).collect(Collectors.toList());
      productAttrValueService.saveProductAttr(collect);
      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku, 组成一个可以销售的商品信息

      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
      if (skus != null &amp;&amp; skus.size() &gt; 0) {
            skus.forEach(item -&gt; {
                String defaultImg = "default.jpg";
                //json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
                for (Images image : item.getImages()) {
                  if (image.getDefaultImg() == 1) {
                        defaultImg = image.getImgUrl();
                  }
                }
//保存 sku 的基本信息;到 commodity_sku_info start
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
//把 item 中的信息拷贝给 skuInfoEntity
                BeanUtils.copyProperties(item, skuInfoEntity);
//item 中没有信息,但是 skuInfoEntity 需要的信息,可以从 spuInfoEntity 中获取
                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
                skuInfoEntity.setSpuId(spuInfoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
                skuInfoService.saveSkuInfo(skuInfoEntity);
//保存 sku 的基本信息;到 commodity_sku_info end
                //保存 sku 的图片信息;到 commodity_sku_images start
//一个 sku 可以有多张图片
                Long skuId = skuInfoEntity.getSkuId();
                List&lt;SkuImagesEntity&gt; imagesEntities = item.getImages().stream().map(img -&gt;
                {
//取出当前 item (就是一个 spk)的信息,组装成 一个 SkuImagesEntity, 进行保存
                  SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                  skuImagesEntity.setSkuId(skuId);
                  skuImagesEntity.setImgUrl(img.getImgUrl());
                  skuImagesEntity.setDefaultImg(img.getDefaultImg());
                  return skuImagesEntity;
                }).filter(entity -&gt; {
//如果 image 为 empty ,就不过滤掉, 返回 true 就是需要,false 就是剔除
                  return !StringUtils.isEmpty(entity.getImgUrl());
                }).collect(Collectors.toList());
                skuImagesService.saveBatch(imagesEntities);
//保存 sku 的图片信息;到 commodity_sku_images end
//保存 sku 的销售属性-值到 commodity_sku_sale_attr_value start
                List&lt;Attr&gt; attr = item.getAttr();
                List&lt;SkuSaleAttrValueEntity&gt; skuSaleAttrValueEntities =
                        attr.stream().map(a -&gt; {
                            SkuSaleAttrValueEntity attrValueEntity = new
                                    SkuSaleAttrValueEntity();
                            BeanUtils.copyProperties(a, attrValueEntity);
                            attrValueEntity.setSkuId(skuId);
                            return attrValueEntity;
                        }).collect(Collectors.toList());
                skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);
//保存 sku 的销售属性-值到 commodity_sku_sale_attr_value end
            });

      }
    }

//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4 、 保 存 spu 的 基 本 属 性 ( 一 个 spu 可 以 有 多 个 基 本 属 性 / 规 格 参 数 ):commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//
//      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku, 组成一个可以销售的商品信息.
//      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
//      if (skus != null &amp;&amp; skus.size() &gt; 0) {
//            skus.forEach(item -&gt; {
//                String defaultImg = "default.jpg";
////json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
//                for (Images image : item.getImages()) {
//                  if (image.getDefaultImg() == 1) {
//                        defaultImg = image.getImgUrl();
//                  }
//                }
////保存 sku 的基本信息;到 commodity_sku_info start
//                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
////把 item 中的信息拷贝给 skuInfoEntity
//                BeanUtils.copyProperties(item, skuInfoEntity);
////item 中没有信息,但是 skuInfoEntity 需要的信息,可以从 spuInfoEntity 中获取
//                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
//                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
//                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
//                skuInfoEntity.setSpuId(spuInfoEntity.getId());
//                skuInfoEntity.setSkuDefaultImg(defaultImg);
//                skuInfoService.saveSkuInfo(skuInfoEntity);
////保存 sku 的基本信息;到 commodity_sku_info end
////保存 sku 的图片信息;到 commodity_sku_images start
////一个 sku 可以有多张图片
//                Long skuId = skuInfoEntity.getSkuId();
//                List&lt;SkuImagesEntity&gt; imagesEntities =
//                        item.getImages().stream().map(img -&gt; {
////取出当前 item (就是一个 spk)的信息,组装成 一个 SkuImagesEntity, 进行保存
//
//                            SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
//                            skuImagesEntity.setSkuId(skuId);
//                            skuImagesEntity.setImgUrl(img.getImgUrl());
//                            skuImagesEntity.setDefaultImg(img.getDefaultImg());
//                            return skuImagesEntity;
//                        }).filter(entity -&gt; {
////如果 image 为 empty ,就不过滤掉, 返回 true 就是需要,false 就是剔除
//                            return !StringUtils.isEmpty(entity.getImgUrl());
//                        }).collect(Collectors.toList());
//                skuImagesService.saveBatch(imagesEntities);
////保存 sku 的图片信息;到 commodity_sku_images end
//            });
//      }
//    }
//


//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
//// commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//
//      //5、保存当前 spu 对应的所有 sku 信息;一个 spu 可以 对应多个 sku,
//// 组成一个可以销售的商品信息
////注意 Images 类在 vo 包下, 确保有@Data 修饰
//      List&lt;Skus&gt; skus = spuSaveVO.getSkus();
//      if(skus!=null &amp;&amp; skus.size()&gt;0){
//            skus.forEach(item-&gt;{
//                String defaultImg = "default.jpg";
////json 会提交很多图片,如果是当前这个 sku 的默认图片就先保存下 url
//                for (Images image : item.getImages()) {
//                  if(image.getDefaultImg() == 1){
//                        defaultImg = image.getImgUrl();
//                  }
//                }
//                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
////把 item 中的信息拷贝给 skuInfoEntity
//                BeanUtils.copyProperties(item,skuInfoEntity);
////item 中没有信息,但是 skuInfoEntity 需要的信息,
////可以从 spuInfoEntity 中获取
//                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
//                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
//                skuInfoEntity.setSaleCount(0L);//初始化销量为 0
//                skuInfoEntity.setSpuId(spuInfoEntity.getId());
//                skuInfoEntity.setSkuDefaultImg(defaultImg);
////1)、保存 sku 的基本信息;到 commodity_sku_info
//                skuInfoService.saveSkuInfo(skuInfoEntity);
//            });
//      }
//    }


//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
//      //1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if (decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      } else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
////4、保存 spu 的基本属性(一个 spu 可以有多个基本属性/规格参数):
//// commodity_product_attr_value
//      List&lt;BaseAttrs&gt; baseAttrs = spuSaveVO.getBaseAttrs();
//      List&lt;ProductAttrValueEntity&gt; collect = baseAttrs.stream().map(attr -&gt; {
//            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
//            valueEntity.setAttrId(attr.getAttrId());
//            AttrEntity id = attrService.getById(attr.getAttrId());
//            valueEntity.setAttrName(id.getAttrName());
//            valueEntity.setAttrValue(attr.getAttrValues());
//            valueEntity.setQuickShow(attr.getShowDesc());
//            valueEntity.setSpuId(spuInfoEntity.getId());
//            return valueEntity;
//      }).collect(Collectors.toList());
//      productAttrValueService.saveProductAttr(collect);
//    }

      //因为有多个添加操作,使用事务控制
//    @Transactional
//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
////2、保存 Spu 的描述图片路径 commodity_spu_info_desc
//      List&lt;String&gt; decript = spuSaveVO.getDecript();
//      SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
////获取到刚刚保存的 spu 基本信息对应的 id
//      spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
////注意:这里有可能没有图片, 可以设置一个默认图片
//      if(decript.size() == 0) {
//            spuInfoDescEntity.setDecript("default.jpg");
//      }else {
//            spuInfoDescEntity.setDecript(String.join(",", decript));
//      }
////保存到 commodity_spu_info_desc, 这个方法时我们完成
//      spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);
//
//      //3、保存 spu 的图片集 commodity_spu_images
//      List&lt;String&gt; images = spuSaveVO.getImages();
////获取到刚刚保存的 spu 基本信息对应的 id
//      imagesService.saveImages(spuInfoEntity.getId(), images);
//    }

      @Override
      public PageUtils queryPage (Map &lt; String, Object &gt; params){
            IPage&lt;SpuInfoEntity&gt; page = this.page(
                  new Query&lt;SpuInfoEntity&gt;().getPage(params),
                  new QueryWrapper&lt;SpuInfoEntity&gt;()
            );

            return new PageUtils(page);
      }


      /**
         * 保存 spu 的基本信息到 commodity_spu_info
         *
         * @param spuInfoEntity
         */
      @Override
      public void saveBaseSpuInfo (SpuInfoEntity spuInfoEntity){
            this.baseMapper.insert(spuInfoEntity);
      }

//    @Override
//    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
////1、保存 spu 基本信息 , 对应的表 commodity_spu_info
//      SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
//      BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
//      spuInfoEntity.setCreateTime(new Date());
//      spuInfoEntity.setUpdateTime(new Date());
////2. 将 SpuInfoEntity 信息保存到 commodity_spu_info
//      this.saveBaseSpuInfo(spuInfoEntity);
//    }

    }
</code></pre>
<h1 id="使用订阅发布功能的实现">使用订阅发布功能的实现</h1>
<p><font style="color: rgba(0, 0, 0, 1)">因为要使用到订阅发布功能,所以我们需要做如下操作. </font></p>
<p><font style="color: rgba(0, 0, 0, 1)">使用 npm 添加依赖:npm install --save pubsub-js(失败的话使用此命令:cnpm install --save pubsub-js)</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202504/3084824-20250429105356791-57403957.png"></p>
<pre><code class="language-shell">E:\Java\project\RainbowSealiving\renren-fast-vue&gt;npm install --save pubsub-js
</code></pre><br><br>
来源:https://www.cnblogs.com/TheMagicalRainbowSea/p/18853323
頁: [1]
查看完整版本: 通用型产品发布解决方案(SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven)03