智慧校园-2-项目搭建
一、创建项目
IDEA --> New Product
选择Spring Initializr,URL可以先选择Default,如果连接失败,手动改成https://start.springboot.io/ --> Next
填写相关信息后,下一步
无需操作,直接Next。 依赖在后期开发中手动添加修改。
选择保存位置,Finish创建项目
二、修改项目相关设置
1、Setting --> Bulid Tools --> Maven --> 修改成自己的Maven地址
2、Setting --> Complier --> Annotaion Processors --> 勾选红框选项
3、pom.xml配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0modelVersion> <parent> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-parentartifactId> <version>2.2.1.RELEASEversion> <relativePath/> parent> <groupId>com.mukagroupId> <artifactId>damartifactId> <version>0.0.1-SNAPSHOTversion> <name>damname> <description>木卡美术后台管理系统description> <dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starterartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-thymeleafartifactId> dependency> <dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-boot-starterartifactId> <version>3.3.1version> dependency> <dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <optional>trueoptional> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> dependency> <dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-generatorartifactId> <version>3.3.1version> dependency> <dependency> <groupId>org.freemarkergroupId> <artifactId>freemarkerartifactId> <version>2.3.31version> dependency> <dependency> <groupId>io.springfoxgroupId> <artifactId>springfox-swagger2artifactId> <version>2.7.0version> dependency> <dependency> <groupId>io.springfoxgroupId> <artifactId>springfox-swagger-uiartifactId> <version>2.7.0version> dependency> <dependency> <groupId>com.github.xiaoymingroupId> <artifactId>knife4j-spring-boot-starterartifactId> <version>2.0.4version> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-devtoolsartifactId> <optional>trueoptional> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>fastjsonartifactId> <version>1.2.29version> dependency> <dependency> <groupId>io.jsonwebtokengroupId> <artifactId>jjwtartifactId> <version>0.7.0version> dependency> <dependency> <groupId>commons-fileuploadgroupId> <artifactId>commons-fileuploadartifactId> <version>1.4version> dependency> dependencies> <build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins> build> project>
三、数据库准备
1、创建出数据库
2、IDEA中连接数据库
四、准备目录结构
1、java目录下:
-
-
controller: 控制层
-
mapper : 持久层接口
-
pojo : 实体类
-
service: 服务层
-
util: 工具类包
2、resources目录下
-
-
public/upload:文件上传目录
-
static: 静态资源目录
-
3、application.yml文件配置
server: port: 9001 spring: #解决SpringBoot2.6.0与swagger冲突问题【原因是在springboot2.6.0中将SpringMVC 默认路径匹配策略从AntPathMatcher 更改为PathPatternParser,导致出错,解决办法是切换回原先的AntPathMatcher】 mvc: pathmatch: matching-strategy: ant_path_matcher #配置数据源 datasource: #配置数据源类型 type: com.zaxxer.hikari.HikariDataSource #配置数据库连接属性 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/schoolbam?characterEncoding=utf-8&serverTimezone=GMT%2B8&userSSL=false username: root password: root #url: jdbc:mysql://r2czkq1vewxat78mnyg60oisurj5h4dp.mysql.qingcloud.link:3306/ssg_zhxy_db?characterEncoding=utf-8&serverTimezone=GMT%2B8&userSSL=false #username: shangguigu #password: shangguigu@QY123 #mybatis-plus内置连接池 hikari: connection-test-query: SELECT 1 connection-timeout: 60000 idle-timeout: 500000 max-lifetime: 540000 maximum-pool-size: 12 minimum-idle: 10 pool-name: GuliHikariPool thymeleaf: #模板的模式,支持 HTML, XML TEXT JAVASCRIPT mode: HTML5 #编码 可不用配置 encoding: UTF-8 #开发配置为false,避免修改模板还要重启服务器 cache: false #配置模板路径,默认是templates,可以不用配置 prefix: classpath:/static/ jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 servlet: #设置文件上传上限 multipart: max-file-size: 10MB max-request-size: 100MB mybatis-plus: configuration: #添加日志支持 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath*:/mapper/**/*.xml
五、准备config下的配置类
??1、分页插件配置类
package com.sloth.schoolbam.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.atguigu.zhxy.mapper")
public class MpConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// paginationInterceptor.setLimit(你的最大单页限制数量,默认 500 条,小于 0 如 -1 不受限制);
return paginationInterceptor;
}
}
??2、Swagger2的配置类
Swagger2的配置类
package com.sloth.schoolbam.config;
import com.google.common.base.Predicates;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2配置信息
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket webApiConfig(){
//添加head参数start
List pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("userId")
.description("用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
ParameterBuilder tmpPar = new ParameterBuilder();
tmpPar.name("userTempId")
.description("临时用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tmpPar.build());
//添加head参数end
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
//可以测试请求头中:输入token
.apis(RequestHandlerSelectors.withClassAnnotation(ApiOperation.class))
//过滤掉admin路径下的所有页面
//.paths(Predicates.and(PathSelectors.regex("/sms/.*")))
//过滤掉所有error或error.*页面
//.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.globalOperationParameters(pars);
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-API文档")
.description("本文档描述了网站微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://atguigu.com", "512111559@qq.com"))
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://atguigu.com", "512111559@qq.com"))
.build();
}
}
?六、准备util下的相关类
验证码图片工具类 CreateVerifiCodeImage
package com.sloth.schoolbam.util;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* @project: ssm_sms
* @description: 绘制验证码图片
*/
public class CreateVerifiCodeImage {
private static int WIDTH = 90;
private static int HEIGHT = 35;
private static int FONT_SIZE = 20; //字符大小
private static char[] verifiCode; //验证码
private static BufferedImage verifiCodeImage; //验证码图片
/**
* @description: 获取验证码图片
* @param: no
* @return: java.awt.image.BufferedImage
*/
public static BufferedImage getVerifiCodeImage() {
verifiCodeImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_BGR);// create a image
Graphics graphics = verifiCodeImage.getGraphics();
verifiCode = generateCheckCode();
drawBackground(graphics);
drawRands(graphics, verifiCode);
graphics.dispose();
return verifiCodeImage;
}
/**
* @description: 获取验证码
* @param: no
* @return: char[]
*/
public static char[] getVerifiCode() {
return verifiCode;
}
/**
* @description: 随机生成验证码
* @param: no
* @return: char[]
*/
private static char[] generateCheckCode() {
String chars = "0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] rands = new char[4];
for (int i = 0; i < 4; i++) {
int rand = (int) (Math.random() * (10 + 26 * 2));
rands[i] = chars.charAt(rand);
}
return rands;
}
/**
* @description: 绘制验证码
* @param: g
* @param: rands
* @return: void
*/
private static void drawRands(Graphics g, char[] rands) {
g.setFont(new Font("Console", Font.BOLD, FONT_SIZE));
for (int i = 0; i < rands.length; i++) {
g.setColor(getRandomColor());
g.drawString("" + rands[i], i * FONT_SIZE + 10, 25);
}
}
/**
* @description: 绘制验证码图片背景
* @param: g
* @return: void
*/
private static void drawBackground(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
// 绘制验证码干扰点
for (int i = 0; i < 200; i++) {
int x = (int) (Math.random() * WIDTH);
int y = (int) (Math.random() * HEIGHT);
g.setColor(getRandomColor());
g.drawOval(x, y, 1, 1);
}
}
/**
* @description: 获取随机颜色
* @param: no
* @return: java.awt.Color
*/
private static Color getRandomColor() {
Random ran = new Random();
return new Color(ran.nextInt(220), ran.nextInt(220), ran.nextInt(220));
}
}
解析request请求中的token口令的工具 AuthContextHolder
package com.sloth.schoolbam.util;
import javax.servlet.http.HttpServletRequest;
/**
* 解析request请求中的 token口令的工具AuthContextHolder
*/
public class AuthContextHolder {
//从请求头token获取userid
public static Long getUserIdToken(HttpServletRequest request) {
//从请求头token
String token = request.getHeader("token");
//调用工具类
Long userId = JwtHelper.getUserId(token);
return userId;
}
//从请求头token获取name
public static String getUserName(HttpServletRequest request) {
//从header获取token
String token = request.getHeader("token");
//jwt从token获取username
String userName = JwtHelper.getUserName(token);
return userName;
}
}
token口令生成工具 JwtHelper
package com.sloth.schoolbam.util;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* token口令生成工具 JwtHelper
*/
public class JwtHelper {
private static long tokenExpiration = 24*60*60*1000;
private static String tokenSignKey = "123456";
//生成token字符串
public static String createToken(Long userId, Integer userType) {
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
// .claim("userName", userName)
.claim("userType", userType)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从token字符串获取userid
public static Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
//从token字符串获取userType
public static Integer getUserType(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws claimsJws
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (Integer)(claims.get("userType"));
}
//从token字符串获取userName
public static String getUserName(String token) {
if(StringUtils.isEmpty(token)) return "";
Jws claimsJws
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("userName");
}
//判断token是否有效
public static boolean isExpiration(String token){
try {
boolean isExpire = Jwts.parser()
.setSigningKey(tokenSignKey)
.parseClaimsJws(token)
.getBody()
.getExpiration().before(new Date());
//没有过期,有效,返回false
return isExpire;
}catch(Exception e) {
//过期出现异常,返回true
return true;
}
}
/**
* 刷新Token
* @param token
* @return
*/
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = Jwts.parser()
.setSigningKey(tokenSignKey)
.parseClaimsJws(token)
.getBody();
refreshedToken = JwtHelper.createToken(getUserId(token), getUserType(token));
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
public static void main(String[] args) {
// String token = JwtHelper.createToken(1L, "lucy");
// System.out.println(token);
// System.out.println(JwtHelper.getUserId(token));
// System.out.println(JwtHelper.getUserName(token));
}
}
加密工具 MD5
package com.sloth.schoolbam.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
}
统一返回结果状态信息类 ResultCodeEnum
package com.sloth.schoolbam.util;
import lombok.Getter;
/**
* 统一返回结果状态信息类
*
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"),
ILLEGAL_REQUEST( 204, "非法请求"),
PAY_RUN(205, "支付中"),
ARGUMENT_VALID_ERROR(206, "参数校验错误"),
LOGIN_ERROR(207, "用户名或密码错误"),
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限"),
SECKILL_NO_START(210, "秒杀还没开始"),
SECKILL_RUN(211, "正在排队中"),
SECKILL_NO_PAY_ORDER(212, "您有未支付的订单"),
SECKILL_FINISH(213, "已售罄"),
SECKILL_END(214, "秒杀已结束"),
SECKILL_SUCCESS(215, "抢单成功"),
SECKILL_FAIL(216, "抢单失败"),
SECKILL_ILLEGAL(217, "请求不合法"),
SECKILL_ORDER_SUCCESS(218, "下单成功"),
COUPON_GET(220, "优惠券已经领取"),
COUPON_LIMIT_GET(221, "优惠券已发放完毕"),
//2022-02-22
LOGIN_CODE(222,"长时间未操作,会话已失效,请刷新页面后重试!"),
CODE_ERROR(223,"验证码错误!"),
TOKEN_ERROR(224,"Token无效!")
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
全局统一返回结果类 Result
package com.sloth.schoolbam.util;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 全局统一返回结果类
*
*/
@Data
@ApiModel(value = "全局统一返回结果")
public class Result {
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private T data;
public Result(){}
// 返回数据
protected static Result build(T data) {
Result result = new Result();
if (data != null)
result.setData(data);
return result;
}
public static Result build(T body, ResultCodeEnum resultCodeEnum) {
Result result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static Result ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data
* @param
* @return
*/
public static Result ok(T data){
Result result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static Result fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param
* @return
*/
public static Result fail(T data){
Result result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result message(String msg){
this.setMessage(msg);
return this;
}
public Result code(Integer code){
this.setCode(code);
return this;
}
public boolean isOk() {
if(this.getCode().intValue() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
return true;
}
return false;
}
}
上传文件的工具类 UploadFile
package com.sloth.schoolbam.util;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @project: zhxy
* @description: 上传文件的工具类
*/
public class UploadFile {
//存储文件上传失败的错误信息
private static Map error_result = new HashMap<>();
//存储头像的上传结果信息
private static Map upload_result = new HashMap<>();
/**
* @description: 效验所上传图片的大小及格式等信息...
* @param: photo
* @param: path
* @return: java.util.Map
*/
private static Map uploadPhoto(MultipartFile photo, String path) {
//限制头像大小(20M)
int MAX_SIZE = 20971520;
//获取图片的原始名称
String orginalName = photo.getOriginalFilename();
//如果保存文件的路径不存在,则创建该目录
File filePath = new File(path);
if (!filePath.exists()) {
filePath.mkdirs();
}
//限制上传文件的大小
if (photo.getSize() > MAX_SIZE) {
error_result.put("success", false);
error_result.put("msg", "上传的图片大小不能超过20M哟!");
return error_result;
}
// 限制上传的文件类型
String[] suffixs = new String[]{".png", ".PNG", ".jpg", ".JPG", ".jpeg", ".JPEG", ".gif", ".GIF", ".bmp", ".BMP"};
SuffixFileFilter suffixFileFilter = new SuffixFileFilter(suffixs);
if (!suffixFileFilter.accept(new File(path + orginalName))) {
error_result.put("success", false);
error_result.put("msg", "禁止上传此类型文件! 请上传图片哟!");
return error_result;
}
return null;
}
/**
* @description: (提取公共代码 : 提高代码的可重用性)获取头像的上传结果信息
* @param: photo
* @param: dirPaht
* @param: portraitPath
* @return: java.util.Map
*/
public static Map getUploadResult(MultipartFile photo, String dirPaht, String portraitPath) {
if (!photo.isEmpty() && photo.getSize() > 0) {
//获取图片的原始名称
String orginalName = photo.getOriginalFilename();
//上传图片,error_result:存储头像上传失败的错误信息
Map error_result = UploadFile.uploadPhoto(photo, dirPaht);
if (error_result != null) {
return error_result;
}
//使用UUID重命名图片名称(uuid__原始图片名称)
String newPhotoName = UUID.randomUUID() + "__" + orginalName;
//将上传的文件保存到目标目录下
try {
photo.transferTo(new File(dirPaht + newPhotoName));
upload_result.put("success", true);
upload_result.put("portrait_path", portraitPath + newPhotoName);//将存储头像的项目路径返回给页面
} catch (IOException e) {
e.printStackTrace();
upload_result.put("success", false);
upload_result.put("msg", "上传文件失败! 服务器端发生异常!");
return upload_result;
}
} else {
upload_result.put("success", false);
upload_result.put("msg", "头像上传失败! 未找到指定图片!");
}
return upload_result;
}
}