代码托管平台:gitee
后端代码:
前端代码:
1.pom文件 阿里云与原生
原生:标签 阿里云用 以下代替 其中pom的pom指其实个父标签。
parent标签的作用: 定义当前SpringBoot所有依赖的版本号
build标签 springboot项目在打包部署发布时,需要依赖maven工具API
<dependencyManagement>
<!--相当于继承了一个父级 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<!--标识其是个父级 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.pom标签说明
<!--项目组ID -->
<groupId>com.example</groupId>
<!--项目 -->
<artifactId>springboot-shiro</artifactId>
<!--项目版本号 -->
<version>0.0.1-SNAPSHOT</version>
<!--项目名 -->
<name>springboot-shiro</name>
<!--项目描述 -->
<description>Demo project for Spring Boot</description>
<!--
<packaging>jar</packaging>
-->
3.dependency标签
主要用来加载外部依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<!--spring boot 启动项
思想:“开箱即用”
说明:只需要简单引入jar包,简单配置,就可以使用相应功能。
-->
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
maven的jar包查询网址:
例如 A项目依赖 b.jar,b.jar依赖c.jar 所以依赖b,jar相当于依赖了b,jar,c.jar.
Maven 依赖的传递性实现原理:
步骤:
当maven开始解析pom.xml文件,根据依赖坐标,找到指定的jar文件,添加该依赖
然后根据相应jar包的pom文件,进行扫描
扫描文件中的dependent
根据dependent坐标重复上述操作
文件传输有效性(以上图)
保证通过jar包镜像导入依赖没被修改,被植入木马
需求:网络数据传输,一般都需进行数据加密 ,maven传输一般采用SHA1数字签名
加密算法,保证数据传输有效性。
一般数据传输会将数据进行SHA1数字加密 生成相应hashcode 与传输好的数据生成的hashcode进行比较,相同才有效
1.porperties文件
数据类型: key=value 注意不要有空格
编码: 默认采用 ISO-8859-1编码
spring.datasource.username=root
2.yaml文件
数据类型:key :(空格)value 注意缩进
有层级效果 默认采用utf-8 可以写中文
spring:
datasource:
username : root
password: 123456
url : jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
msg:
hello: “下雨”
配置环境切换
可以在原有yaml的文件下新建 application-dev.yaml,application-test.yaml。。。等,跟相应的后缀,在其中进行相应配置,而在application.yaml 定义所要应用的环境
(properties相同,但要注意格式)
#spring.profiles.active=dev properties的后面跟所要应用application-的后缀
#指定默认的环境
spring:
profiles:
active: test
也可以通过分割符进行配置
#指定环境默认匹配值---配置要启动的环境
spring:
profiles:
active: dev
---
server:
port: 9090
#为环境定义名称
spring:
config:
activate:
on-profile: dev
---
#采用---实现环境分割
spring:
config:
activate:
on-profile: test
server:
port: 8080
注意 我们缩写的dao包,controller包。。。。应该和主启动类处于同一包下,否则无法内识别
常用注解
/**
@Controller
1. 将该类交给spring容器管理
2. @Controller 注解,在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面
@ResponseBody 将数据转化为JSON字符串
@restCOntroller 相当于@Controller+@ResponseBody
*/
@RestController //将该类交给Spring管理
@PropertySource(value="classpath:/demo.yaml",encoding = "UTF-8")
//当定义的yaml配合文件不是默认名application时,可以通过该注解获取yaml的地址,以
//及定义其编码格式
public class HelloController {
/**
* 规则:
* 1. 当Spring容器启动时,会加载YML配置文件.
* 会将内部的key-value结构 加载到spring维护的内存空间中
* 2. @Value功能,从spring容器中根据key 动态赋值
* 3. ${key} spring提供的springel表达式 简称:spel表达式
语法 从spring容器内部动态赋值
*
* 使用场景:
* 如果代码中需要给成员变量赋值时,一般采用动态赋值的方式.
*/
@Value("${msg.hello}")
private String msg;
/**
@RequestMapping 注解为控制器指定可以处理哪些 URL 请求
当接收到相应的地址请求,执行其下的方法
默认 value=“URL地址”
还可通过Method定义传参方法 get post等
@GetMapper 功能相同,传参方法为get
@PostMapper 功能相同,传参方法为post
*/
@RequestMapping("/hello")
public String hello(){
return msg;
}
}
@SpringBootApplication //主启动类的标记注解
public class SpringBootApplication {
//args是jvm进行参数传递的默认参数
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
}
3 .热部署
3.1 我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都会浪费大量时间,我们可以在修改代码后不重启就生效,在pom.xml中添加如下配置就可以实现这样的功能,我们称之为热部署
<!--
在配置里添加依赖
支持热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
3.2 配置后也有可能不生效:原因idea默认下不会自动编译,开启后重启idea即可
测试效果:
修改类–>保存:应用会重启
修改配置文件–>保存:应用会重启
修改页面–>保存:应用不会重启,但会重新加载,页面会刷新
4 Lombok插件
作用:动态生成常见 get/set/toString/equals/hashcode构造等在pojo类的方法
1 试用其要先在idea加载其插件
4.2 添加jar
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//@Api(注释) 能够对swagger进行相应注释
@Data //自动添加相对应的 get、set。tostring等方法
@AllArgsConstructor //生成所有的有参构造
@NoArgsConstructor //生成无参构造 因为写了有参会将系统不会生成默认无参,因此要自己写
@Accessors(chain = true)//开启链式编程 重写set方法
@ApiModel("用户实体类")//swagger 注解提供有关swagger模型的其它信息,类将在操作中用作类型时自动内省
public class User implements Serializable{//序列化
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("密码")
private String password;
/* public DemoUser setName(String name){
this.name=name;
return this;
}
public DemoUser setPassword(String password){
this.password=password;
return this;
}
//可以链式编程 必须写上述set方法
//Accessors(chain = true) 帮我们写了set方法
*/
//test
public void test(){
DemoUser demoUser=new DemoUser();
demoUser.setPassword(“10”).setName(“10”);
}
}
以下数据库表结构
item 商品主表 item_cat 商品分类表 item_desc 商品描述表
item表是商品的基本信息,tem_desc 表为商品详细数据,用户一般查询时都是查询基本信息。之后对具体的信息进行点击查询商品详细信息,为提高查询效率,将商品表分为item,item_desc 。一个商品和一个详情相互对应 item_desc .id = item.id id;
report 商品数量表
rights 权限表 role 角色表 role_rights 角色权限对应表
user 用户表 user_role 用户对应角色表
创建spring boot项目
编辑pom.xml文件
导入要用到的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
配置application.yaml
配置数据库链接 mybatis配置
设置端口号
server:
port: 8091
#开启日志调试
# 开启查询日志
logging:
level:
com.nuc.ssm.mapper: debug
#web: trace
#设置数据库链接
spring:
datasource:
# 要改对应数据库名
url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&autoReconnect=true&allowMultiQueries=true
# 要改对应名 码
username: 用户名
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver #数据源
#配置mybatis 若用MP只用 改为 mybatis-plus其他不变
mybatis:
#指定别名包----要对应,如有变化,也要修改
mapper-locations: classpath:/mappers/*.xml
# 要改对应路径
type-aliases-package: pojo对应路径
configuration:
#开启驼峰映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ```
按照数据库建立对应pojo类
导入前端vue框架项目
在命令指令框 vue ui 启动框架
1.1 用户登录验证接口
1.2 业务逻辑
1.3 实现
定义vo包下的SysResult类
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class SysResult {
private Integer status;//200正常 201失败
private String msg; //服务器返回的提示信息
private Object data; //服务器返回的业务数据
/*
* 说明:sysResult对象是系统的返回值对象,调用次数较多
* 如果每次都要手动添加200/201 则较为繁琐,能否封装一些方法简化代码调用
* 解决方法: 添加静态方法简化调用
* */
public static SysResult fail(){
return new SysResult(201,"服务器调用失败",null);
}
public static SysResult success(){
return new SysResult(200,"服务器调用成功",null);
}
//重载规则:参数不要耦合,否则会有歧义
public static SysResult success(Object data){
return new SysResult(200,"服务器调用成功",data);
}
public static SysResult success(String msg,Object data){
return new SysResult(200,msg,data);
}
}
controller层方法
/**
* 需求: 根据u/p查询数据库,返回秘钥token
* URL: /user/login
* 类型: post
* 参数: username/password json
* 返回值: SysResult对象(token)
*/
@PostMapping("/login")
public SysResult login(@RequestBody User user){
String token = userService.findUserByUP(user);
if(token == null || "".equals(token)){
//表示用户名和密码错误
return SysResult.fail();
}
//表示用户名和密码正确,返回秘钥信息
return SysResult.success(token);
}
serviceImpl层方法
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public String findUserByUP(User user) {
//将密码加密
byte[] bytes = user.getPassword().getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(bytes);
user.setPassword(md5Pass);
//根据用户名和秘闻查询数据库
User userDB = userMapper.findUserByUP(user);
// 判断userDB是否有值
if (userDB == null){
return null;
}
//String token= "密钥";//暂为密钥,具体之后进行具体编写
//密钥特点: 唯一性,迷惑性 UUID:可根据时间加上随机数 进行hash计算生成几乎唯一的数 几乎可保证唯一性 例如:40b449d8-28a0-11ec-8433-d8c4973d35a8
String token= UUID.randomUUID().toString().replace("-", "");
return token;
}
}
在userService中编写findUserByUP方法,在mapper包下的UserMapper编写相应的持久层代码。
controller层
@RestController
@CrossOrigin
@RequestMapping("/rights")
public class RightsController {
@Autowired
private RightsService rightsService;
/*
* 左侧菜单获取
请求路径 /rights/getRightsList
请求类型 GET
请求参数 无
响应数据 SysResult(List)对象
* */
@GetMapping("/getRightsList")
public SysResult getRightsList(){
List<Rights> list = rightsService.getRightsList();
return SysResult.success(list);
}
}
mapper,service层省略
RightsMapper.xml
<select id="getRightsList" resultMap="RightsRM">
SELECT a.id,a.name,a.parent_id,a.path,a.`level`,a.created,a.updated,b.id c_id,b.parent_id c_parent_id,b.name c_name,b.created c_id,b.`level` c_level,b.updated c_update,b.path c_path
FROM (SELECT * from rights WHERE parent_id = 0) a LEFT JOIN rights b
on a.id = b.parent_id
</select>
<resultMap id="RightsRM" type="rights" autoMapping="true">
<id property="id" column="id"></id>
<collection property="children" ofType="rights">
<id property="id" column="c_id"></id>
<result property="name" column="c_name"></result>
<result property="path" column="c_path"></result>
<result property="level" column="c_level"></result>
<result property="parentId" column="c_parent_id"></result>
<result property="created" column="c_create"></result>
<result property="updated" column="c_updated"></result>
</collection>
</resultMap>
效果:
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home', component: Home,children:[
/*定义父组件的子组件*/
{path: '/user', component: User}
]}
]
子组件将会展现在父组件定义的内
<!-- 定义主页面结构-->
<el-main>
<!-- 定义路由展现页面-->
<router-view></router-view>
</el-main>
用户列表接口文档
封装vo对象PageResult
@Data
@Accessors(chain = true)
public class PageResult { //定义分页查询对象
private String query; //查询参数
private Integer pageNum; //查询页数
private Integer pageSize; //每页条数
private Long total; //总记录数
private Object rows; //查询结果
}
补充知识: vue.js作用域插槽 获取当前行的全部数据
<template slot-scope="scope">//scope 是一个形参,用以获取当前行的全部数据(包括 隐藏的)
{{scope.row}}
</template>
接口文档
@PutMapping("/status/{id}/{status}")
public SysResult updateStatus(@PathVariable("id") Integer id,@PathVariable("status") Boolean status ){
int i = userService.updatestatus(id,status);
if ( i>=0){
return SysResult.success();
}
return SysResult.fail();
}
注意:status字段在数据库为tinyint类型 在sql输入 false会转化为 0 true会转化为1 。
接口文档
[
注意: 更新时,业务的回显
/* 用户更新,数据回显
根据ID查询用户信息
请求路径: /user/{id}
请求类型: GET
返回值: SysResult对象
* */
@GetMapping("/{id}")
public SysResult findById(@PathVariable("id") Integer id){
User user = userService.findById(id);
return SysResult.success(user);
}/*根据用户ID更新数据
请求路径: /user/updateUser
请求类型: PUT
请求参数: User对象结构*/
@PutMapping("/updateUser")
public SysResult updateUser(@RequestBody User user){
userService.updateUser(user);
return SysResult.success();
}
/* 根据ID删除用户
请求路径: /user/{id}
请求类型: delete
请求参数: id
返回值: SysResult对象*/
@DeleteMapping("/{id}")
public SysResult deleteById(@PathVariable("id")Integer id){
userService.deleteById(id);
return SysResult.success();
}
异常说明:
使用 try/catch 缺点:如果代码中添加大量try-catch,使代码结构混乱,代码复杂度变高,不便维护
@DeleteMapping("/{id}")
public SysResult deleteById(@PathVariable("id")Integer id){
try{
userService.deleteById(id);
return SysResult.success();}catch (Exception e){
return SysResult.fail();
}
}
解决方法 spring 4后 添加了全局异常处理机制
@RestControllerAdvice
//标识该类是全局异常处理机制 返回值都是json串 使用aop的技术,解决特定问题。
//@ControllerAdvice
public class SystemExe {
/*
* 说明: 需要为全局异常定义一个方法
* 要求: 返回统一的业务数据 SysResult
* 拦截 指定遇到某种异常实现aop处理
* 特点: 该异常处理机制,只拦截controller层抛出的异常。
* 注意在我们的业务方法中不要随便使用 try-catch ,他将异常捕获,不再向上层抛出,可能会导致业务执行不成功,却返回正常信息
* */
@ExceptionHandler(RuntimeException.class)
//@ExceptionHandler({RuntimeException.class, SQLException.class})
//当初出现了运行时异常时 进行拦截 执行下面方法 拦截异常可以为多种
public SysResult fail(Exception e){
e.printStackTrace(); //打印异常
return SysResult.fail();
}
}
事务特性
spring默认事务策略(事务控制 未开)
public SysResult deleteById(@PathVariable("id")Integer id){
userService.deleteById(id);
return SysResult.success();
int i = 1/0
//添加了全局异常处理机制,会返回事务删除错误,但数据依然被删除
}
解决方法:
@DeleteMapping("/{id}")
/*
* @Transactional()----一般加载有数据库修改的方法上,也可以加载该mapper接口方法上。一般不要加在查询方法上,会影响查询效率。
* 作用:
* 1.默认条件下,志兰姐运行时异常
* 可以有参数:(一般不加参数)
* rollbackFor : 指定异常的类型回滚 rollbackFor = RuntimeException.class
* noRollbackFor : 指定异常不回滚 noRollbackFor = RuntimeException.class
* */
@Transactional()
public SysResult deleteById(@PathVariable("id")Integer id){
userService.deleteById(id);
return SysResult.success();
}
@RestController
@CrossOrigin
@RequestMapping("/itemCat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* 需求: 查询3级分类数据信息
* 类型: get
* URL: /itemCat/findItemCatList/{level}
* 参数: level
* 返回值: SysResult(list)
*/
@GetMapping("/findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level){
List<ItemCat> list = itemCatService.findItemCatList(level);
return SysResult.success(list);
}
}
编辑ItemCatService(两种写法)
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 弊端: 由于多次循环遍历 查询数据库,导致数据库查询次数太多效率极低.
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
//查询一级商品分类信息
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//查询二级商品分类信息
for(ItemCat oneItemCat: oneList){
//1.复用条件构造器 将之前的数据清空
queryWrapper.clear();
//查询二级数据 parent_id = 一级ID
queryWrapper.eq("parent_id",oneItemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
//遍历二级列表 查询三级数据,封装数据返回
if(teoList.size>0){
for(ItemCat itemCat: twoList){
queryWrapper.clear();
queryWrapper.eq("parent_id",itemCat.getId());
List<ItemCat> threeItemCat = itemCatMapper.selectList(queryWrapper);
twoItemCat.setChildren(threeItemCat);
}
}
oneItemCat.setChildren(twoList);
}
return oneList;
}
}
封装到map内利用<key,value>,key值唯一用以存放 parent_id 然后根据所需要的层级返回封装的值
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 思路:获取所有的数据库记录,之后按照父子级关系进行封装
* 数据结构: Map<k,v>
* Map<parentId,List当前父级的子级信息(不嵌套)>
* 例子: Map<0,List[{id=1,name="xx",children=null}.....]>
*
* 封装数据规则:
* 1.遍历所有的数据.
* 2.获取parentId
* 3.判断parentId是否存在,之后实现数据封装
*/
public Map<Integer,List<ItemCat>> getMap(){
Map<Integer,List<ItemCat>> map = new HashMap<>();
//查询所有的数据库记录
List<ItemCat> list = itemCatMapper.selectList(null);
//1.遍历数据
for(ItemCat itemCat:list){
//获取parentId
int parentId = itemCat.getParentId();
if(map.containsKey(parentId)){ //判断集合中是否有key
//表示数据存在,将自己追加
map.get(parentId).add(itemCat);
}else{
//key不存在, 定义list集合,将自己作为第一个元素追加
List<ItemCat> childrenList = new ArrayList<>();
childrenList.add(itemCat);
//将数据保存到map集合中
map.put(parentId,childrenList);
}
}
return map;
}
//该方法获取1-2级数据信息
public List<ItemCat> getTwoList(Map<Integer,List<ItemCat>> map){
//1.先查询一级菜单数据
List<ItemCat> oneList = map.get(0);
//2.遍历每个一级菜单去封装二级数据
for(ItemCat oneItemCat : oneList){
//parent_id = 一级ID
int parentId = oneItemCat.getId();
//查询二级数据
List<ItemCat> twoList = map.get(parentId);
//将数据进行封装
oneItemCat.setChildren(twoList);
}
//返回一级数据
return oneList;
}
/**
* 实现思路:
* 1. 获取二级分类列表信息
* 2. 遍历一级菜单,获取二级数据
* 3. 根据二级菜单查询三级数据 防止二级数据为null的现象
* 4. 将三级数据封装到二级中
* @param map
* @return
*/
public List<ItemCat> getThreeList(Map<Integer,List<ItemCat>> map){
//1.获取1-2数据信息 包含了2级的children
List<ItemCat> oneList = getTwoList(map);
//2.编辑一级数据,获取二级数据
for(ItemCat oneItemCat : oneList){
List<ItemCat> twoList = oneItemCat.getChildren();
if(twoList == null || twoList.size()==0){
//跳过本地循环,进入下一次循环
continue;
}
//3.遍历二级数据,查询三级信息
for (ItemCat twoItemCat : twoList){
//查询三级 parentId = 二级ID
int parentId = twoItemCat.getId();
List<ItemCat> threeList = map.get(parentId);
//将三级封装到二级中
twoItemCat.setChildren(threeList);
}
}
return oneList;
}
//实现列表数据功能展示
@Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
//获取所有集合数据
Map<Integer,List<ItemCat>> map = getMap();
if(level == 1){
//1.一级商品分类信息
return map.get(0);
}
//获取一级菜单和二级菜单
if(level == 2){
return getTwoList(map);
}
//获取三级菜单数据 1-2-3
List<ItemCat> allList = getThreeList(map);
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
return allList;
}
效果:
修改商品分类状态
controller
/*
*
修改商品分类状态
请求路径: /itemCat/status/{id}/{status}
请求类型: put
请求参数:id status
* */
@PutMapping("/status/{id}/{status}")
public SysResult status(@PathVariable("id") Integer id, @PathVariable("status") Boolean status) {
itemCatService.status(id, status);
return SysResult.success();
}
```
Impl
@Override
@Transactional
public void status(Integer id, Boolean status) {
ItemCat itemCat = new ItemCat();
itemCat.setId(id).setStatus(status).setUpdated(new Date());
itemCatMapper.updateById(itemCat);
}
```
用户新增
@PostMapping("/saveItemCat")
public SysResult saveItemCat(@RequestBody ItemCat itemCat) {
itemCatService.saveItemCat(itemCat);
return SysResult.success();
}
@Override
//事务管理
@Transactional
public void saveItemCat(ItemCat itemCat) {
Date date = new Date();
itemCat.setStatus(true);
itemCat.setCreated(date).setUpdated(date);
itemCatMapper.insert(itemCat);
}
商品分类名修改(和状态修改操作相同,只不过url不同,传入的对象为空的位置不同
商品分类删除
controller
@DeleteMapping("/deleteItemCat")
public SysResult deleteItemCat(ItemCat itemCat){
itemCatService.deleteItemCat(itemCat);
return SysResult.success();
}
impl
*
* 业务: 如果是父级,则应该删除子集和自己
* 思路:
* 1. 判断是否为3级标签, 直接删除
* 2. 判断是否为2级标签, 先删三级,再删二级
* 3. 判断是否为1级标签, 先查询二级,再删除三级二级,再删一级。
* */
@Override
@Transactional
public void deleteItemCat(ItemCat itemCat) {
if(itemCat.getLevel() == 3){
int id = itemCat.getId();
itemCatMapper.deleteById(id);
return;//return 表示程序终止
}
if(itemCat.getLevel() == 2){
int id = itemCat.getId();
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", id);
itemCatMapper.delete(queryWrapper);//删除三级数据
itemCatMapper.deleteById(id);
return;
}
if(itemCat.getLevel() == 1){
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", itemCat.getId());
//obj方法获取主键 由于是删除的业务,所以只需获得id即可
List idlist = itemCatMapper.selectObjs(queryWrapper);
//判断是否有二级数据
if(idlist.size()>0) {
//根据二级id删除三级数据 sql where parent_id in (1,2,3,4)
queryWrapper.clear();//清空上次的数据
queryWrapper.in("parent_id", idlist);
itemCatMapper.delete(queryWrapper);
//最后删除二级和一级
idlist.add(itemCat.getId());
/*
queryWrapper.clear();
queryWrapper.in("id", idlist);
itemCatMapper.delete(queryWrapper);
*/
itemCatMapper.deleteBatchIds(idlist);
}else {
itemCatMapper.deleteById(itemCat.getId());
}
}
}
完成数据自动填充
每次进行插入修改操作都要去相应的修改时间,为了简洁代码,我们可以将其他pojo类中的共有属性提取到一个父pojo类中,通过注解,使其在数据操作时自动更新时间
public class BasePojo implements Serializable{
//新增操作时自动填充
@TableField(fill = FieldFill.INSERT)
private Date created; //表示入库时需要赋值
//新增和修改时自动填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated; //表示入库/更新时赋值.}
为是起生效要配置config类
/**
* @author lxb
* 2021/10/14 9:33
* MP 自动属性自动填充
*/
@Component //将对象交给spring容器管理
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Date date = new Date();
this.setFieldValByName("created", date,metaObject);
this.setFieldValByName("updated", date,metaObject);
//在插入操作时,自动执行 改变属性名 更新数据
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updated", new Date(),metaObject);
}
}
注意: price 是实际价格的100倍,在取数据时进行显示时要缩小100倍,存数据时要扩大100倍(保证精度)
2. 页面分析
* 添加商品按钮 */
async addItemBtn(){
//console.log(this.addItemForm)
//1.完成表单校验
this.$refs.addItemFormRef.validate( valid => {
if(!valid) return this.$message.error("请输入商品必填项")
})
//2.完成商品参数的封装
//2.0 将商品价格扩大100倍
this.addItemForm.price = this.addItemForm.price * 100
//2.1 将商品图片的数据转化为字符串
this.addItemForm.images = this.addItemForm.images.join(",")
//2.5 实现商品数据提交
let submitAddItem = {
item : this.addItemForm,
itemDesc: this.itemDesc
}
console.log(submitAddItem)
let {data: result} = await this.$http.post("/item/saveItem",submitAddItem)
if(result.status !== 200) return this.$message.error("商品添加失败")
this.$message.success("商品添加成功")
//2.5添加完成之后,将数据重定向到商品展现页面
this.$router.push("/item")
}
//controller类
@Autowired
private ItemService itemService;
/*
* 业务: 实现商品列表分页展现
* 请求路径: /item/getItemList?query=&pageNum=1&pageSize=10
* 请求类型: get
* 请求参数: 使用pageResult对象接收
* 返回值结果: Sysresult(pageResult)
* */
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);
}
/*
* selectPage 语法
* 1.page MP内部指定分页对象
* 2.querywrapper 条件构造器
* sql: 。。。 where title= "%"#{title}"%"
* */
@Override
public PageResult getItemList(PageResult pageResult) {
//判断是否有值
Boolean flag = StringUtils.hasLength(pageResult.getQuery());
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.like(flag,"title", pageResult.getQuery());
//Ipage 接口 Page 类 向上转型
IPage<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
page = itemMapper.selectPage(page, queryWrapper);
long total = page.getTotal();
List<Item> rows = page.getRecords();
return pageResult.setTotal(total).setRows(rows);
}
注意使用selectPage方法要定义MP配置
@Configuration
//表示此类是一个配置类
public class MybatisPlusConfig {
/*
* @Bean 将方法的返回值交给spring容器管理
* */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));//也可以写MYSQL
return interceptor;
}
}
/* 添加商品按钮 */
async addItemBtn(){
//console.log(this.addItemForm)
//1.完成表单校验
this.$refs.addItemFormRef.validate( valid => {
if(!valid) return this.$message.error("请输入商品必填项")
})
//2.完成商品参数的封装
//2.0 将商品价格扩大100倍
this.addItemForm.price = this.addItemForm.price * 100
//2.1 将商品图片的数据转化为字符串
this.addItemForm.images = this.addItemForm.images.join(",")
//2.5 实现商品数据提交
let submitAddItem = {
item : this.addItemForm,
itemDesc: this.itemDesc
}
console.log(submitAddItem)
let {data: result} = await this.$http.post("/item/saveItem",submitAddItem)
if(result.status !== 200) return this.$message.error("商品添加失败")
this.$message.success("商品添加成功")
//2.5添加完成之后,将数据重定向到商品展现页面
this.$router.push("/item")
}
{
item: {
images: "/2021/05/20/da0c1d4781c1499399f090da8b60f359.jpg,/2021/05/20/2ac1c34776a7465887eb019655354c3c.jpg"
itemCatId: 560
num: "100"
price: 718800
sellPoint: "【华为官方直供,至高12期免息0首付,原装正品】送华为原装无线充+运动蓝牙耳机+蓝牙音箱+三合一多功能数据线+钢化膜等!"
title: "华为P40 Pro 5G手机【12期免息可选送豪礼】全网通智能手机"
},
itemDesc: {
itemDesc: "<ul><li>品牌: <a href=https://list.jd.com/list.html"....... "
}
}
封装IteemVo类
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO { //该对象封装商品所有的参数信息
private Item item;
private ItemDesc itemDesc;
}
编辑controller
/*
*
商品分类新增
请求路径: /itemCat/saveItemCat
请求类型: post
请求参数: 表单数据
ItemCat同时封装了 item 和item_desc 属性
* */
@PostMapping("/saveItemCat")
public SysResult saveItemCat(@RequestBody ItemCat itemCat) {
itemCatService.saveItemCat(itemCat);
return SysResult.success();
}
4.1 Impl
//由于数据统一提交 所以要确保,并且item.id = itemDesc.id 数据要对应
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
item.setStatus(true);
//要求item动态入库后,动态返回id
//MP原则:入库后动态回显数据
itemMapper.insert(item);
//实现desc入库
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
1.在所需位置添加标签
<!-- 定义富文本编辑器-->
<quill-editor ref="myQuillEditor" v-model="itemDesc.itemDesc">
</quill-editor>
2.配置组件引用
/* 导入富文本编辑器 */
import VueQuillEditor from 'vue-quill-editor'
/* 导入富文本编辑器对应的样式 */
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
3.在vue-ui中导入 vue-quill-editor依赖
实现商品详情入库5
itemDesc说明
tem表是商品的基本信息,itemDesc表是商品的详情信息. 用户一般查询时都是查询的基本信息.只有点击某个商品时才会查询详情信息.为了提高查询效率 将商品分为 item/itemDesc
逻辑关系
\1. 一个商品对应一个详情
\2. 一个详情对应一个商品
数据库表示
item.id = itemDesc.id
商品详情参数传递
用户点击添加商品时,会将item/itemDesc的对象进行传递.则在后端动态接收数据则可以获取2个对象数据.
编辑ItemDesc POJO对象
@Data
@Accessors(chain = true)
@TableName("item_desc")
public class ItemDesc extends BasePojo{
//由于item.id=itemDesc.id 所以ID不能主键自增
@TableId
private Integer id;
private String itemDesc;
}
编辑对应mapper接口
public interface ItemDescMapper extends BaseMapper<ItemDesc> {}
编辑对应实现类
当用户点击入库时,应该将item/itemDesc一起入库操作.
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem(); //id=null
item.setStatus(true);
//要求item入库之后,动态返回Id!!!!
//MP原则: 入库之后动态回显数据!!
itemMapper.insert(item);
//实现itemDesc对象入库
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
其他商品模块的增删改同上
实现文件上传
文件上传入门案例
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务