守听 发表于 2023-11-5 01:46:02

Spring Boot 实现审核功能,实战来了!

一、审核功能实现的方式

1、普通

方案:经办时入A表,审核后从A表读取数据,然后操作目标B表;
优势:思路简单
劣势:对后端功能实行高度的嵌入;审核功能数据操作不统一
2、弹框式

方案:前台实现,操作时判断是否需要权限控制,如果需要,则弹出框,由审核人员进行审核,审核通过后,进行后续操作。
优势:对后台功能无嵌入;可支持查询、导出、操作等全部功能;
劣势:需要经办人和审核人同时在场操作
3、入参缓冲时

方案:审核功能是独立的功能,前台发起业务后,入参存入数据库。待审核通过后,后台触发调用相应的接口,并将执行结果通知到经办人。
优势:对前后台功能均无嵌入;支持导出及操作类;经办人和审核人可以异步操作;审核功能数据操作统一;
劣势:需要框架层支持;实现逻辑稍微复杂
4、临时表

方案:所有需要审核功能涉及的表均增加相应的表,该表比源表主要增加1个字段,即审核流水,其余字段命名完全一致;所有功能操作时先入该表,审核通过后,由后台从该表将数据同步至正表。
优势:无需要框架支持;支持导出及操作类;经办人和审核人可以异步操作;审核功能数据操作统一;
劣势:对后端功能实行高度的嵌入;
Spring Boot 基础就不介绍了,推荐看这个实战项目:
https://github.com/javastacks/spring-boot-best-practice
二、SpringBoot实现

1.创建数据库表SQL

CREATE TABLE `audit` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '报修名称',
`user` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '报修人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '报修时间',
`img` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '详情图片',
`state` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '待审核' COMMENT '待审核,审核通过,审核不通过',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;2.写Java后端

https://img2023.cnblogs.com/other/1218593/202310/1218593-20231017102508875-41825090.png
其实审核功能最主要的就是我们的新增功能,用户只有新增过后,我们的管理员才能去对你的申请进行审核,最后实现效果。
AuditController
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Result;
import com.example.demo.entity.Audit;
import com.example.demo.entity.Sanitation;
import com.example.demo.entity.User;
import com.example.demo.mapper.FileMapper;
import com.example.demo.service.IAuditService;
import com.example.demo.utils.TokenUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@CrossOrigin
@RestController
@RequestMapping("/audit")
public class AuditController {

    @Resource
    private IAuditService auditService;

    @Resource
    private FileMapper fileMapper;

//    //新增或者更新
//    @PostMapping
//    public Result save(@RequestBody Audit audit) {
//      audit.setUser(TokenUtils.getCurrentUser().getUsername());
      audit.setImg(Files.url);
//      return Result.success(auditService.saveOrUpdate(audit));
//    }

    // 新增或者更新
    @PostMapping
    public Result save(@RequestBody Audit audit) {
      if (audit.getId() == null) {
            // 新增
            audit.setUser(TokenUtils.getCurrentUser().getUsername());

      }
      auditService.saveOrUpdate(audit);
      return Result.success();
    }

    //删除
//    @DeleteMapping("/{id}")
//    public Result delete(@PathVariable Integer id) {
//      return Result.success(userService.removeById(id));
//    }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {//批量删除
      return Result.success(auditService.removeByIds(ids));
    }

    //查询所有数据
    @GetMapping
    public Result findAll() {
      return Result.success(auditService.list());
    }

//    @GetMapping("/role/{role}")
//    public Result findNames(@PathVariable String role) {
//      QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
//      queryWrapper.eq("role", role);
//      List<Audit> list = auditService.list(queryWrapper);
//      return Result.success(list);
//    }

    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
      return Result.success(auditService.getById(id));
    }

    @GetMapping("/username/{username}")
    public Result findByUsername(@PathVariable String username) {
      QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
      queryWrapper.eq("username", username);
      return Result.success(auditService.getOne(queryWrapper));
    }

    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {
      QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
      queryWrapper.orderByDesc("id");
      if (!"".equals(name)) {
            queryWrapper.like("name", name);
      }
      User currentUser = TokenUtils.getCurrentUser();
//      if (RoleEnum.ROLE_USER.toString().equals(currentUser.getRole())) {// 角色是普通用户
//            queryWrapper.eq("user", currentUser.getUsername());
//      }
      return Result.success(auditService.page(new Page<>(pageNum, pageSize), queryWrapper));

    }
}三、前端调用

1.实现效果

https://img2023.cnblogs.com/other/1218593/202310/1218593-20231017102509276-1046875168.png
2.核心代码

https://img2023.cnblogs.com/other/1218593/202310/1218593-20231017102509713-1432812511.png
<el-table-column label="审核" width="240">
<template v-slot="scope">
    <el-button type="success" @click="changeState(scope.row, '审核通过...师傅正在赶来的路上')" :disabled="scope.row.state !== '待审核'">审核通过</el-button>
    <el-button type="danger" @click="changeState(scope.row, '审核不通过')" :disabled="scope.row.state !== '待审核'">审核不通过</el-button>
</template>
</el-table-column>3.后台管理

https://img2023.cnblogs.com/other/1218593/202310/1218593-20231017102510089-901279121.png
4.后台管理核心代码

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Result;
import com.example.demo.entity.Audit;
import com.example.demo.entity.User;
import com.example.demo.mapper.FileMapper;
import com.example.demo.service.IAuditService;
import com.example.demo.utils.TokenUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@CrossOrigin
@RestController
@RequestMapping("/audit")
public class AuditController {

    @Resource
    private IAuditService auditService;

    @Resource
    private FileMapper fileMapper;

//    //新增或者更新
//    @PostMapping
//    public Result save(@RequestBody Audit audit) {
//      audit.setUser(TokenUtils.getCurrentUser().getUsername());
      audit.setImg(Files.url);
//      return Result.success(auditService.saveOrUpdate(audit));
//    }

    // 新增或者更新
    @PostMapping
    public Result save(@RequestBody Audit audit) {
      if (audit.getId() == null) {
            // 新增
            audit.setUser(TokenUtils.getCurrentUser().getUsername());
      }
      auditService.saveOrUpdate(audit);
      return Result.success();
    }

    //删除
//    @DeleteMapping("/{id}")
//    public Result delete(@PathVariable Integer id) {
//      return Result.success(userService.removeById(id));
//    }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {//批量删除
      return Result.success(auditService.removeByIds(ids));
    }

    //查询所有数据
    @GetMapping
    public Result findAll() {
      return Result.success(auditService.list());
    }

    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
      return Result.success(auditService.getById(id));
    }

    @GetMapping("/username/{username}")
    public Result findByUsername(@PathVariable String username) {
      QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
      queryWrapper.eq("username", username);
      return Result.success(auditService.getOne(queryWrapper));
    }

    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {
      QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
      queryWrapper.orderByDesc("id");
      if (!"".equals(name)) {
            queryWrapper.like("name", name);
      }
      User currentUser = TokenUtils.getCurrentUser();
//      if (RoleEnum.ROLE_USER.toString().equals(currentUser.getRole())) {// 角色是普通用户
//            queryWrapper.eq("user", currentUser.getUsername());
//      }
      return Result.success(auditService.page(new Page<>(pageNum, pageSize), queryWrapper));

    }
}5.vue前台完整代码

(1)、前台功能页面
前台负责新增请求,然后保存请求之后,我们管理员审核通过之后就不可以编辑和删除我们的请求,我们会保留数据在前台页面
<template>

   
      <el-inputplaceholder="请输入报修描述" clearable v-model="name" ></el-input>
      <el-buttontype="primary" @click="load"><i/>搜索</el-button>
      <el-button type="warning" @click="reset"><i/>刷新</el-button>
   
   
      <el-button type="primary" @click="handleAdd" ><i/>新增</el-button>
      <el-popconfirm
         
          confirm-button-text='确认'
          cancel-button-text='取消'
          icon="el-icon-info"
          icon-color="red"
          title="确定批量删除这些信息吗?"
          @confirm="delBatch">
      <el-button type="danger" slot="reference" ><i/>删除</el-button>
      </el-popconfirm>
   
    <el-table :data="tableData" border stripe :header-cell-class-name="headerBg"
            @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="name" label="报修描述" ></el-table-column>
      <el-table-column prop="user" label="用户" ></el-table-column>
      <el-table-column prop="createTime" label="创建时间" ></el-table-column>
      <el-table-column label="图片">
      <template slot-scope="scope">
          <el-image:src="scope.row.img" :preview-src-list=""></el-image>
      </template>
      </el-table-column>
      <el-table-column prop="state" label="进度"></el-table-column>
      <el-table-column label="操作">
      <template slot-scope="scope">
          <el-button type="success" @click="handleEdit(scope.row)" :disabled="scope.row.state !== '待审核'"><i/>编辑</el-button>
      </template>
      </el-table-column>
    </el-table>
   
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[ 5, 10, 15]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
   
    <el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
      <el-form label-width="100px" size="small">
      <el-form-item label="报修描述" >
          <el-input v-model="form.name" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="物品图片">
          <el-upload action="http://localhost:9090/file/upload" ref="img" :on-success="handleImgUploadSuccess">
            <el-button size="small" type="primary">点击上传</el-button>
          </el-upload>
      </el-form-item>
      </el-form>
      
      <el-button @click="dialogFormVisible = false">取 消</el-button>
      <el-button type="primary" @click="save">确 定</el-button>
      
    </el-dialog>

</template>


(2)、后台管理功能页面
<template>






   

      <el-popconfirm
         
          confirm-button-text='确认'
          cancel-button-text='取消'
          icon="el-icon-info"
          icon-color="red"
          title="确定批量删除这些信息吗?"
          @confirm="delBatch">
      <el-button type="danger" slot="reference" ><i/>删除</el-button>
      </el-popconfirm>
   
    <el-table :data="tableData" border stripe :header-cell-class-name="headerBg"
            @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="name" label="报修描述" ></el-table-column>
      <el-table-column prop="user" label="用户" ></el-table-column>
      <el-table-column prop="createTime" label="创建时间" ></el-table-column>
      <el-table-column prop="img" label="详情图片" >
      <template slot-scope="scope">
          <el-image:src="scope.row.img" :preview-src-list=""></el-image>
      </template>
      </el-table-column>
      <el-table-column prop="state" label="进度"></el-table-column>
      <el-table-column label="审核" width="240">
      <template v-slot="scope">
          <el-button type="success" @click="changeState(scope.row, '审核通过...师傅正在赶来的路上')" :disabled="scope.row.state !== '待审核'">审核通过</el-button>
          <el-button type="danger" @click="changeState(scope.row, '审核不通过')" :disabled="scope.row.state !== '待审核'">审核不通过</el-button>
      </template>
      </el-table-column>
    </el-table>
   
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[ 5, 10, 15]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
   


</template>


重点!!!!图片上传
https://img2023.cnblogs.com/other/1218593/202310/1218593-20231017102510351-1651763688.png
核心代码
CREATE TABLE `file` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名称',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件类型',
`size` bigint DEFAULT NULL COMMENT '文件大小(kb)',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载链接',
`md5` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件md5',
`creat_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '时间',
`is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Constants;
import com.example.demo.common.Result;
import com.example.demo.entity.Files;
import com.example.demo.mapper.FileMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

@RestController
@RequestMapping("/file")
public class FileController {

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @Value("${server.ip}")
    private String serverIp;

    @Resource
    private FileMapper fileMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
      String originalFilename = file.getOriginalFilename();
      String type = FileUtil.extName(originalFilename);
      long size = file.getSize();

      // 定义一个文件唯一的标识码
      String fileUUID = IdUtil.fastSimpleUUID() + StrUtil.DOT + type;

      File uploadFile = new File(fileUploadPath + fileUUID);
      // 判断配置的文件目录是否存在,若不存在则创建一个新的文件目录
      File parentFile = uploadFile.getParentFile();
      //判断目录是否存在,不存在就新建
      if (!parentFile.exists()) {
             parentFile.mkdirs();
      }
      String url;
      // 获取文件的md5
      String md5 = SecureUtil.md5(file.getInputStream());
      // 从数据库查询是否存在相同的记录
      Files dbFiles = getFileByMd5(md5);
      if (dbFiles != null) {
            url = dbFiles.getUrl();
      } else {
            // 上传文件到磁盘
            file.transferTo(uploadFile);
            // 数据库若不存在重复文件,则不删除刚才上传的文件
            url = "http://" + serverIp + ":9090/file/" + fileUUID;
      }
      //存储到数据库
      Files saveFile = new Files();
      saveFile.setName(originalFilename);
      saveFile.setType(type);
      saveFile.setSize(size/1024);
      saveFile.setUrl(url);
      saveFile.setMd5(md5);
      fileMapper.insert(saveFile);
      return url;

//      String md5 = SecureUtil.md5(file.getInputStream());
//      Files files = getFileByMd5(md5);
//
//      String url;
//      if (files != null) {
//            url = files.getUrl();
//      } else {
//            file.transferTo(uploadFile);
//            url = "http://localhost:9090/file/" + fileUUID;
//      }
//      //存储到数据库
//      Files saveFile = new Files();
//      saveFile.setName(originalFilename);
//      saveFile.setType(type);
//      saveFile.setSize(size/1024);
//      saveFile.setUrl(url);
//      saveFile.setMd5(md5);
//      fileMapper.insert(saveFile);
//      return url;
    }


    @GetMapping("/{fileUUID}")
    public void download(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {
      // 根据文件的唯一标识码获取文件
      File uploadFile = new File(fileUploadPath + fileUUID);
      // 设置输出流的格式
      ServletOutputStream os = response.getOutputStream();
      response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));
      response.setContentType("application/octet-stream");

      // 读取文件的字节流
      os.write(FileUtil.readBytes(uploadFile));
      os.flush();
      os.close();
    }

    /**
   * 通过文件的md5查询文件
   * @param md5
   * @return
   */
    private Files getFileByMd5(String md5) {
      // 查询文件的md5是否存在
      QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
      queryWrapper.eq("md5", md5);
      List<Files> filesList = fileMapper.selectList(queryWrapper);
      return filesList.size() == 0 ? null : filesList.get(0);
    }

    //    @CachePut(value = "files", key = "'frontAll'")
    @PostMapping("/update")
    public Result update(@RequestBody Files files) {
      fileMapper.updateById(files);
      flushRedis(Constants.FILES_KEY);
      return Result.success();
    }

    @GetMapping("/detail/{id}")
    public Result getById(@PathVariable Integer id) {
      return Result.success(fileMapper.selectById(id));
    }

    //清除一条缓存,key为要清空的数据
//    @CacheEvict(value="files",key="'frontAll'")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
      Files files = fileMapper.selectById(id);
      files.setIsDelete(true);
      fileMapper.updateById(files);
      flushRedis(Constants.FILES_KEY);
      return Result.success();
    }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
      // select * from sys_file where id in (id,id,id...)
      QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
      queryWrapper.in("id", ids);
      List<Files> files = fileMapper.selectList(queryWrapper);
      for (Files file : files) {
            file.setIsDelete(true);
            fileMapper.updateById(file);
      }
      return Result.success();
    }

    /**
   * 分页查询接口
   * @param pageNum
   * @param pageSize
   * @param name
   * @return
   */
    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {

      QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
      // 查询未删除的记录
      queryWrapper.eq("is_delete", false);
      queryWrapper.orderByDesc("id");
      if (!"".equals(name)) {
            queryWrapper.like("name", name);
      }
      return Result.success(fileMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper));
    }

    // 删除缓存
    private void flushRedis(String key) {
      stringRedisTemplate.delete(key);
    }
}小结

以上就是对怎么利用SpringBoot实现审核功能简单的概述,让我们更加了解SpringBoot的作用,为我们的知识储备又加上一笔。
来源:blog.csdn.net/weixin_65950231/article/details/128756143
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Spring Boot 实现审核功能,实战来了!