管理系统开发

搭建后端程序

1、Spring Initializr 创建后端项目

image-20220318161426863

image-20220318161638925

image-20220318161748415

2、后期引入需要的依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--spring整合mybatis-plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!--Mybatis代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
    <!--velocity 模板引擎,Mybatis Plus 代码生成器需要(默认模板引擎)-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>
    <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>

</dependencies>

3、对全局配置文件application.yaml进行修改

server:
  port: 8080
  servlet:
    context-path: /
  tomcat:
    uri-encoding: utf-8

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/database?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: root
    password: root
    # 使用druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20
  ## 集群版本
  redis:
    database: 0
    pool:
      max-active: 100     #连接池最大连接数(负值表示没有限制)
      max-wait: 3000     #连接池最大阻塞等待时间(负值表示没有限制)
      max-idle: 200     #连接池最大空闭连接数
      min-idle: 50         #连接汉最小空闲连接数
      timeout: 600         #连接超时时间(毫秒)
    cluster:
      nodes:
        - 192.168.75.132:6380
        - 192.168.75.132:6381
        - 192.168.75.132:6382
        - 192.168.75.132:6383
        - 192.168.75.132:6384
        - 192.168.75.132:6385
  ## 单机版本
  redis:
    host: 192.168.75.132
    port: 6379  
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true

mybatis:
  mapper-locations: classpath:mapping/*.xml
  type-aliases-package: cn.felixfang.flog.entity

#mybatis-plus配置
mybatis-plus:
    mapper-locations: classpath:/mybatis/mappers/*.xml            #配置Mapper映射文件
    type-aliases-package: com.yunnuo.server.pojo                # 配置Mybatis数据返回类型别名(默认别名为类名)
    configuration:
        map-underscore-to-camel-case: false                        # 自动驼峰命名

#配置控制台打印日志Debug
logging:
    level:
        com.jd.mapper: debug

#自己也可以配置属性,通过@value、@ConfigurationProperties(prefix="")、@Component注入属性
my:
  users:
    - name: 大李
      age: 100
    - name: 小刘
      age: 200

4、利用代码生成器codeGenerator

package com.ly.utils;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;

import java.util.Collections;

/**
 * mp代码生成器
 */
public class codeGenerator {

    public static void main(String[] args) {
        generate();
    }

    private static void generate(){
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/project?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false", "root", "123456")
                .globalConfig(builder -> {
                    builder.author("ly") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("E:\\code_set\\IdeaProjects\\ly_aftermanage\\src\\main\\java\\"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.ly") // 设置父包名
                            .moduleName(null) // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E:\\code_set\\IdeaProjects\\ly_aftermanage\\src\\main\\resources\\Mapper\\")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.entityBuilder().enableLombok();
                    builder.mapperBuilder().enableMapperAnnotation().build();
                    builder.controllerBuilder().enableHyphenStyle()  // 开启驼峰转连字符
                            .enableRestStyle();  // 开启生成@RestController 控制器
                    builder.addInclude("menu")      // 设置需要生成的表名
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
//                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();

    }
}

5、修改代码生成器模板

  • maven仓库内搜索mybatis-plus-generator,打开,复制其中的templates目录至自己项目的resources下

image-20220318164019125

  • 所有的vm都是默认模板,也可以使用别的模板引擎来生成代码
package ${package.Controller};

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;


import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};

#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end

/**
 * @author ${author}
 * @since ${date}
 */
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end

#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end

    @Resource
    private ${table.serviceName} ${table.entityPath}Service;

    //新增或者更新
    @PostMapping
    public result save(@RequestBody ${entity} ${table.entityPath}) {
        return result.success(${table.entityPath}Service.saveOrUpdate(${table.entityPath}));
    }

    //删除
    @DeleteMapping("/{id}")
    public result delete(@PathVariable Integer id) {
        return result.success(${table.entityPath}Service.removeById(id));
    }

    //批量删除
    @PostMapping("/del/batch")  //地址记得变更
    public result deleteBatch(@RequestBody List<Integer> ids){
        return result.success(${table.entityPath}Service.removeBatchByIds(ids));
    }

    //查询所有
    @GetMapping
    public result findAll() {
        return result.success(${table.entityPath}Service.list());
    }

    //根据id查询
    @GetMapping("/{id}")
    public result findOne(@PathVariable Integer id) {
        return result.success(${table.entityPath}Service.getById(id));
    }

    //分页查询
    @GetMapping("/page")
    public result findPage(@RequestParam Integer pageNum,
                                    @RequestParam Integer pageSize) {

        QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();

        queryWrapper.orderByDesc("id");
        return result.success(${table.entityPath}Service.page(new Page<>(pageNum, pageSize),queryWrapper));
    }

}

#end

6、搭建数据库database,制作所需要的用户表、系统表、权限表、文件表等等

7、通过codegenerator生成对应表的对应实体类、mapper、controller、service等

image-20220318164508336

8、编写Controller层和Service层业务逻辑代码

image-20220318164630810

9、通过Constants类封装状态码,结合Result返回类封装返回参数

public class Constants {
    public static String CODE_200 = "200";        //成功
    public static String CODE_500 = "500";        //系统错误
    public static String CODE_401 = "401";        //权限不足
    public static String CODE_400 = "400";        //参数错误
    public static String CODE_600 = "600";        //数据错误
}
package com.ly.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 接口统一返回包装类
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
public class result {
    private String code;
    private String msg;
    private Object data;

    public static result success(){
        return new result(Constants.CODE_200,"",null);
    }

    public static result success(Object data){
        return new result(Constants.CODE_200,"",data);
    }

    public static result error(String code,String msg){
        return new result(code,msg,null);
    }

    public static result error(){
        return new result(Constants.CODE_500,"系统错误",null);
    }
}

常见问题

跨域问题

  • 配置跨域访问的配置类CorsConfig
package com.ly.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {

    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
}

登录校验问题

  • 使用jwt利用token进行登录校验
package com.ly.config.interceptor;

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.ly.common.Constants;
import com.ly.entity.User;
import com.ly.exception.ServiceException;
import com.ly.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        //如果不是映射的方法直接通过
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        //执行认证
        if (StrUtil.isBlank(token)){
            throw new ServiceException(Constants.CODE_401,"无token,请重新登录");
        }
        //获取token中的userid
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException e) {
            throw new ServiceException(Constants.CODE_401,"token验证失败");
        }
        //根据token中的userid查询数据库
        User user =  userService.getById(userId);
        if (user == null){
            throw new ServiceException(Constants.CODE_401,"用户不存在,请重新登录");
        }
        //用户密码加签验证token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录");
        }
        return true;
    }
}

开发需求

权限菜单分配

利用redis缓存


文章作者: 寜笙
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 寜笙 !
 上一篇
2022-03-18 寜笙
下一篇 
2022-03-15 寜笙
  目录