Search K
Appearance
Appearance
在学习完瑞吉外卖后一直没有进行实战练习,故此借助周末时间开发一套简单的 TodoTask App
的 API
,原型是参考时光系App
的,我们只开发App
需要用到的API
接口,不开发具体的客户端,项目以练习SpringBoot
框架为主要目的。
1、用户注册,注册成功后用用户密码去登录,登陆成功后返回token,后续所有请求都需要在headers中携带token进行验证
2、用户可以创建多个任务,并且可以更改当前任务状态(完结、未完成),可以设置任务执行时间,可以关联分类,可以关联多张照片,在创建任务时如果传入数据包含了子任务则自动创建对应的子任务
3、一个任务可以包含多个子任务,用户可以针对一个任务创建多个子任务,用户可以更改子任务状态(完结、未完成),如果一个任务的子任务全部都完成了,默认完成该任务
4、一个任务可以关联多个分类,一个分类也可以关联多个任务
用户注册接口
编辑用户信息
用户登录接口
创建任务(如果填写了子任务则在创建任务的同时创建好子任务)接口
创建子任务接口
编辑任务接口
编辑子任务接口
删除任务(同步删除所有子任务)接口
删除子任务接口
图片公共上传接口
公共图片下载接口
分页查询任务列表接口
根据任务id分页查询子任务接口
根据分类id查询所有任务接口
根据任务id查询所有的分类接口
勾选必要的依赖,这里我们使用的SpringBoot
版本是2.7.12
,待会我们在pom
文件中修改版本
1、setting\maven
查看使用的仓库是不是本地仓库
2、setting\maven\runner
查看运行的java
版本
3、project structure
查看jdk
版本
我们修改SpringBoot
版本为2.4.5
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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu.task</groupId>
<artifactId>task-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>task-app</name>
<description>todo task App接口服务</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
引入Slf4j
包并且添加项目启动提示
com.atguigu.task.taskapp.TaskAppApplication
package com.atguigu.task.taskapp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class TaskAppApplication {
public static void main(String[] args) {
SpringApplication.run(TaskAppApplication.class, args);
log.info("项目启动成功....");
}
}
引入数据库驱动依赖以及mybatis-plus
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
配置application.yml
server:
port: 3000
spring:
application:
name: task_app
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/task_app?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: AUTO
SpringBoot
使用jwt
登录,引入jwt
依赖
pom.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
添加jwt
工具类
com.atguigu.task.taskapp.utils.JwtUtil
package com.atguigu.task.taskapp.utils;
import com.atguigu.task.taskapp.entity.User;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.UUID;
@Slf4j
public class JwtUtil {
// 设置 token 过期时间(单位:毫秒):目前为 6000 秒
private static final long ttl = 6000 * 1000;
// Signature 签名
private static final String signature = "Zhao-Chao";
public static String createToken(User user) {
JwtBuilder jwtBuilder = Jwts.builder();
return jwtBuilder
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setId(UUID.randomUUID().toString())
.setSubject(user.getUserName())
.setExpiration(new Date(System.currentTimeMillis() + ttl))
.setIssuer("Wu-Yikun")
.setIssuedAt(new Date())
.claim("userId", user.getId())
.claim("role", "user")
.signWith(SignatureAlgorithm.HS256, signature)
.compact();
}
/**
* 校验token
* @param token
* @return
*/
public static boolean parseToken(String token) {
if (token == null) {
return false;
}
JwtParser jwtParser = Jwts.parser();
try {
jwtParser.setSigningKey(signature).parseClaimsJws(token);
} catch (Exception e) {
return false;
}
return true;
}
/**
* 提取token信息
* @param token
*/
public static Claims tokenToOut(String token) {
Claims claims = Jwts.parser()
.setSigningKey(signature)
.parseClaimsJws(token)
.getBody();
return claims;
}
}
com.atguigu.task.taskapp.entity.User
package com.atguigu.task.taskapp.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// 主键,用户id
private long id;
// 用户名称
private String userName;
// 手机号码
private String phone;
// 性别
private Integer sex;
// 身份证
private String idNumber;
// 头像
private String avatar;
// 状态0表示禁用 1表示启用
private Integer status;
// 密码
private String password;
}
com.atguigu.task.taskapp.mapper.UserMapper
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
com.atguigu.task.taskapp.service.UserService
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
com.atguigu.task.taskapp.service.impl.UserServiceImpl
package com.atguigu.task.taskapp.service.impl;
import com.atguigu.task.taskapp.entity.User;
import com.atguigu.task.taskapp.mapper.UserMapper;
import com.atguigu.task.taskapp.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
用户登录注册以及修改用户信息 com.atguigu.task.taskapp.controller.UserController
package com.atguigu.task.taskapp.controller;
import com.atguigu.task.taskapp.common.R;
import com.atguigu.task.taskapp.dto.UserDto;
import com.atguigu.task.taskapp.entity.User;
import com.atguigu.task.taskapp.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
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.util.HashMap;
import java.util.Map;
import static com.atguigu.task.taskapp.utils.JwtUtil.createToken;
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用户注册
* @param request
* @param user
* @return
*/
@PostMapping("/register")
public R<String> register(HttpServletRequest request, @RequestBody User user){
log.info("user{}",user);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 通过传入的userName查询是否可以匹配到数据
queryWrapper.eq(user.getUserName() != null,User::getUserName,user.getUserName());
User user1 = userService.getOne(queryWrapper);
// 如果用户已存在直接返回已注册信息
if(user1 != null){
return R.error("该用户已注册,请直接登录");
}
// 进行密码md5加密
String password = user.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
user.setPassword(password);
userService.save(user);
return R.success("注册成功");
}
/**
*
* @param request
* @param user
* @return
*/
@PostMapping("/login")
public R<User> login(HttpServletRequest request, @RequestBody User user){
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(user!=null,User::getUserName,user.getUserName());
User user1 = userService.getOne(queryWrapper);
// 用户是否存在
if(user1==null){
return R.error("用户不存在");
}
// 获取md5加密后密码
String password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
// 比较密码是否一致
if(!user1.getPassword().equals(password)){
return R.error("密码不一致");
}
// 账号禁用
if(user1.getStatus() == 0){
return R.error("账号已经禁用");
}
// 登录成功,生成token
String token = createToken(user1.getUserName());
return R.success(user1).add("token",token);
}
}
com.atguigu.task.taskapp.TaskAppApplication
package com.atguigu.task.taskapp;
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 TaskAppApplication {
public static void main(String[] args) {
SpringApplication.run(TaskAppApplication.class, args);
log.info("项目启动成功....");
}
}
com.atguigu.task.taskapp.filter.LoginCheckFilter
package com.atguigu.task.taskapp.filter;
import com.alibaba.fastjson.JSON;
import com.atguigu.task.taskapp.common.R;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
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;
import static com.atguigu.task.taskapp.utils.JwtUtil.parseToken;
import static com.atguigu.task.taskapp.utils.JwtUtil.tokenToOut;
/**
* 检测当前访问用户是否已完成了登录
*/
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter{
// 路径匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/**
* 1、获取本次请求的url
* 2、判断本次请求是否需要处理
* 3、如果不需要处理则直接放行
* 4、判断登录状态,从header中获取token信息,并进行校验
* 5、如果token失效则返回登陆结果
*/
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 设置字符集编码,避免返回中文时乱码
response.setContentType("text/html;charset=UTF-8");
// 从头部信息中获取token令牌
String token = request.getHeader("token");
// 获取本次请求地址
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}",requestURI);
// 放行白名单
String[] urls = new String[]{
"/user/login",
"/user/register"
//"/backend/**"
};
// 判断请求地址是否在白名单中
boolean check = check(urls,requestURI);
// 白名单放行
if(check){
log.info("本次请求: {},白名单不需要处理",requestURI);
filterChain.doFilter(request,response);
return;
}
log.info("token: {}",token);
// token不存在直接返回登陆失败
if(token == null){
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
try {
// 校验token
boolean checkToken = parseToken(token);
Claims claims = tokenToOut(token);
Integer userId = (Integer) claims.get("userId");
String userName = claims.getSubject();
// 校验token通过
if(!checkToken){
log.info("token校验失败");
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
log.info("用户已登陆,用户id为{},用户名称为{}",userId,userName);
filterChain.doFilter(request,response);
return;
} catch (Exception e) {
// token已过期
if(e.getMessage().contains("JWT expired")){
response.getWriter().write(JSON.toJSONString(R.error("token已过期,请重新登录")));
}
}
}
/**
* 路径匹配函数
* @param urls 传入的路径字符串数组
* @param requestURI 当前的请求地址
* @return
*/
public boolean check(String[] urls,String requestURI){
for(String url: urls){
boolean match = PATH_MATCHER.match(url,requestURI);
if(match){
return true;
}
}
return false;
}
}
每一个任务都可以关联多个分类,每个分类也可以关联多个任务,接下来我们需要创建分类模块的增删改查
com.atguigu.task.taskapp.entity.Category
package com.atguigu.task.taskapp.entity;
import lombok.Data;
@Data
public class Category {
private static final long serialVersionUID = 1L;
// 主键,用户id
private long id;
// 用户名称
private String categoryName;
}
com.atguigu.task.taskapp.mapper.CategoryMapper
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.Category;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
package com.atguigu.task.taskapp.service;
import com.atguigu.task.taskapp.entity.Category;
import com.baomidou.mybatisplus.extension.service.IService;
public interface CategoryService extends IService<Category> {
}
com.atguigu.task.taskapp.service.impl.CategoryServiceImpl
package com.atguigu.task.taskapp.service.impl;
import com.atguigu.task.taskapp.entity.Category;
import com.atguigu.task.taskapp.mapper.CategoryMapper;
import com.atguigu.task.taskapp.service.CategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
com.atguigu.task.taskapp.controller.CategoryController
package com.atguigu.task.taskapp.controller;
import com.atguigu.task.taskapp.common.R;
import com.atguigu.task.taskapp.entity.Category;
import com.atguigu.task.taskapp.service.CategoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 添加分类
* @param request
* @param category
* @return
*/
@PostMapping("/add")
public R<String> add(HttpServletRequest request, @RequestBody Category category){
categoryService.save(category);
return R.success("新增成功");
}
/**
* 编辑分类
* @param request
* @param category
* @return
*/
@PostMapping("/edit")
public R<String> edit(HttpServletRequest request,@RequestBody Category category){
categoryService.updateById(category);
return R.success("保存成功");
}
/**
* 删除一条分类
* @param id
* @return
*/
@DeleteMapping("/delete")
public R<String> delete(@RequestParam long id){
log.info("删除id:{}",id);
categoryService.removeById(id);
return R.success("删除成功");
}
/**
* 分页查询分类列表
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("page = {},pageSize={},name={}",page,pageSize,name);
// 构造分页器
Page pageInfo = new Page(page,pageSize);
// 构造条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
// 添加一个过滤条件
queryWrapper.like(StringUtils.hasText(name),Category::getCategoryName,name);
categoryService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
}
任务模块是核心模块,业务流程如下:
创建任务的同时可以创建子任务以及关联多个分类,后台在接收到任务时,要将对应的子任务同步入库。
分别创建子任务表和任务分类中间表模块
com.atguigu.task.taskapp.entity.SubTask
package com.atguigu.task.taskapp.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
@Data
public class SubTask {
private static final long serialVersionUID = 1L;
// 主键,用户id
private long id;
// 任务执行时间
private Date date;
// 任务内容
private String content;
// 创建时间
private LocalDateTime createTime;
// 修改时间
private LocalDateTime updateTime;
// 创建用户id
private long userId;
// 任务id
private long taskId;
// 任务名称
private String taskName;
// 用户名称
private String userName;
// 任务状态 0未完成1已完成
private Integer status;
}
com.atguigu.task.taskapp.mapper.SubTaskMapper
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.SubTask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SubTaskMapper extends BaseMapper<SubTask> {
}
com.atguigu.task.taskapp.service.SubTaskService
package com.atguigu.task.taskapp.service;
import com.atguigu.task.taskapp.entity.SubTask;
import com.baomidou.mybatisplus.extension.service.IService;
public interface SubTaskService extends IService<SubTask> {
}
com.atguigu.task.taskapp.service.impl.SubTaskServiceImpl
package com.atguigu.task.taskapp.service.impl;
import com.atguigu.task.taskapp.entity.SubTask;
import com.atguigu.task.taskapp.mapper.SubTaskMapper;
import com.atguigu.task.taskapp.service.SubTaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class SubTaskServiceImpl extends ServiceImpl<SubTaskMapper, SubTask> implements SubTaskService {
}
com.atguigu.task.taskapp.entity.TaskCategory
package com.atguigu.task.taskapp.entity;
import lombok.Data;
@Data
public class TaskCategory {
private static final long serialVersionUID = 1L;
// 主键,用户id
private long id;
// 任务id
private long taskId;
// 分类id
private long categoryId;
}
com.atguigu.task.taskapp.mapper.TaskCategoryMapper
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.TaskCategory;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TaskCategoryMapper extends BaseMapper<TaskCategory> {
}
com.atguigu.task.taskapp.service.TaskCategoryService
package com.atguigu.task.taskapp.service;
import com.atguigu.task.taskapp.entity.TaskCategory;
import com.baomidou.mybatisplus.extension.service.IService;
public interface TaskCategoryService extends IService<TaskCategory> {
}
com.atguigu.task.taskapp.service.impl.TaskCategoryServiceImpl
package com.atguigu.task.taskapp.service.impl;
import com.atguigu.task.taskapp.entity.TaskCategory;
import com.atguigu.task.taskapp.mapper.TaskCategoryMapper;
import com.atguigu.task.taskapp.service.TaskCategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class TaskCategoryServiceImpl extends ServiceImpl<TaskCategoryMapper, TaskCategory> implements TaskCategoryService {
}
com.atguigu.task.taskapp.entity.Task
package com.atguigu.task.taskapp.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
@Data
public class Task {
private static final long serialVersionUID = 1L;
// 主键,任务id
private long id;
// 任务执行时间
private Date date;
// 任务内容
private String content;
// 创建时间
private LocalDateTime createTime;
// 修改时间
private LocalDateTime updateTime;
// 创建用户id
private long userId;
// 任务封面
private String covers;
// 备注
private String remark;
// 用户名称
private String userName;
// 任务状态 0未完成1已完成
private Integer status;
}
com.atguigu.task.taskapp.dto.TaskDto
package com.atguigu.task.taskapp.dto;
import com.atguigu.task.taskapp.entity.Category;
import com.atguigu.task.taskapp.entity.SubTask;
import com.atguigu.task.taskapp.entity.Task;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class TaskDto extends Task {
// 子任务列表
private List<SubTask> subTaskList = new ArrayList<>();
// 关联的分类列表
private List<Category> categoryList = new ArrayList<>();
}
com.atguigu.task.taskapp.mapper.TaskMapper
package com.atguigu.task.taskapp.mapper;
import com.atguigu.task.taskapp.entity.Task;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TaskMapper extends BaseMapper<Task> {
}
com.atguigu.task.taskapp.service.TaskService
package com.atguigu.task.taskapp.service;
import com.atguigu.task.taskapp.dto.TaskDto;
import com.atguigu.task.taskapp.entity.Task;
import com.baomidou.mybatisplus.extension.service.IService;
public interface TaskService extends IService<Task> {
// 新增菜品,同时插入子任务
public void saveWithSubtask(TaskDto taskDto);
}
com.atguigu.task.taskapp.service.impl.TaskServiceImpl
package com.atguigu.task.taskapp.service.impl;
import com.atguigu.task.taskapp.dto.TaskDto;
import com.atguigu.task.taskapp.entity.Category;
import com.atguigu.task.taskapp.entity.SubTask;
import com.atguigu.task.taskapp.entity.Task;
import com.atguigu.task.taskapp.entity.TaskCategory;
import com.atguigu.task.taskapp.mapper.TaskMapper;
import com.atguigu.task.taskapp.service.SubTaskService;
import com.atguigu.task.taskapp.service.TaskCategoryService;
import com.atguigu.task.taskapp.service.TaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements TaskService {
@Autowired
private TaskCategoryService taskCategoryService;
@Autowired
private SubTaskService subTaskService;
@Transactional
@Override
public void saveWithSubtask(TaskDto taskDto) {
// 1、数据提交任务对象、子任务列表、当前关联的分类列表
// 2、将任务对象数据入库
// 3、将分类列表入库
// 4、将子任务列表入库
// 创建主任务
Task task = new Task();
task.setDate(taskDto.getDate());
task.setContent(taskDto.getContent());
task.setCreateTime(LocalDateTime.now());
task.setUpdateTime(LocalDateTime.now());
task.setUserName(taskDto.getUserName());
task.setUserId(taskDto.getUserId());
task.setCovers(taskDto.getCovers());
task.setRemark(taskDto.getRemark());
task.setStatus(taskDto.getStatus());
// 保存主任务
this.save(task);
// 创建子任务列表
List<SubTask> subTaskList = new ArrayList<>();
// 抽取子任务
subTaskList = taskDto.getSubTaskList();
// 遍历subTaskList 设置taskId
subTaskList = subTaskList.stream().map((item) -> {
// 为每一项设置taskId
item.setTaskId(task.getId());
item.setCreateTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
return item;
}).collect(Collectors.toList());
List<TaskCategory> taskCategoryList = new ArrayList<>();
// 遍历categoryList 填充taskCategoryList
taskDto.getCategoryList().stream().map((item) -> {
TaskCategory taskCategory = new TaskCategory();
taskCategory.setTaskId(task.getId());
taskCategory.setCategoryId(item.getId());
taskCategoryList.add(taskCategory);
return item;
}).collect(Collectors.toList());
log.info("taskCategoryList{}",taskCategoryList);
log.info("subTaskList{}",subTaskList);
// 保存子任务
subTaskService.saveBatch(subTaskList);
// 保存在任务分类表
taskCategoryService.saveBatch(taskCategoryList);
}
}
com.atguigu.task.taskapp.controller.TaskController
package com.atguigu.task.taskapp.controller;
import com.atguigu.task.taskapp.common.R;
import com.atguigu.task.taskapp.dto.TaskDto;
import com.atguigu.task.taskapp.entity.SubTask;
import com.atguigu.task.taskapp.entity.Task;
import com.atguigu.task.taskapp.entity.TaskCategory;
import com.atguigu.task.taskapp.service.SubTaskService;
import com.atguigu.task.taskapp.service.TaskCategoryService;
import com.atguigu.task.taskapp.service.TaskService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.atguigu.task.taskapp.utils.JwtUtil.tokenToOut;
@Slf4j
@RestController
@RequestMapping("/task")
public class TaskController {
@Autowired
private TaskService taskService;
@Autowired
private SubTaskService subTaskService;
@Autowired
private TaskCategoryService taskCategoryService;
/**
* 提交任务
* @param request
* @param taskDto
* @return
*/
@PostMapping("/add")
public R<String> add(HttpServletRequest request, @RequestBody TaskDto taskDto){
try {
taskService.saveWithSubtask(taskDto);
}catch (Exception e){
log.error("保存任务报错{}",e.getMessage());
return R.error("保存失败,请稍后重试");
}
return R.success("保存成功");
}
/**
* 任务列表
* @param page
* @param pageSize
* @return
*/
@GetMapping("/page")
public R<Page> page(int page, int pageSize,HttpServletRequest request){
// 获取token
String token = request.getHeader("token");
Claims claims = tokenToOut(token);
// 获取当前用户id
Integer userId = (Integer) claims.get("userId");
// 构造分页器
Page<Task> pageInfo = new Page(page,pageSize);
// 构造一个taskDto的分页器,用来保存最终的结果
Page<TaskDto> taskDtoPage = new Page<>();
LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件
queryWrapper.eq(Task::getUserId,userId);
// 执行分页查询 为pageInfo赋值
taskService.page(pageInfo,queryWrapper);
// 将pageInfo中的分页信息拷贝到taskDtoPage,排除record不拷贝,
// 因为taskDtoPage中record的每一项均需要扩展成TaskDto
BeanUtils.copyProperties(pageInfo,taskDtoPage,"record");
// 获取查询出来的records数据
List<Task> records = pageInfo.getRecords();
List<TaskDto> list = records.stream().map((item) -> {
TaskDto taskDto = new TaskDto();
// 将item拷贝到taskDto中共有的部分
BeanUtils.copyProperties(item,taskDto);
LambdaQueryWrapper<SubTask> subTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
subTaskLambdaQueryWrapper.eq(SubTask::getTaskId,item.getId());
List<SubTask> subTaskList = subTaskService.list(subTaskLambdaQueryWrapper);
taskDto.setSubTaskList(subTaskList);
return taskDto;
}).collect(Collectors.toList());
taskDtoPage.setRecords(list);
return R.success(taskDtoPage);
}
/**
* 完成任务以及子任务,更改任务状态的同时更新关联的子任务状态
* @param request
* @param task
* @return
*/
@PostMapping("/complete")
public R<String> complete(HttpServletRequest request,@RequestBody Task task){
Long id = task.getId();
Task task1 = taskService.getById(id);
if(task1 == null){
return R.error("任务不存在");
}
// 将任务状态修改成完成状态
task1.setStatus(task.getStatus());
LambdaQueryWrapper<SubTask> subTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 设置一个条件
subTaskLambdaQueryWrapper.eq(id!=null,SubTask::getTaskId,id);
List<SubTask> subTaskList = subTaskService.list(subTaskLambdaQueryWrapper);
// 批量更新关联的子任务状态
subTaskList = subTaskList.stream().map(item -> {
item.setStatus(task.getStatus());
return item;
}).collect(Collectors.toList());
taskService.updateById(task1);
subTaskService.updateBatchById(subTaskList);
log.info("subTaskList:{}",subTaskList);
return R.success("更新成功");
}
/**
* 分页根据category分页查询任务列表
* @param page
* @param pageSize
* @param categoryId
* @return
*/
@GetMapping("/category/list")
public R<Page> getListByCategoryId(int page,int pageSize,@RequestParam Long categoryId){
try {
// 最终返回的结果
List<Task> taskList = new ArrayList<>();
// 用来保存taskid集合
List<Long> taskIdList = new ArrayList<>();
// 构建分页查询器
Page<Task> pageInfo = new Page<>(page,pageSize);
// 创建条件构造器
LambdaQueryWrapper<TaskCategory> queryWrapper = new LambdaQueryWrapper<>();
// 设置条件
queryWrapper.eq(categoryId!=null,TaskCategory::getCategoryId,categoryId);
// 将指定分类关联数据查询出来
List<TaskCategory> taskCategoryList = taskCategoryService.list(queryWrapper);
log.info("taskCategoryList:{}",taskCategoryList);
// 提取并填充taskIdList
taskCategoryList.stream().map((item) -> {
taskIdList.add(item.getTaskId());
return item;
}).collect(Collectors.toList());
LambdaQueryWrapper<Task> taskLambdaQueryWrapper = new LambdaQueryWrapper<>();
taskLambdaQueryWrapper.in(Task::getId,taskIdList);
taskLambdaQueryWrapper.orderByAsc(Task::getUpdateTime);
// 执行分页查询并填充pageInfo
taskService.page(pageInfo,taskLambdaQueryWrapper);
log.info("pageInfo:{}",pageInfo);
return R.success(pageInfo);
}catch (Exception e){
log.info("e,{}",e);
return R.error("查询失败,请稍后重试");
}
}
}
添加项目部署脚本
#!/bin/sh
echo =================================
echo 自动化部署脚本启动
echo =================================
echo 停止原来运行中的工程
APP_NAME=taskapp
tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'Stop Process...'
kill -15 $tpid
fi
sleep 2
tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'Kill Process!'
kill -9 $tpid
else
echo 'Stop Success!'
fi
echo 准备从Git仓库拉取最新代码
cd /usr/local/taskapp
echo 开始从Git仓库拉取最新代码
git pull
echo 代码拉取完成
echo 开始打包
output=`mvn clean package -Dmaven.test.skip=true`
cd target
echo 启动项目
nohup java -jar task-app-0.0.1-SNAPSHOT &> taskapp.log &
echo 项目启动完成