创建一个空的maven工程项目,将pom.xml文件中的信息更改为如下信息,以此创建webapp开发环境。
<?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>org.sinian</groupId>
<artifactId>reggie</artifactId>
<version>1.0-SNAPSHOT</version>
<!--maven配置 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<parent>
<!--继承 Spring Boot 管理的版本号-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<!--引入 Spring Boot 依赖坐标-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<!-- 简化javaBean的书写-->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--json与实体对象之间的转化-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
<build>
<!-- 打Jar包时会引入依赖包-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
创建引导类(启动类),实现webApp启动功能。置于src/main/java/org/sinian/reggie
package org.sinian.reggie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class reggieApplication {
public static void main(String[] args) {
SpringApplication.run(reggieApplication.class,args);
log.info("项目启动成功");
}
}
构建webapp配置文件application.yml,该配置文件置于resources目录下。
server:
port: 8080
spring:
application:
#应用的名称,可选
name: reggie
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
mybatis-plus:
configuration:
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: ASSIGN_ID
使用代码生成器根据数据库表生成webapp代码框架。注意生成Dao类中,加上@Mapper
package org.sinian.reggie.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
public class CodeGenerator {
public static void main(String[] args) {
//1.获取代码生成器的对象
AutoGenerator autoGenerator = new AutoGenerator();
//设置数据库相关配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/database?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123456");
autoGenerator.setDataSource(dataSource);
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/模块名/src/main/java");
globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("sinian"); //设置作者
globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("org.sinan.reggie");//设置生成的包名,与之前路径叠加组成完整路径
packageInfo.setEntity("domain"); //设置实体类包名
packageInfo.setMapper("dao"); //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user","tbl_manage");//设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_");//设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted");//设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true);//设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);
//2.执行生成操作
autoGenerator.execute();
}
}
将前端代码置于resources,默认情况下,浏览器访问tomcat服务器,默认在resources/static或者resources/templates目录下寻找静态文件。因此将前端代码拷贝到resources目录下之后,需要使用资源处理器,进行静态文件路径映射。创建config包,使用WebMvcConfig类继承WebMvcConfigurationSupport,通过重写方式,添加webmvc资源处理器。
package org.sinian.reggie.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始静态资源映射...");
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
}
前后端数据交换协议,通过封装一个通用实体类达成。
package org.sinian.reggie.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Employee;
import org.sinian.reggie.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
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;
import javax.servlet.http.HttpServletRequest;
/**
* 员工信息 前端控制器
* @author sinian
* @since 2023-01-25
*/
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
//将页面提交的密码,进行md5加密处理。
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//根据页面提交的用户名username查询数据库。
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
//没有查询到则返回失败结果。
if(emp==null) {
return R.error("查无此人!");
}
//密码比对不一致,返回登录失败结果。
if(!emp.getPassword().equals(password)){
return R.error("密码错误");
}
//查看账号状态是否可用。
if(emp.getStatus()==0){
return R.error("账号冻结");
}
//登录成功,将员工id存入Session并返回结果。
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功!");
}
}
基于过滤器实现请先登录功能
package org.sinian.reggie.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.sinian.reggie.common.R;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
public class LoginCheckfilter implements Filter {
//AntPathMatcher可以实现带通配符比较
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
log.info("拦截到请求:{}",httpServletRequest.getRequestURI());
//获取本次请求
String requestUrl = httpServletRequest.getRequestURI();
//定义不需要过滤的请求
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//判断本次请求需不需要处理
boolean match = false;
for (String url:urls){
if(PATH_MATCHER.match(url, requestUrl)){
log.info("{}:匹配成功!",requestUrl);
match=true;
}
}
if(match){
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else if(httpServletRequest.getSession().getAttribute("employee")!=null){
//确保用户保持登录状态
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else{
log.info("{}:匹配失败!",requestUrl);
//结合前端代码,实现登录页面重定向
httpServletResponse.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
}
}
}
使用注解@ServletComponentScan扫描@WebFilter(filterName = “LoginCheckFilter”,urlPatterns = “/*”)
package org.sinian.reggie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@Slf4j
@SpringBootApplication
@ServletComponentScan
public class reggieApplication {
public static void main(String[] args) {
SpringApplication.run(reggieApplication.class,args);
log.info("项目启动成功");
}
}
通过断点debug,可以知道从前端获取的数据,此外,通过会话拿到当前登录用户的id,其余字段可通过set进行初始化。
package org.sinian.reggie.controller;
import lombok.extern.slf4j.Slf4j;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Employee;
import org.sinian.reggie.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
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;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
*
* 新增员工信息
*
*
* @author sinian
* @since 2023-01-25
*/
@RestController
@RequestMapping("/employee")
@Slf4j
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
log.info("新增员工,员工信息:{}",employee.toString());
//设置初始密码123456,需要进行md5加密处理
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
employee.setCreate_time(LocalDateTime.now());
employee.setUpdate_time(LocalDateTime.now());
//获得当前登录用户的id
Long empId = (Long) request.getSession().getAttribute("employee");
employee.setCreate_user(empId);
employee.setUpdate_user(empId);
employeeService.save(employee);
return R.success("新增员工成功");
}
}
由于Controller可能接收到来自业务层、数据层、数据库抛出的异常,因此需要使用AOP思想,进行全局异常处理,异常可通过调试获得。
package org.sinian.reggie.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
@ControllerAdvice(annotations = {RestController.class, Controller.class})//表示这是一个aop通知类,做功能增强。
@ResponseBody//类中方法返回值将以JSON格式返回给前端。
@Slf4j//日志记录
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
//SQLIntegrityConstraintViolationException,该异常可通过测试,出现在调试信息中。
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)//表明该方法为一个异常处理器。
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("未知错误");
}
}
为了使用Mybatis-plus提供的分页接口,由于mybatis-plus基于AOP思想仅仅只是对mybatis功能进行增强,因此需要使用分页拦截器,拦截所有查询请求,并使用代理的方式,使用Mybatis-plus提供的接口完成自动分页功能。(mybatis-plus将会注入MybatisPlusInterceptor类型对象,如果没有添加任何拦截器,将不会拦截任何请求),这也是没有添加拦截器,使用mybatis-plus提供的分页接口实现分页失败的原因。
package org.sinian.reggie.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
实现reggie外卖分页功能
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Employee;
import org.sinian.reggie.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
* 分页查询
*
*
* @author sinian
* @since 2023-01-25
*/
@RestController
@RequestMapping("/employee")
@Slf4j
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name){
log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
//构造分页构造器,使用mybatisplus提供的实体。
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件,使用带bool类型的lambda表达式。
queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Employee::getUpdate_time);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
}
扩展:
queryWrapper.lt()——小于
queryWrapper.le()——小于等于
queryWrapper.gt()——大于
queryWrapper.ge()——大于等于
queryWrapper.eq()——等于
queryWrapper.ne()——不等于
queryWrapper.betweeen(“age”,10,20)——age在值10到20之间
queryWrapper.notBetweeen(“age”,10,20)——age不在值10到20之间
queryWrapper.like(“属性”,“值”)——模糊查询匹配值‘%值%’
queryWrapper.notLike(“属性”,“值”)——模糊查询不匹配值‘%值%’
queryWrapper.likeLeft(“属性”,“值”)——模糊查询匹配最后一位值‘%值’
queryWrapper.likeRight(“属性”,“值”)——模糊查询匹配第一位值‘值%’
queryWrapper.isNull()——值为空或null
queryWrapper.isNotNull()——值不为空或null
queryWrapper.in(“属性”,条件,条件 )——符合多个条件的值
queryWrapper.notIn(“属性”,条件,条件 )——不符合多个条件的值
queryWrapper.or()——或者
queryWrapper.and()——和
queryWrapper.orderByAsc(“属性”)——根据属性升序排序
queryWrapper.orderByDesc(“属性”)——根据属性降序排序
queryWrapper.inSql(“sql语句”)——符合sql语句的值
queryWrapper.notSql(“sql语句”)——不符合SQL语句的值
queryWrapper.esists(“SQL语句”)——查询符合SQL语句的值
queryWrapper.notEsists(“SQL语句”)——查询不符合SQL语句的值
通过前端页面,点击禁用按钮,查看请求及其负载,编写Controller,实现根据传送而来的id进行更新操作。由于默认转化器只能处理前端JSON对象映射为后端java对象,无法处理后端java对象映射为前端JSON对象,因此会出现前端js代码,会对来自后端的Long型数据做四舍五入,因此需要在Spring MVC框架中扩展转化器,或者添加转化器。
package org.sinian.reggie.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
扩展Spring MVC转化器
package org.sinian.reggie.config;
import lombok.extern.slf4j.Slf4j;
import org.sinian.reggie.common.JacksonObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始静态资源映射...");
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("自定义扩展消息转换器初始化...");
//创建一个消息转换器
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(new JacksonObjectMapper());
converters.add(0,messageConverter);
log.info("自定义扩展消息转换器初始化成功!");
}
}
通过前端页面,点击禁用按钮,查看请求及其负载,编写Controller,实现根据传送而来的id进行更新操作。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Employee;
import org.sinian.reggie.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
*
* 更新操作,实现员工启用和禁用功能
*
*
* @author sinian
* @since 2023-01-25
*/
@RestController
@RequestMapping("/employee")
@Slf4j
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
//设置数据更改者,从请求会话中,获取当前用户id,并设置employee数据对象中update_user。
Long current_user_id = (Long) request.getSession().getAttribute("employee");
//设置更新时间。
employee.setUpdate_user(current_user_id);
employee.setUpdate_time(LocalDateTime.now());
//执行更新操作。
employeeService.updateById(employee);
//返回执行成功。
return R.success("修改成功!");
}
}
Spring MVC只负责资源定位,对于参数的解析只能通过自己解析,后端Controller,通过@RequestBody、@PathVariable、@RequestParam进行解析参数,前端通过如下代码进行解析。
function requestUrlParam(argname){
var url = location.href
var arrStr = url.substring(url.indexOf("?")+1).split("&")
for(var i =0;i<arrStr.length;i++)
{
var loc = arrStr[i].indexOf(argname+"=")
if(loc!=-1){
return arrStr[i].replace(argname+"=","").replace("?","")
}
}
return ""
}
数据库字段表向实体对象映射,开启驼峰映射,mybatis-plus将会把数据库表字段全部映射成为驼峰式命名。
mybatis-plus:
configuration:
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: false
由于部分字段名不匹配,Response JSON数组不能很好映射给前端JSON数组,因此方法一:更改前端数据模型中JSON数组数据字段名及其页面涉及的该字段名。方法二:更改后端数据对象实体字段名。总结:一般数据库中字段名使用带下划线的方式进行命名,前端以及后端基本使用驼峰命名的方式。
点击编辑之后,由浏览器发送请求访问静态资源编辑页面html,编辑页面需要可通过解析location.href,解析出请求中的参数id,处理方式入上一点所讲述,进而发送ajax请求,根据id查询需要编辑的用户。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Employee;
import org.sinian.reggie.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
*
* 员工信息 前端控制器
*
*
* @author sinian
* @since 2023-01-25
*/
@RestController
@RequestMapping("/employee")
@Slf4j
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
/**
* 根据员工id查询信息
* @param id
* @return
*/
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable("id") Long id){
log.info("根据id查询员工信息");
Employee emp = employeeService.getById(id);
log.info("查询到的员工信息:{}",emp.toString());
if(emp!=null)
return R.success(emp);
else
return R.error("没有查询到员工信息");
}
}
编辑之后进一步保存,发送ajax请求与实现员工启用和禁用功能请求相同
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
//设置数据更改者,从请求会话中,获取当前用户id,并设置employee数据对象中update_user。
Long current_user_id = (Long) request.getSession().getAttribute("employee");
//设置更新时间。
employee.setUpdate_user(current_user_id);
employee.setUpdate_time(LocalDateTime.now());
//执行更新操作。
employeeService.updateById(employee);
//返回执行成功。
return R.success("修改成功!");
}
1、在实体类的属性上加上@TableFiled注解,指定自动填充的策略。
2、由于当前用户id只能通过HttpServletRequest获得,无法传递给元数据对象处理器,因此使用TreadLocal进行当前用户id的传递。
3、编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHander接口。最后交由Spring管理,该类会被Mybatis-plus自动识别装配。
package org.sinian.reggie.domain;
import com.baomidou.mybatisplus.annotation.*;
import java.time.LocalDateTime;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
* 员工信息
*
*
* @author sinian
* @since 2023-01-25
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 手机号
*/
private String phone;
/**
* 性别
*/
private String sex;
/**
* 身份证号
*/
@JsonAlias("idNumber")
private String id_number;
/**
* 状态 0:禁用,1:正常
*/
private Integer status;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime create_time;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime update_time;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private Long create_user;
/**
* 修改人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long update_user;
}
1、编写BaseContext工具类,基于TreadLocal封装的工具类。
2、在loginCkerFilter的已登录代码模块中调用BaseContext来设置当前登录id。
3、在MyMetaObjectHandler的方法中调用BaseContext来获取登录。
package org.sinian.reggie.common;
/**
* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
/**
* 设置值
* @param id
*/
public static void setCurrentId(Long id){
threadLocal.set(id);
}
/**
* 获取值
* @return
*/
public static Long getCurrentId(){
return threadLocal.get();
}
}
2、在loginCkerFilter的已登录代码模块中调用BaseContext来设置当前登录id。
package org.sinian.reggie.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.sinian.reggie.common.BaseContext;
import org.sinian.reggie.common.R;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
public class LoginCheckfilter implements Filter {
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
log.info("拦截到请求:{}",httpServletRequest.getRequestURI());
//获取本次请求
String requestUrl = httpServletRequest.getRequestURI();
//定义不需要过滤的请求
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//判断本次请求需不需要处理
boolean match = false;
for (String url:urls){
if(PATH_MATCHER.match(url, requestUrl)){
log.info("{}:匹配成功!",requestUrl);
match=true;
}
}
if(match){
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else if(httpServletRequest.getSession().getAttribute("employee")!=null){
long empId = (long) httpServletRequest.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);//使用本次线程来保存当前用户id;
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else{
log.info("{}:匹配失败!",requestUrl);
httpServletResponse.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
}
}
}
3、在MyMetaObjectHandler的方法中调用BaseContext来获取登录。
package org.sinian.reggie.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
metaObject.setValue("create_time", LocalDateTime.now());
metaObject.setValue("update_time",LocalDateTime.now());
metaObject.setValue("create_user",BaseContext.getCurrentId());
metaObject.setValue("update_user",BaseContext.getCurrentId());
}
@Override
public void updateFill(MetaObject metaObject) {
metaObject.setValue("update_time",LocalDateTime.now());
metaObject.setValue("update_user",BaseContext.getCurrentId());
}
}
通过代码生成器生成MVC代码框架,数据层记得加@Mapper,由@RequestBody映射数据到java实体对象,其中字段名不匹配时,在实体类中使用@JsonAlias(“别名”),起别名解决,以下代码实现菜品分类。
package org.sinian.reggie.controller;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.service.ICategoryService;
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 sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private ICategoryService categoryService;
@PostMapping
public R<String> save(@RequestBody Category category){
categoryService.save(category);
return R.success("新增分类成功!");
}
}
与之前分页查询一直,只不过不用重新构造查询拦截器
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.service.ICategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
* 菜品及套餐分类 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private ICategoryService categoryService;
@GetMapping("/page")
public R<Page> page( int page, int pageSize){
//构造分页实体对象
Page<Category> pageInfo = new Page<>(page, pageSize);
//条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByAsc(Category::getSort);
Page<Category> respage = categoryService.page(pageInfo, queryWrapper);
return R.success(respage);
}
}
删除主要注意菜品分类是否关联由菜品和套餐,因此可以在CategoryService接口中扩展remove方法,并在实现类中实现。
package org.sinian.reggie.service;
import org.sinian.reggie.domain.Category;
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
* 菜品及套餐分类 服务类
*
*
* @author sinian
* @since 2023-01-27
*/
public interface ICategoryService extends IService<Category> {
public void remove(Long id);
}
package org.sinian.reggie.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.dao.CategoryDao;
import org.sinian.reggie.domain.Dish;
import org.sinian.reggie.domain.Setmeal;
import org.sinian.reggie.exception.DeleteErrorException;
import org.sinian.reggie.service.ICategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.sinian.reggie.service.IDishService;
import org.sinian.reggie.service.ISetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* 菜品及套餐分类 服务实现类
*
*
* @author sinian
* @since 2023-01-27
*/
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, Category> implements ICategoryService {
@Autowired
private IDishService dishService;
@Autowired
private ISetmealService setmealService;
@Override
public void remove(Long id) {
//查询是否关联了菜品,若是,抛出异常。
//查询是否关联了套餐,若是,抛出异常。
//正常操作
LambdaQueryWrapper<Dish> queryWrapper1 = new LambdaQueryWrapper<>();
LambdaQueryWrapper<Setmeal> queryWrapper2 = new LambdaQueryWrapper<>();
queryWrapper1.eq(Dish::getCategory_id,id);
queryWrapper2.eq(Setmeal::getCategory_id,id);
int count1 = dishService.count(queryWrapper1);
int count2 = setmealService.count(queryWrapper2);
if(count1>0||count2>0){
throw new DeleteErrorException("该菜品分类有所关联,禁止删除!");//自定义异常
}
else {
super.removeById(id);
}
}
}
自定义异常
package org.sinian.reggie.exception;
public class DeleteErrorException extends RuntimeException{
public DeleteErrorException(String Message){
super(Message);
}
}
全局异常异常(DeleteErrorException)处理
package org.sinian.reggie.common;
import lombok.extern.slf4j.Slf4j;
import org.sinian.reggie.exception.DeleteErrorException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
@ControllerAdvice(annotations = {RestController.class, Controller.class})//表示这是一个aop通知类,做功能增强。
@ResponseBody//类中方法返回值将以JSON格式返回给前端。
@Slf4j//日志记录
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
@ExceptionHandler(DeleteErrorException.class)
public R<String> deleteErrorExceptionHandler(DeleteErrorException ex){
log.error(ex.getMessage());
return R.error("DeleteErrorException");
}
}
实现菜品分类删除操作
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.service.ICategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
* 菜品及套餐分类 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private ICategoryService categoryService;
@DeleteMapping
public R<String> delete(Long ids){
categoryService.remove(ids);
return R.success("删除数据成功!");
}
}
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.service.ICategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
* 菜品及套餐分类 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private ICategoryService categoryService;
@PutMapping
public R<String> update(@RequestBody Category category){
categoryService.updateById(category);
return R.success("修改成功!");
}
}
文件的上传统一用MultipartFile类对象进行接收,值得注意的是文件名重新构造,和转存操作。
文件的下载值得注意的是,文件输入流对象和response输出流对象配合使用,其中需要bytes寄存数组作为媒介进行数据结构的转化。
package org.sinian.reggie.controller;
import org.sinian.reggie.common.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
@RestController
@RequestMapping("/common")
public class FileUploadDownloadController {
@Value("${basePath.uploadPath}")
private String uploadPath;
@PostMapping("/upload")
public R<String> upload(MultipartFile file) throws IOException {
if(file.isEmpty()){
return R.error("上床图片为空!");
}
else {
//将上传的图片重命名
//使用uuid生成新的图片名称
String newName = UUID.randomUUID().toString();
//获取原始图像后缀
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
//拼接图片名称后缀
String newNamewithsuffix = newName + suffix;
//判断文件夹是否存在
File dir = new File(uploadPath);
if(!dir.exists()){
dir.mkdir();
}
//转存图片
file.transferTo(new File(uploadPath+"/"+newNamewithsuffix));
return R.success(newNamewithsuffix);
}
}
@GetMapping("/download")
private void download(String name, HttpServletResponse response) throws IOException {
response.setContentType("/image/jepg");
FileInputStream fileInputStream = new FileInputStream(uploadPath + "/" + name);
ServletOutputStream outputStream = response.getOutputStream();
int readLen = 0;
byte[] bytes = new byte[1024];
while((readLen=fileInputStream.read(bytes))!=-1){
outputStream.write(bytes,0,readLen);
outputStream.flush();
}
fileInputStream.close();
outputStream.close();
}
}
该分页菜品展示数据来自两张数据表,因此需要扩展数据传输对象,还要扩展pageInfo。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.domain.Dish;
import org.sinian.reggie.dto.DishDto;
import org.sinian.reggie.service.ICategoryService;
import org.sinian.reggie.service.IDishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* 菜品管理 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private IDishService dishService;
@Autowired
private ICategoryService categoryService;
@RequestMapping("/page")
public R<Page> getpage(int page, int pageSize, String name){
//创建一个页面对象,records为List数据类型
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件,使用带bool类型的lambda表达式。
queryWrapper.like(StringUtils.isNotEmpty(name),Dish::getName,name);
//添加排序条件
queryWrapper.orderByAsc(Dish::getSort);
//执行查询
dishService.page(pageInfo,queryWrapper);
//创建一个页面对象,records为List数据类型
Page<DishDto> pageInfoDto = new Page<>();
//将pageInfo中的数据拷贝到pageInfoDto中,除了不一样的records
BeanUtils.copyProperties(pageInfo,pageInfoDto,"records");
//将pageInfo中的records加上categoryName,形成新的records赋值给pageInfoDto的records
List<Dish> records = pageInfo.getRecords();
List<DishDto> collect = records.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
Long category_id = item.getCategory_id();
//根据item中的category_id在category表中查询name
LambdaQueryWrapper<Category> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.eq(category_id != null, Category::getId, category_id);
Category one = categoryService.getOne(queryWrapper1);
String categoryName = one.getName();
dishDto.setCategoryName(categoryName);
return dishDto;
}).collect(Collectors.toList());
pageInfoDto.setRecords(collect);
return R.success(pageInfoDto);
}
}
此处新增为查询分页功能的逆过程,关键点在于数据传输对象扩展,拆分,数据整合。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.domain.Dish;
import org.sinian.reggie.domain.Dish_flavor;
import org.sinian.reggie.dto.DishDto;
import org.sinian.reggie.service.ICategoryService;
import org.sinian.reggie.service.IDishService;
import org.sinian.reggie.service.IDish_flavorService;
import org.springframework.beans.BeanUtils;
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;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* 菜品管理 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private IDishService dishService;
@Autowired
private IDish_flavorService dish_flavorService;
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
//先保存dishDto传输对象基线数据
dishService.save(dishDto);
Long id = dishDto.getId();
//剥离非基线数据
List<Dish_flavor> flavors = dishDto.getFlavors();
//构建flavor传输对象
List<Dish_flavor> collect = flavors.stream().map((item) -> {
Dish_flavor dish_flavor = new Dish_flavor();
BeanUtils.copyProperties(item, dish_flavor);
dish_flavor.setDish_id(id);
return dish_flavor;
}).collect(Collectors.toList());
dish_flavorService.saveBatch(collect);
return R.success("新增菜品成功!");
}
}
同上面相同原理,关键点在于数据传输对象的构造。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.domain.Dish;
import org.sinian.reggie.domain.Dish_flavor;
import org.sinian.reggie.dto.DishDto;
import org.sinian.reggie.service.ICategoryService;
import org.sinian.reggie.service.IDishService;
import org.sinian.reggie.service.IDish_flavorService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* 菜品管理 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private IDishService dishService;
@Autowired
private ICategoryService categoryService;
@Autowired
private IDish_flavorService dish_flavorService;
@GetMapping("/{id}")
public R<DishDto> getById(@PathVariable Long id){
//完成基线数据的查询
Dish dish = dishService.getById(id);
Long category_id = dish.getCategory_id();
LambdaQueryWrapper<Category> categoryquery = new LambdaQueryWrapper<>();
categoryquery.eq(category_id!=null,Category::getId,category_id);
Category one = categoryService.getOne(categoryquery);
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(dish,dishDto);
Long dishId = dish.getId();
LambdaQueryWrapper<Dish_flavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dishId!=null,Dish_flavor::getDish_id,dishId);
List<Dish_flavor> list = dish_flavorService.list(queryWrapper);
dishDto.setFlavors(list);
dishDto.setCategoryName(one.getName());
return R.success(dishDto);
}
}
修改,删除,启用,停用,下拉框list,代码如下。
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Category;
import org.sinian.reggie.domain.Dish;
import org.sinian.reggie.domain.Dish_flavor;
import org.sinian.reggie.dto.DishDto;
import org.sinian.reggie.service.ICategoryService;
import org.sinian.reggie.service.IDishService;
import org.sinian.reggie.service.IDish_flavorService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* 菜品管理 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {
@Autowired
private IDishService dishService;
@Autowired
private ICategoryService categoryService;
@Autowired
private IDish_flavorService dish_flavorService;
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
//先修改dishDto传输对象基线数据
dishService.updateById(dishDto);
//分离非基线数据保存
List<Dish_flavor> flavors = dishDto.getFlavors();
dish_flavorService.updateBatchById(flavors);
log.info("修改菜品成功");
return R.success("修改菜品成功!");
}
@PostMapping("/status/0")
public R<String> stop(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Dish::getId,collect);
updateWrapper.set(Dish::getStatus,0);
dishService.update(updateWrapper);
return R.success("停售成功!");
}
@PostMapping("/status/1")
public R<String> start(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Dish::getId,collect);
updateWrapper.set(Dish::getStatus,1);
dishService.update(updateWrapper);
return R.success("启售成功!");
}
@DeleteMapping
public R<String> delete(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Dish::getId,collect);
updateWrapper.set(Dish::getIs_deleted,1);
dishService.update(updateWrapper);
return R.success("删除成功");
}
@GetMapping("/list")//下拉框分类list
public R<List<Category>> getByCategoryId(Long categoryId){
//构造条件表达器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(categoryId != null,Category::getId,categoryId);
queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdate_time);
List<Category> list = categoryService.list(queryWrapper);
return R.success(list);
}
}
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.*;
import org.sinian.reggie.dto.DishDto;
import org.sinian.reggie.dto.SetmealDto;
import org.sinian.reggie.service.ICategoryService;
import org.sinian.reggie.service.ISetmealService;
import org.sinian.reggie.service.ISetmeal_dishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* 套餐 前端控制器
*
*
* @author sinian
* @since 2023-01-27
*/
@RestController
@RequestMapping("/setmeal")
@Slf4j
public class SetmealController {
@Autowired
private ISetmealService setmealService;
@Autowired
private ICategoryService categoryService;
@Autowired
private ISetmeal_dishService setmeal_dishService;
@RequestMapping("/page")
public R<Page> getpage(int page, int pageSize, String name){
//创建一个页面对象,records为List数据类型
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件,使用带bool类型的lambda表达式。
queryWrapper.like(StringUtils.isNotEmpty(name),Setmeal::getName,name);
//添加排序条件
queryWrapper.orderByAsc(Setmeal::getUpdate_time);
//判断is_delete
queryWrapper.eq(Setmeal::getIs_deleted,0);
//执行查询
setmealService.page(pageInfo,queryWrapper);
//创建一个页面对象,records为List数据类型
Page<SetmealDto> pageInfoDto = new Page<>();
//将pageInfo中的数据拷贝到pageInfoDto中,除了不一样的records
BeanUtils.copyProperties(pageInfo,pageInfoDto,"records");
//将pageInfo中的records加上categoryName,形成新的records赋值给pageInfoDto的records
List<Setmeal> records = pageInfo.getRecords();
List<SetmealDto> collect = records.stream().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(item, setmealDto);
Long category_id = item.getCategory_id();
//根据item中的category_id在category表中查询name
LambdaQueryWrapper<Category> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.eq(category_id != null, Category::getId, category_id);
Category one = categoryService.getOne(queryWrapper1);
String categoryName = one.getName();
setmealDto.setCategoryName(categoryName);
Long id = item.getId();
LambdaQueryWrapper<Setmeal_dish> queryWrapper2 = new LambdaQueryWrapper<>();
queryWrapper2.eq(id!=null,Setmeal_dish::getSetmeal_id,id);
List<Setmeal_dish> list = setmeal_dishService.list();
setmealDto.setSetmealDishes(list);
return setmealDto;
}).collect(Collectors.toList());
pageInfoDto.setRecords(collect);
return R.success(pageInfoDto);
}
@PostMapping
public R<String> save(@RequestBody SetmealDto setmealDto){
//先保存SetmealDto传输对象基线数据
setmealService.save(setmealDto);
String id = String.valueOf(setmealDto.getId());
//剥离非基线数据
List<Setmeal_dish> setmealDishes = setmealDto.getSetmealDishes();
List<Setmeal_dish> collect = setmealDishes.stream().map((item) -> {
Setmeal_dish setmeal_dish = new Setmeal_dish();
BeanUtils.copyProperties(item, setmeal_dish);
setmeal_dish.setSetmeal_id(id);
return setmeal_dish;
}).collect(Collectors.toList());
setmeal_dishService.saveBatch(collect);
return R.success("新增菜品成功!");
}
@PutMapping
public R<String> update(@RequestBody SetmealDto setmealDto){
//保存基线数据
setmealService.updateById(setmealDto);
//分离非基线数据保存
List<Setmeal_dish> setmealDishes = setmealDto.getSetmealDishes();
setmeal_dishService.updateBatchById(setmealDishes);
log.info("修改菜品成功");
return R.success("修改菜品成功!");
}
@PostMapping("/status/0")
public R<String> stop(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Setmeal> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Setmeal::getId,collect);
updateWrapper.set(Setmeal::getStatus,0);
setmealService.update(updateWrapper);
return R.success("停售成功!");
}
@PostMapping("/status/1")
public R<String> start(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Setmeal> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Setmeal::getId,collect);
updateWrapper.set(Setmeal::getStatus,1);
setmealService.update(updateWrapper);
return R.success("启售成功!");
}
@DeleteMapping
public R<String> delete(String ids){
String[] split = ids.split(",");
List<Long> collect = Arrays.stream(split).map((item) -> {
Long id = Long.valueOf(item);
return id;
}).collect(Collectors.toList());
LambdaUpdateWrapper<Setmeal> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids!=null,Setmeal::getId,collect);
updateWrapper.set(Setmeal::getIs_deleted,1);
setmealService.update(updateWrapper);
return R.success("删除成功");
}
@GetMapping("/{id}")
public R<SetmealDto> getById(@PathVariable Long id){
//完成基线数据的查询
Setmeal setmeal = setmealService.getById(id);
Long setmealId = setmeal.getId();
LambdaQueryWrapper<Setmeal_dish> setmeal_dishQuery = new LambdaQueryWrapper<>();
setmeal_dishQuery.eq(setmealId!=null,Setmeal_dish::getSetmeal_id,setmealId);
List<Setmeal_dish> setmealList = setmeal_dishService.list(setmeal_dishQuery);
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(setmeal,setmealDto);
setmealDto.setSetmealDishes(setmealList);
return R.success(setmealDto);
}
}
package org.sinian.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.sinian.reggie.common.R;
import org.sinian.reggie.domain.Orders;
import org.sinian.reggie.service.IOrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* 订单表 前端控制器
*
*
* @author sinian
* @since 2023-01-28
*/
@RestController
@RequestMapping("/order")
public class OrdersController {
@Autowired
private IOrdersService ordersService;
@RequestMapping("/page")
public R<Page> getpage(int page, int pageSize, String number, String beginTime,String endTime){
//创建一个页面对象,records为List数据类型
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件,使用带bool类型的lambda表达式。
queryWrapper.eq(number!=null,Orders::getNumber,number);
queryWrapper.ge(beginTime!=null,Orders::getOrder_time,beginTime);
queryWrapper.le(endTime!=null,Orders::getOrder_time,endTime);
//添加排序条件
queryWrapper.orderByAsc(Orders::getOrder_time);
//执行查询
ordersService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
}