一、审核功能实现的方式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后端
其实审核功能最主要的就是我们的新增功能,用户只有新增过后,我们的管理员才能去对你的申请进行审核,最后实现效果。
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 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 queryWrapper = new QueryWrapper();// queryWrapper.eq("role", role);// List 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 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 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.实现效果
2.核心代码
审核通过 审核不通过
3.后台管理
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 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 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 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)、前台功能页面
前台负责新增请求,然后保存请求之后,我们管理员审核通过之后就不可以编辑和删除我们的请求,我们会保留数据在前台页面
搜索 刷新 新增 删除 编辑 点击上传 取 消 确 定 export default { name: "Audit", data() { return { tableData: [], total: 0, pageNum: 1, pageSize: 5, name: "", form: {}, dialogFormVisible: false, multipleSelection: [], headerBg: "headerBg", roles: [], user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {} } }, created() { this.load() }, methods: { load: function () { this.request.get("/audit/page", { params: { pageNum: this.pageNum, pageSize: this.pageSize, name: this.name, } }).then(res => { this.tableData = res.data.records this.total = res.data.total }) // this.request.get("/role").then(res => { // this.roles = res.data // }) }, home() { this.$router.push("/") }, save() { this.request.post("/audit", this.form).then(res => { if (res.code === '200') { this.$message.success("保存成功") this.dialogFormVisible = false this.load() } else { this.$message.error("保存失败") } }) }, handleAdd() { this.dialogFormVisible = true this.form = {} }, handleEdit(row) { this.form = row this.dialogFormVisible = true }, handleSelectionChange(val) { console.log(val) this.multipleSelection = val; }, delBatch() { let ids = this.multipleSelection.map(v => v.id) //[{}, {}, {}] => [1,2,3] this.request.post("/audit/del/batch", ids).then(res => { if (res.code === '200') { this.$message.success("删除信息成功") this.load() } else { this.$message.error("删除信息失败") } }) }, reset() { this.name = "" this.load() }, handleSizeChange(pageSize) { console.log(pageSize) this.pageSize = pageSize this.load() }, handleCurrentChange(pageNum) { console.log(pageNum) this.pageNum = pageNum this.load() }, handleImgUploadSuccess(res) { this.form.img = res }, }} .headerBg { background: #eee!important;}(2)、后台管理功能页面
<!-- --><!-- --><!-- 搜索--><!-- 刷新--> <!-- 新增--> 删除 审核通过 审核不通过 export default { name: "Audit", data() { return { tableData: [], total: 0, pageNum: 1, pageSize: 5, username: "", form: {}, // dialogFormVisible: false, multipleSelection: [], headerBg: "headerBg", roles: [], user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}, } }, created() { this.load() }, methods: { load: function () { this.request.get("/audit/page", { params: { pageNum: this.pageNum, pageSize: this.pageSize, username: this.username, } }).then(res => { this.tableData = res.data.records this.total = res.data.total }) this.request.get("/role").then(res => { this.roles = res.data }) }, home() { this.$router.push("/") }, save() { this.request.post("/audit", this.form).then(res => { if (res.code === '200') { this.$message.success("保存成功") this.dialogFormVisible = false this.load() } else { this.$message.error("保存失败") } }) }, handleAdd() { this.dialogFormVisible = true this.form = {} }, handleEdit(row) { this.form = row this.dialogFormVisible = true }, handleSelectionChange(val) { console.log(val) this.multipleSelection = val; }, delBatch() { let ids = this.multipleSelection.map(v => v.id) //[{}, {}, {}] => [1,2,3] this.request.post("/audit/del/batch", ids).then(res => { if (res.code === '200') { this.$message.success("删除信息成功") this.load() } else { this.$message.error("删除信息失败") } }) }, reset() { this.username = "" this.load() }, handleSizeChange(pageSize) { console.log(pageSize) this.pageSize = pageSize this.load() }, handleCurrentChange(pageNum) { console.log(pageNum) this.pageNum = pageNum this.load() }, changeState(row, state) { this.form = JSON.parse(JSON.stringify(row)) this.form.state = state; this.save(); }, // handleImgUploadSuccess() { // this.$message.success("图片上传成功") // this.load() // }, }} .headerBg { background: #eee!important;}重点!!!!图片上传
核心代码
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 queryWrapper = new QueryWrapper(); queryWrapper.eq("md5", md5); List 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 ids) { // select * from sys_file where id in (id,id,id...) QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.in("id", ids); List 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 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的作用,为我们的知识储备又加上一笔。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!