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.5pom.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: AUTOSpringBoot使用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 项目启动完成