学习目标:
根据以上的需求,以此将SKU关联的数据库表结构设计为如下:
添加销售属性信息
在代码生成器中,生成商品SKU相关表基础代码到service-product
商品微服务模块,相关表名称如下
但是这里我们不建议生成以上相关表三层类,这样同时生成的三层类过于多,而以上的6张表都跟商品SKU相关,故建议只提供对应的实体类跟持久层接口。业务层/持久层我们提供一个抽取SkuManagerXxx
注意:在mybatis-plus-code
代码生成器模块中CodeGenerator
设置禁用模板来达到目标
代码生成器执行需要填写的表:sku_info,sku_sale_attr,sku_sale_attr_value,sku_attr_value,sku_image
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/235
在SpuManageController处理查询SPU销售属性方法
/**
* 根据商品SPUID查询销售属性名称以及值
* @param spuId
* @return
*/
@GetMapping("/spuSaleAttrList/{spuId}")
public Result<List<SpuSaleAttr>> getSpuSaleAttrList(@PathVariable("spuId") Long spuId){
List<SpuSaleAttr> list = spuManageService.getSpuSaleAttrList(spuId);
return Result.ok(list);
}
SpuManageService中增加接口
/**
* 根据spuId 查询销售属性集合
* @param spuId
* @return
*/
List<SpuSaleAttr> getSpuSaleAttrList(Long spuId);
SpuManageServiceImpl实现类中实现该方法
/**
* 根据spuId 查询销售属性集合
* @param spuId
* @return
*/
@Override
public List<SpuSaleAttr> getSpuSaleAttrList(Long spuId) {
return spuSaleAttrMapper.selectSpuSaleAttrList(spuId);
}
SpuSaleAttrMapper中增加自定义接口
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.SpuSaleAttr;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* spu销售属性 Mapper 接口
*
* @author atguigu
* @since 2022-12-24
*/
public interface SpuSaleAttrMapper extends BaseMapper<SpuSaleAttr> {
/**
* 根据SPUID查询商品Spu销售属性名称列表 包含spu销售属性值
* @param spuId
* @return
*/
List<SpuSaleAttr> getSpuSaleAttrList(@Param("spuId") Long spuId);
}
对应的在resource/mapper目录下创建映射文件SpuSaleAttrMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.gmall.product.mapper.SpuSaleAttrMapper">
<!--自定义结果映射-->
<resultMap id="spuSaleAttrMap" type="com.atguigu.gmall.product.model.SpuSaleAttr" autoMapping="true">
<id property="id" column="id"></id>
<!--单独处理销售属性值集合-->
<collection property="spuSaleAttrValueList" ofType="com.atguigu.gmall.product.model.SpuSaleAttrValue" autoMapping="true">
<id property="id" column="spu_sale_attr_value_id"></id>
</collection>
</resultMap>
<!--根据商品spuId查询spu销售属性列表-包含销售属性值-->
<select id="getSpuSaleAttrList" resultMap="spuSaleAttrMap">
select
ssa.id ,
ssa.spu_id,
ssa.sale_attr_name,
ssa.base_sale_attr_id,
sav.id spu_sale_attr_value_id,
sav.sale_attr_value_name
from spu_sale_attr ssa
inner join spu_sale_attr_value sav
on ssa.base_sale_attr_id = sav.base_sale_attr_id
and ssa.spu_id = sav.spu_id
where ssa.spu_id = #{spuId}
</select>
</mapper>
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/243
功能分析:图片列表是根据spuId得来,涉及到的数据库表spu_image
在service-product
模块中Spu控制器类:SpuManageController
/**
* 根据spuId 查询spuImageList图片列表
*
* @param spuId
* @return
*/
@GetMapping("spuImageList/{spuId}")
public Result<List<SpuImage>> getSpuImageList(@PathVariable("spuId") Long spuId) {
List<SpuImage> spuImageList = spuManageService.getSpuImageList(spuId);
return Result.ok(spuImageList);
}
在SpuManageService
接口中新增方法
/**
* 根据spuId 查询spuImageList
* @param spuId
* @return
*/
List<SpuImage> getSpuImageList(Long spuId);
在SpuManageServiceImpl
中实现上面方法
/**
* 根据spuId 查询spuImageList
* @param spuId
* @return
*/
@Override
public List<SpuImage> getSpuImageList(Long spuId) {
LambdaQueryWrapper<SpuImage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SpuImage::getSpuId, spuId);
return spuImageMapper.selectList(queryWrapper);
}
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/227
新建SkuManageController中提供处理保存SKU方法
package com.atguigu.gmall.product.controller;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.product.model.SkuInfo;
import com.atguigu.gmall.product.service.SkuManageService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Api(tags = "商品SKU控制器")
@RestController
@RequestMapping("admin/product")
public class SkuManageController {
@Autowired
private SkuManageService skuManageService;
/**
* 保存SKU信息
*
* @param skuInfo SKU相关信息
* @return
*/
@PostMapping("/saveSkuInfo")
public Result saveSkuInfo(@RequestBody SkuInfo skuInfo) {
skuManageService.saveSkuInfo(skuInfo);
return Result.ok();
}
}
创建业务接口:SkuManageService接口中增加方法
package com.atguigu.gmall.product.service;
import com.atguigu.gmall.product.model.SkuInfo;
public interface SkuManageService {
/**
* 保存sku
* @param skuInfo
* @return
*/
void saveSkuInfo(SkuInfo skuInfo);
}
创建业务实现类:SkuManageServiceImpl实现类中实现上面方法
package com.atguigu.gmall.product.service.impl;
import com.atguigu.gmall.product.mapper.SkuAttrValueMapper;
import com.atguigu.gmall.product.mapper.SkuImageMapper;
import com.atguigu.gmall.product.mapper.SkuInfoMapper;
import com.atguigu.gmall.product.mapper.SkuSaleAttrValueMapper;
import com.atguigu.gmall.product.model.*;
import com.atguigu.gmall.product.service.SkuManageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @author: atguigu
* @create: 2022-12-13 01:15
*/
@Service
public class SkuManageServiceImpl implements SkuManageService {
@Autowired
private SkuInfoMapper skuInfoMapper;
@Autowired
private SkuImageMapper skuImageMapper;
@Autowired
private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
@Autowired
private SkuAttrValueMapper skuAttrValueMapper;
/**
* 保存SKU信息
* 1.将SKU基本信息存入sku_info表中
* 2.将提交SKU图片存入sku_image表 关联SKU 设置sku_id逻辑外键
* 3.将提交的平台属性列表 批量保存 sku_attr_value 关联SKU 设置sku_id逻辑外键
* 4.将提交的销售属性列表 批量保存 sku_sale_attr_value 关联SKU 设置sku_id逻辑外键
*
* @param skuInfo SKU相关信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveSkuInfo(SkuInfo skuInfo) {
//1.将SKU基本信息存入sku_info表中
skuInfoMapper.insert(skuInfo);
//2.将提交SKU图片存入sku_image表 关联SKU 设置sku_id逻辑外键
if (!CollectionUtils.isEmpty(skuInfo.getSkuImageList())) {
skuInfo.getSkuImageList().stream().forEach(skuImage -> {
//2.1 关联商品SKU 设置逻辑外键sku_Id
skuImage.setSkuId(skuInfo.getId());
//2.2 执行批量保存
skuImageMapper.insert(skuImage);
});
}
//3.将提交的平台属性列表 批量保存 sku_attr_value 关联SKU 设置sku_id逻辑外键
if (!CollectionUtils.isEmpty(skuInfo.getSkuAttrValueList())) {
skuInfo.getSkuAttrValueList().stream().forEach(skuAttrValue -> {
//3.1 关联商品SKU 设置逻辑外键sku_Id
skuAttrValue.setSkuId(skuInfo.getId());
//3.2 保存商品SKU平台属性值
skuAttrValueMapper.insert(skuAttrValue);
});
}
//4.将提交的销售属性列表 批量保存 sku_sale_attr_value 关联SKU 设置sku_id逻辑外键
if (!CollectionUtils.isEmpty(skuInfo.getSkuSaleAttrValueList())) {
skuInfo.getSkuSaleAttrValueList().stream().forEach(skuSaleAttrValue -> {
//4.1 关联商品SPU 设置逻辑外键spu_Id
skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
//4.2 关联商品SKU 设置逻辑外键sku_Id
skuSaleAttrValue.setSkuId(skuInfo.getId());
//4.3 完成保存
skuSaleAttrValueMapper.insert(skuSaleAttrValue);
});
}
}
}
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/179
SkuManageController 控制器
/**
* 查询商品SKU列表
* @param page 页面
* @param limit 页大小
* @param skuInfo 查询条件
* @return
*/
@GetMapping("/list/{page}/{limit}")
public Result getSkuInfoPage(@PathVariable Long page,
@PathVariable Long limit,
SkuInfo skuInfo){
// 构建一个Page对象
Page<SkuInfo> skuInfoPage = new Page<>(page,limit);
// 调用服务层方法!
IPage iPage = skuManageService.getSkuInfoPage(skuInfoPage,skuInfo);
return Result.ok(iPage);
}
在SkuManageService 接口中添加方法
/**
* 根据三级分类数据获取到skuInfo 列表
* @param skuInfoPage
* @param skuInfo
* @return
*/
IPage getSkuInfoPage(Page<SkuInfo> skuInfoPage, SkuInfo skuInfo);
在SkuManageServiceImpl 实现类中实现上面方法
/**
* 根据分类ID查询商品SKU列表
*
* @param skuInfoPage
* @param skuInfo 查询条件
* @return
*/
@Override
public IPage getSkuInfoPage(Page<SkuInfo> skuInfoPage, SkuInfo skuInfo) {
//1.构建查询条件
LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
if (skuInfo.getCategory3Id() != null) {
queryWrapper.eq(SkuInfo::getCategory3Id, skuInfo.getCategory3Id());
}
queryWrapper.orderByDesc(SkuInfo::getUpdateTime);
//2.执行查询
return skuInfoMapper.selectPage(skuInfoPage, queryWrapper);
}
YAPI接口地址:
SkuManageController 控制器
/**
* 商品上架
* @param skuId
* @return
*/
@GetMapping("onSale/{skuId}")
public Result onSale(@PathVariable("skuId") Long skuId) {
skuManageService.onSale(skuId);
return Result.ok();
}
/**
* 商品下架
* @param skuId
* @return
*/
@GetMapping("cancelSale/{skuId}")
public Result cancelSale(@PathVariable("skuId") Long skuId) {
skuManageService.cancelSale(skuId);
return Result.ok();
}
在SkuManageService 接口中新增方法
/**
* 商品上架
* @param skuId
*/
void onSale(Long skuId);
/**
* 商品下架
* @param skuId
*/
void cancelSale(Long skuId);
在SkuManageServiceImpl 实现类中实现上面方法
/**
* 将指定SKU进行上架
* TODO 同步更新索引库的上下架状态
* @param skuId
* @return
*/
@Override
public void onSale(Long skuId) {
SkuInfo skuInfo = new SkuInfo();
skuInfo.setId(skuId);
skuInfo.setIsSale(1);
skuInfoMapper.updateById(skuInfo);
}
/**
* 将指定SKU进行下架
*
* @param skuId
* @return
*/
@Override
public void cancelSale(Long skuId) {
SkuInfo skuInfo = new SkuInfo();
skuInfo.setId(skuId);
LambdaUpdateWrapper<SkuInfo> updateWrapper = new LambdaUpdateWrapper<>();
//设置更新字段
updateWrapper.set(SkuInfo::getIsSale, 0);
//设置更新条件
updateWrapper.eq(SkuInfo::getId, skuId);
skuInfoMapper.update(skuInfo, updateWrapper);
}
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。与其它模板引擎相比, Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用!
类似模板技术
官方网站:https://www.thymeleaf.org/index.html
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu</groupId>
<artifactId>thymeleaf-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--引入spring boot 父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
启动类
package com.atguigu.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author: atguigu
* @create: 2022-12-13 10:45
*/
@SpringBootApplication
public class ThymeleafDemoApp {
public static void main(String[] args) {
SpringApplication.run(ThymeleafDemoApp.class, args);
}
}
创建application.yml
server:
port: 9999
spring:
thymeleaf:
#关闭Thymeleaf的缓存
cache: false
不需要做任何配置,启动器已经帮我们把Thymeleaf的视图器配置完成:
而且,还配置了模板文件(html)的位置,与jsp类似的前缀+ 视图名 + 后缀风格:
![1526435706301](assets/1526435706301.png)
classpath:/templates/
.html
所以如果我们返回视图:users
,会指向到 classpath:/templates/users.html
准备一个controller,控制视图跳转。注意:控制器上要使用@Controller注解
package com.atguigu.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author: atguigu
* @create: 2022-12-13 10:56
*/
@Controller
public class HelloController {
@GetMapping("/hello")
public String show1(Model model){
model.addAttribute("msg", "Hello, Thymeleaf!");
return "hello";
}
}
在resources目录下新建文件夹:templates
在templates
目录下新建一个html模板文件:hello.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
<h1 th:text="${msg}">你好</h1>
</body>
</html>
启动项目,访问页面:
就像JSP的<%@Page %>一样 ,Thymeleaf的也要引入标签规范。不加这个虽然不影响程序运行,但是你的idea会不识别标签,不方便开发。
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
request.setAttribute("name", "刘德华");
<p th:text="'hello' + ${name}"></p>
List list = Arrays.asList("郑爽", "刘德华", "张惠妹", "成龙");
request.setAttribute("list", list);
<table> <!--s 表示集合中的元素 ${list}表示后台存储的集合 --> <tr th:each="s,stat: ${list}"> <td th:text="${s}"></td> <td th:text="${stat.index}"></td> <td th:text="${stat.count}"></td> <td th:text="${stat.size}"></td> <td th:text="${stat.even}"></td> <td th:text="${stat.odd}"></td> <td th:text="${stat.first}"></td> <td th:text="${stat.last}"></td> </tr> </table>
语法关键字 解释 stat 称作状态变量 index 当前迭代对象的 index(从 0 开始计算) count 当前迭代对象的 index(从 1 开始计算) size 被迭代对象的大小 even/odd 布尔值,当前循环是否是偶数/奇数 first 布尔值,当前循环是否是第一个 last 布尔值,当前循环是否是最后一个 5.2.4 判断
th:if 条件成立显示
th:unless 条件不成立的时候才会显示内容
model.addAttribute("age",18);
<h2>判断 if</h2> <div th:if="${age}>=18" th:text="success">good</div> <a th:unless="${age != 18}" th:text="success" >atguigu</a> <h2>判断 三元</h2> <div th:text="${age}>=18?'success':'failure'"></div>
5.2.5 取session中的属性
httpSession.setAttribute("addr","北京中南海");
<div th:text="${session.addr}"> </div>
5.2.6 引用内嵌页
top.html内容如下
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>hello</title> </head> <body> <html> 我是公共的内容组件 </html>
在hello.html中引入
<div th:include="top"/>
5.2.7 th:utext :解析样式
th:utext:识别html中的标签
request.setAttribute("gname","绿色");
<p th:utext="${gname}">color</p>
5.2.8 点击链接传值
```> @RequestMapping("list.html") > public String list(String category1Id, HttpServletRequest request){ > // 接收传递过来的数据 > System.out.println("获取到的数据:\t"+category1Id); > /*保存 category1Id*/ > request.setAttribute("category1Id",category1Id); > return category1Id; > } > ```
html 点我带你飞
### 5.2.9 多种存储方式 > ```java > HashMap<String, Object> map = new HashMap<>(); > map.put("stuNo","1000"); > map.put("stuName","张三"); > model.addAllAttributes(map); > ```
html
多种方式存储数据