reggie外卖项目管理端快速开发

一、基于maven构建webApp开发环境

(1)基于maven构建webApp开发环境

创建一个空的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>

(2)创建引导类

创建引导类(启动类),实现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("项目启动成功");
    }
}

(3)创建webApp配置文件

构建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。

reggie外卖项目管理端快速开发_第1张图片

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); } }

你可能感兴趣的:(WEB项目组件,java,mysql,tomcat,spring,boot,restful)