梳理黑马案例tlias教学管理系统

最近也是马上学完黑马的ssm和springboot的相关知识,那么也重新回顾一下我的第一个案例/项目,梳理思路以及所思所感,如若有任何问题欢迎评论区指正!

首先该项目是使用了springboot框架以及mybatis等框架的一个小案例。根本目的在于将springboot和mybatis相关知识复习及上手。

在一个springboot的项目中,会将不同功能的代码分在起码四个层中存放,这四个基础层是controller,pojo,mapper,service。在本项目中,还存在anno,aop,config,exception,filter,interceptor,utils这几个包存在。除此之外,也学习并使用了xml,yml,properties来配置相应的依赖及mapper的方法。本案例的前端来自黑马准备好的nginx-1.22.0-tlias,我也不是太懂前端,故不在此赘述。那么我将从每个包的具体作用及学到的知识这两方面开始总结。

一、controller层

1.1 EmpController

controller层下有四个方法,分别是dept,emp,login,upload。也对应着部门管理,员工管理,登录管理和更新管理。该层主要是与前端交互,获取前端接收到的数据,再调用service层,通过service层与mapper层交互从而实现操作数据库实现crud的逻辑。

dept的逻辑与emp逻辑相差不多,故在这里只讲述emp层的实现逻辑。那么我从主方法开始讲述,既然要实现增删改查,那么controller方法中肯定要有增删改查的逻辑,而且四个方法大同小异我们拿删除员工举例:
在这里插入图片描述

该功能的接口文档如图所示,请求路径为/emps/{ids},那么我们就要通过@DeleteMapping这个注解来定义请求路径,之后使用@PathVariable这个方法获得前端传入的Integer类型的id,再定义删除方法,调用service层的delete方法将获取到的id传进去,之后响应给前端一个json格式的数据。具体代码如下:

@DeleteMapping("/{ids}") public Result delete(@PathVariable List<Integer> ids){ *log*.info("删除所选员工:{}",ids); empService.delete(ids); return Result.*success*(); }

这里deletemapping没有写全请求路径的原因是因为我在该controller方法上使用了@RequestMapping这个注解,这个注解可以将通用的路径参数提取出来,也避免了很多代码的冗余。

由于部门员工较多所以会使用分页展示这个功能,接口文档如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据接口文档,我们得知请求路径为/emps由于在前面加了注解故在此不用标注,我们直接定义一个返回值为result的方法,将页数和每页展示的员工数量定义好,

代码如下:

@GetMapping public Result page(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pageSize, String name, Short gender, @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end){ *log*.info("分页查询,参数:{},{},{},{},{},{}",page,pageSize,name,gender,begin,end); //调用service方法进行分页查询 PageBean pagebean= empService.page(page,pageSize,name,gender,begin,end); return Result.*success*(pagebean); }

在page方法中传入参数:page定义页码,pageSize定义分页展示的数量,name,gender见名知意,使用datetimeformat定义时间格式,入职日期和最后操作时间。将这些参数都传入page方法。调用service的方法控制mapper的list集合展示所获取的数据返回给前端。

需要注意的是:在修改员工数据的时候需要先获取要修改的员工的id再通过id调用update方法更新员工数据。

1.2 LoginController

该类主要是用来判断登录用户是否存在的,请求路径为/login使用@RequestBody获取前端传入的用户对象。调用service层的实现类与mapper层交互获取数据库中的用户数据,并且判断两边获得的数据是否相同,若相同直接发放jwt令牌,若不相同直接返回错误信息。具体代码如下:

`@PostMapping(“/login”)
public Result login(@RequestBody Emp emp){
log.info(“判断是否存在该用户:{}”,emp);
Emp e = empService.login(emp);

​ if(e != null){
​ Map<String,Object> cliams = new HashMap<>();
​ cliams.put(“name”,e.getName());
​ cliams.put(“id”,e.getId());
​ cliams.put(“username”,e.getUsername());
​ String jwt = JwtUtils.generateJwt(cliams);
​ return Result.success(jwt);
​ }
​ return Result.error(“用户名或密码错误”);
}`

使用哈希集直接消除相同的选项,只留下一个用户信息,最后使用该用户信息生成jwt令牌,最后return jwt令牌即可。

1.3 UploadController

该类主要是将前端上传的图片保存起来,可以使用本地储存方法,但是本地储存过于落后也容易造成数据丢失。所以在本项目中我采用了阿里云oss储存上传的图片。本地储存方法贴在这里供参考,并不过多赘述:

`@PostMapping(“upload”)
public Result upload(String name, Integer age, MultipartFile image) throws IOException {
log.info(“传入的姓名为{},年龄为{},图片为{}”,name,age,image);

​ //获取到原始的文件名
​ String p = image.getOriginalFilename();
​ // 取得.的索引然后截取后缀再将新生成的随机的uuid转成字符串拼接到后缀之前,这就是新的文件名
​ int a = p.lastIndexOf(“.”);
​ String newFileName = UUID.randomUUID().toString()+p.substring(a);

​ log.info(“新的文件名为{}”,newFileName);
​ //将文件存入本地磁盘中
​ image.transferTo(new File(“D:\uploadImage\”+newFileName));

​ return Result.success();
}`

使用阿里云oss储存文件得先去阿里云配置好endpoint,accessKeyId,accessKeySecret,bucketName这几个重要参数,还要在properties中配置。阿里云相关操作不细说。我们通过upload方法传入类型为MultipartFile的参数调用aliossutils工具类中的方法upload传入该图像生成url将url上传,并将url返回到前端。具体代码如下:

`@PostMapping(“/upload”)
public Result upload(MultipartFile image) throws IOException {

log.info(“上传的文件名为:{}”,image.getOriginalFilename());
​ String url = aliOSSUtils.upload(image);
log.info(“上传的文件url为:{}”,url);

​ return Result.success(url);
}`

二、utils层

因为上一节使用了utils这层,本层内容也不多故先讲解本层。本层只有两个类AliOSSUtils和JwtUtils。

2.1 AliOSSUtils

本工具类的作用就是实现上传图片至oss,我们会使用io流获取上传文件的输入流,new一个oss的对象,调用ossClient的方法将bucketName, fileName,和inputStream三个参数传入,所以也得引入alibaba的相关依赖:<groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version>

别忘了有可能传入相同文件名的图像造成覆盖,所以我们使用uuid的random方法生成随机数重命名该文件即可,之后将文件访问路径直接返回并且直接关闭输入流即可。具体代码如下:

`// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();

// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(“.”));

//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);

//文件访问路径
String url = endpoint.split(“//”)[0] + “//” + bucketName + “.” + endpoint.split(“//”)[1] + “/” + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回`

2.2 JwtUtils

该方法旨在生成jwt令牌和解析jwt令牌,JWT(JSON Web Token)是一种用于在网络应用间传递信息的开放标准(RFC 7519)。它以 JSON 格式存储被加密后的信息,通常用于验证和身份认证。**这是token验证的一种令牌。叫身份验证令牌。在前后端分离的架构中常用。**本质上是一个json格式的数据。将设置好的jwt令牌返回,都是jwt的相关使用方法。具体代码如下:

`*/**
* *** *生成JWT令牌
* *** @param claims JWT**第二部分负载 payload *中存储的内容
* *** **@return
** **/
*public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}

/*
* *** *解析JWT令牌
* *** @param jwt JWT令牌
* *** @return *JWT
第二部分负载
payload *中存储的内容
* **/
*public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}`

三、exception

该层主要是处理每个层之间未处理的异常,因为我们在各个层的异常都是直接上抛,并没有解决,我们在这里直接给出统一的解决方法——联系我,故代码如下:

@ExceptionHandler(Exception.class) public Result ex(Exception ex){ ex.printStackTrace(); return Result.*error*("对不起,操作失败,请联系我"); }

四、service

本层有两个实现类和两个接口,两个接口会定义抽象方法,之后在本层抽象方法的实现类中调用mapper层的sql语句执行应该的操作。例如我们会在抽象类中定义save新建员工方法,在实现类中实现并且调用mapper的具体方法,代码如下:

//新增员工 void save(Emp emp);

@Override public void save(Emp emp) { emp.setCreateTime(LocalDateTime.*now*()); emp.setUpdateTime(LocalDateTime.*now*()); empMapper.insert(emp); }

五、pojo

本层即定义每个具体对象应有的属性值,方便其他层调用避免重复定义。拿emp举例,代码如下:

@Data @NoArgsConstructor @AllArgsConstructor public class Emp { private Integer id; //ID private String username; //用户名 private String password; //密码 private String name; //姓名 private Short gender; //性别 , 1 男, 2 女 private String image; //图像url private Short job; //职位 , 1 班主任 , 2 讲师 , 3 学工主管 , 4 教研主管 , 5 咨询师 private LocalDate entrydate; //入职日期 private Integer deptId; //部门ID private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //修改时间 }

六、mapper

该层就是引用sql方法,从而实现操作数据库的功能,可以直接编写sql语句也可以在resources中使用xml文件对方法进行编写,我们拿插入进行举例,我是选择直接编写sql语句,代码如下:

@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)"+ "values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})") void insert(Emp emp);

七、filter/interceptor和config

本层主要是实现jwt令牌的两种实现方式,过滤器和拦截器,两种功能相近但不一样,之后详细介绍。

八、anno/aop

这两层主要是面向切面编程从而实现在增删改的方法上添加log注解,从而实现在增删改这三个方法实现后记录日志的功能。定义一个Log空annotation,之后仅在方法上添加该注解即可应用aop方法。比如我们要记录操纵者的相关信息,我们就可以先定义该方法,并使用ProceedingJoinPoint将参数传入然后再根据相关方法把要记录的东西记录下来,代码如下:

`@Autowired
private HttpServletRequest request;

@Autowired
private OperateLogMapper operateLogMapper;

@Around(“@annotation(com.itheima.anno.Log)”)
public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {

​ //操作人id
​ String jwt = request.getHeader(“token”);
​ Claims claims = JwtUtils.parseJWT(jwt);
​ Integer OperatorId = (Integer) claims.get(“id”);
​ //操作时间
​ LocalDateTime now = LocalDateTime.now();
​ //操作类名
​ String ClassName = joinPoint.getTarget().getClass().getName();
​ //操作方法名
​ String mName = joinPoint.getSignature().getName();
​ //操作方法参数
​ Object[] args = joinPoint.getArgs();
​ String argsString = Arrays.toString(args);
​ //开始时间
​ long begin = System.currentTimeMillis();
​ //按原始方法运行
​ Object result = joinPoint.proceed();
​ //结束时间
​ long end = System.currentTimeMillis();
​ //操作方法返回值
​ String MenthodReturn = JSONObject.toJSONString(result);
​ //操作耗时
​ long time = end - begin;
​ //插入数据库
​ OperateLog operateLog = new OperateLog(null,OperatorId,now,ClassName,mName,argsString,MenthodReturn,time);
​ operateLogMapper.insert(operateLog);
log.info(“aop记录操作日志;{}”,operateLog);
​ return result;
}`

使用的很多方法也都是joinpoint的方法,故需要引入

<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId>

该依赖即可。

九、总结

至此我们也完成了该项目的绝大多数的总结,具体课程可参照黑马2023/3/21的135至182集,具体代码我已上传至github

链接:H-ikaRI/tlias (github.com)欢迎浏览!!!若有什么问题也可私聊或评论区交流~

需要引入

<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId>

该依赖即可。

九、总结

至此我们也完成了该项目的绝大多数的总结,具体课程可参照黑马2023/3/21的135至182集,具体代码我已上传至github,链接:H-ikaRI/tlias (github.com)欢迎浏览!!!若有什么问题也可私聊或评论区交流~

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值