学习目标:
根据以上的需求,以此将SKU关联的数据库表结构设计为如下:
添加销售属性信息
在代码生成器中,生成商品SKU相关表基础代码到service-product
商品微服务模块,相关表名称如下
但是这里我们不建议生成以上相关表三层类,这样同时生成的三层类过于多,而以上的6张表都跟商品SKU相关,故建议只提供对应的实体类跟持久层接口。业务层/持久层我们提供一个抽取SkuManagerXxx
注意:在mybatis-plus-code
代码生成器模块中CodeGenerator
设置禁用模板来达到目标
代码生成器执行需要填写的表:sku_info,sku_image,sku_attr_value,sku_sale_attr_value
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/235
在SpuManageController处理查询SPU销售属性方法
/**
* 根据SPUID查询商品销售属性列表(包含属性值)
*
* @param spuId
* @return
*/
@GetMapping("/spuSaleAttrList/{spuId}")
public Result 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) {
//调用自定义SQL查询销售属性名称以及值
SpuSaleAttrMapper spuSaleAttrMapper = (SpuSaleAttrMapper) spuSaleAttrService.getBaseMapper();
List<SpuSaleAttr> spuSaleAttrList = spuSaleAttrMapper.getSpuSaleAttrList(spuId);
return spuSaleAttrList;
}
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 2023-02-22
*/
public interface SpuSaleAttrMapper extends BaseMapper<SpuSaleAttr> {
/**
* 根据SPUID查询销售属性列表(包含属性值)
* @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查询当前商品所有图片
* @param spuId
* @return
*/
@GetMapping("/spuImageList/{spuId}")
public Result getSpuImageList(@PathVariable("spuId") Long spuId){
List<SpuImage> list = spuManageService.getSpuImageList(spuId);
return Result.ok(list);
}
在SpuManageService
接口中新增方法
/**
* 根据商品SpuID查询当前商品所有图片
* @param spuId
* @return
*/
List<SpuImage> getSpuImageList(Long spuId);
在SpuManageServiceImpl
中实现上面方法
/**
* 根据商品SpuID查询当前商品所有图片
*
* @param spuId
* @return
*/
@Override
public List<SpuImage> getSpuImageList(Long spuId) {
LambdaQueryWrapper<SpuImage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SpuImage::getSpuId, spuId);
return spuImageService.list(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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: atguigu
* @create: 2023-02-24 11:39
*/
@RestController
@RequestMapping("/admin/product")
public class SkuManageController {
@Autowired
private SkuManageService skuManageService;
/**
* 保存商品SKU信息
* @param skuInfo
* @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.model.SkuAttrValue;
import com.atguigu.gmall.product.model.SkuImage;
import com.atguigu.gmall.product.model.SkuInfo;
import com.atguigu.gmall.product.model.SkuSaleAttrValue;
import com.atguigu.gmall.product.service.*;
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: 2023-02-24 11:40
*/
@Service
public class SkuManageServiceImpl implements SkuManageService {
@Autowired
private SkuInfoService skuInfoService;
@Autowired
private SkuImageService skuImageService;
@Autowired
private SkuAttrValueService skuAttrValueService;
@Autowired
private SkuSaleAttrValueService skuSaleAttrValueService;
/**
* 保存商品SKU信息
* 1.将前端提交商品SKU基本信息存入到sku_info表
* 2.将前端提交当前SKU相关图片集合 存入到 sku_image表
* 3.将前端提交当前SKU的平台属性信息 存入到 sku_attr_value表
* 4.将前端提交当前SKU的销售属性信息 存入到 sku_sale_attr_value 表
*
* @param skuInfo
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveSkuInfo(SkuInfo skuInfo) {
//1.将前端提交商品SKU基本信息存入到sku_info表
skuInfoService.save(skuInfo);
//2.将前端提交当前SKU相关图片集合 存入到 sku_image表
List<SkuImage> skuImageList = skuInfo.getSkuImageList();
if (!CollectionUtils.isEmpty(skuImageList)) {
skuImageList.stream().forEach(skuImage -> {
//2.1 将商品SKU图片关联到SKU
skuImage.setSkuId(skuInfo.getId());
});
//2.2 批量保存商品SKU图片
skuImageService.saveBatch(skuImageList);
}
//3.将前端提交当前SKU的平台属性信息 存入到 sku_attr_value表
List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
if (!CollectionUtils.isEmpty(skuAttrValueList)) {
//2.1 将平台属性信息关联到SKU
skuAttrValueList.stream().forEach(skuAttrValue -> {
skuAttrValue.setSkuId(skuInfo.getId());
});
//2.2 批量保存平台属性
skuAttrValueService.saveBatch(skuAttrValueList);
}
//4.将前端提交当前SKU的销售属性信息 存入到 sku_sale_attr_value 表
List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
if(!CollectionUtils.isEmpty(skuSaleAttrValueList)){
skuSaleAttrValueList.stream().forEach(skuSaleAttrValue -> {
//4.1 销售属性关联SPU
skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
//4.2 销售属性关联SKU
skuSaleAttrValue.setSkuId(skuInfo.getId());
});
//4.3 批量保存SKU销售属性信息
skuSaleAttrValueService.saveBatch(skuSaleAttrValueList);
}
}
}
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/179
SkuManageController 控制器
/**
* 根据分类ID分页查询商品SKU列表
*
* @param page
* @param limit
* @return
*/
@GetMapping("/list/{page}/{limit}")
public Result getSkuListByPage(@PathVariable("page") Long page, @PathVariable("limit") Long limit, @RequestParam("category3Id") Long category3Id) {
//1.构建分页对象
IPage<SkuInfo> iPage = new Page<>(page, limit);
//2.查询分页数据 alt+enter 快速修正错误
iPage = skuManageService.getSkuListByPage(iPage, category3Id);
return Result.ok(iPage);
}
在SkuManageService 接口中添加方法
/**
* 根据分类ID分页查询商品SKU列表
* @param iPage
* @param category3Id
* @return
*/
IPage<SkuInfo> getSkuListByPage(IPage<SkuInfo> iPage, Long category3Id);
在SkuManageServiceImpl 实现类中实现上面方法
/**
* 根据分类ID分页查询商品SKU列表
*
* @param iPage
* @param category3Id
* @return
*/
@Override
public IPage<SkuInfo> getSkuListByPage(IPage<SkuInfo> iPage, Long category3Id) {
LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
if (category3Id != null) {
queryWrapper.eq(SkuInfo::getCategory3Id, category3Id);
}
queryWrapper.orderByDesc(SkuInfo::getUpdateTime);
return skuInfoService.page(iPage, queryWrapper);
}
YAPI接口地址:
SkuManageController 控制器
/**
* 商品SKU上架
* @param skuId
* @return
*/
@GetMapping("/onSale/{skuId}")
public Result onSale(@PathVariable("skuId") Long skuId){
skuManageService.onSale(skuId);
return Result.ok();
}
/**
* 商品SKU下架
* @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上架
*
* @param skuId
* @return
*/
@Override
public void onSale(Long skuId) {
//1.修改数据库中上架状态
SkuInfo skuInfo = new SkuInfo();
skuInfo.setId(skuId);
skuInfo.setIsSale(1);
skuInfoService.updateById(skuInfo);
//2.TODO 将来还需要同步将索引库ES的商品进行上架;需要构建商品缓存到Redis
}
/**
* 商品SKU下架
*
* @param skuId
* @return
*/
@Override
public void cancelSale(Long skuId) {
//SkuInfo skuInfo = skuInfoService.getById(skuId);
//if (skuInfo != null && skuInfo.getIsSale() != 0) {
// skuInfo.setIsSale(0);
// skuInfoService.updateById(skuInfo);
//}
LambdaUpdateWrapper<SkuInfo> updateWrapper = new LambdaUpdateWrapper<>();
//1.设置更新条件
updateWrapper.eq(SkuInfo::getId, skuId);
//1.设置更新字段值
updateWrapper.set(SkuInfo::getIsSale, 0);
skuInfoService.update(updateWrapper);
//2.TODO 将来还需要同步将索引库ES的商品进行下架;需要删除商品缓存Redis
}
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
多种方式存储数据
### 5.2.10 字符串替换
request.setAttribute("today", new Date());
¥1,000.00 ``` 替换: 后台代码: request.setAttribute("str","atguigu"); 页面: http://localhost:8080/atguigu ```strings 操作字符串的工具类,还有 @dates ,#numbers 等工具类。
aaa
bbb
```