管理系统中经典审核功能实现

前言

        先简单交代和阐述一下业务背景和逻辑,该系统是一个综合类的音乐系统,上传音乐时,逻辑和qq音乐一样,前端页面就能体现出大概逻辑,如下图所示:

 专辑和歌曲是密不可分的,而且歌曲的封面就是对应专辑的封面,提交审核的时候,也是同理,是一起打包提交的,sql层如下图所示:

设计

 由于后台管理系统,qq音乐的后台不予开放,那就自己设计一下,初步设想是这样:

  1. 有一个整体的 通过/拒绝通过 按钮,然后有一个整体的备注(必填)
  2. 每个歌曲都可以去听,展示其信息,都有对应的 通过/拒绝通过 按钮,而且必须给注释
  3. 整体通过与局部拒绝互斥,整体拒绝与局部通过互斥,没有全选功能,必须一个一个选

后端的话,

  1. 需要建一个专辑对应的log表,来储存一些操作信息,比如管理员的id,操作时间,操作类型等等
  2. 再建音乐的log表,来储存其备注等等
  3. 并修改申请表中的状态
  4. 提供其需要接口

逻辑大致如上。

实现

SQL实现

先建两个log表

 

由于业务逻辑是一次提交处理,本质上专辑的审核和歌曲的审核是父与子的关系,所以直接绑定一个专辑处理的id即可,音乐信息肯定不能去正式音乐表里拿,要从申请的音乐表里拿(这里面是还未通过审核的)

UI设计

动态实现,非静态

后端接口实现

歌曲栏就参考这个ui,给专辑音乐申请的时候,直接写入数据库即可,接口较为简单,下面为实现代码:

    public Result applyMusic(ApplyMusicListDto dto) {// 向数据库里开始插入信息,先校验参数boolean isEmpty = BeanUtil.isEmpty(dto);if (isEmpty) return Result.error("参数不许为空!");// 有两个表 所以分两个表进行插入try {List<ApplyMusicDto> musicList = dto.getMusicList();ApplyAlbumPo applyAlbumPo = new ApplyAlbumPo();applyAlbumPo.setAlbumId(dto.getAlbumId());applyAlbumPo.setMusicCount(musicList.size());applyAlbumPo.setApplyTime(LocalDateTime.now());applyAlbumMapper.insert(applyAlbumPo);applyMapper.insertAlbumMusic(applyAlbumPo.getId(), musicList);} catch (Exception e) {return Result.error("一个专辑内的歌曲不能重名");}// 因为已经提交 还要删除草稿箱的 数据,其实草稿箱使用redis实现更好 但是无所谓了draftMapper.delDraftMusicList(dto.getAccount());return Result.ok();}

然后就是申请处理部分,分为两种请求get和put,get主要就是获取需要处理的专辑列表,和查询详细专辑信息 这两个接口,put请求也分为两个,处理整张专辑、处理单个音乐,里面的业务要求大概下面几点:

  1. 处理专辑和处理音乐必须分开,保证处理的具体性,必须每个操作都有批注
  2. 必须所有音乐处理完后,才能处理整张专辑,而且当有音乐未通过时,整张专辑不能审批通过,只能审批拒绝。
  3. 由于目前没有超级管理员分配任务的功能,管理员间可能出现处理冲突情况,后端使用update的排他读,当处理时(条件where status = 0)发现处理的个数为0,就是说明已经被别人处理,就会让前端进行同步处理(重新获取数据)。

然后就直接列出代码

分页列表查询

public Result queryAlbumPages(Integer status, Integer styleId,LocalDateTime startDate, LocalDateTime endDate, int page, int size) {// 去分页查询一下专辑LambdaQueryWrapper<ApplyAlbumPo> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(status != null, ApplyAlbumPo::getStatus, status).ge(startDate != null, ApplyAlbumPo::getApplyTime, startDate).le(endDate != null, ApplyAlbumPo::getApplyTime, endDate);queryWrapper.orderByDesc(ApplyAlbumPo::getApplyTime);// 分页查询Page<ApplyAlbumPo> resultPage = new Page<>(page, size);Page<ApplyAlbumPo> poPage = applyAlbumMapper.selectPage(resultPage, queryWrapper);// Po 转 VoList<ApplyAlbumVo> voList = poPage.getRecords().stream().map(this::convertPoToVo) // 转换方法.collect(Collectors.toList());// 获取专辑id的列表List<Integer> idList = poPage.getRecords().stream().map(ApplyAlbumPo::getAlbumId).collect(Collectors.toList());// 查询到对应专辑的信息List<AlbumBo> albumBos = musicClient.pageAlbumByIdList(idList);for (int i = 0; i < voList.size(); i++) {BeanUtil.copyProperties(albumBos.get(i), voList.get(i));}// 包装返回结果Page<ApplyAlbumVo> voPage = new Page<>();voPage.setRecords(voList);voPage.setCurrent(poPage.getCurrent());voPage.setSize(poPage.getSize());voPage.setTotal(poPage.getTotal());return Result.ok(voPage);}

逻辑比较简单就是需要一些联表操作,这个接口里遇到了跨模块(跨库) 不能再使用SQL层面的联表了,就直接合并对象进行返回即可。

查询具体专辑信息

public Result getApplyAlbumInfoById(Integer id) {if (id == null) return Result.error("G了");ApplyAlbumPo applyAlbumPo = applyAlbumMapper.selectById(id);// Po 转 VoApplyAlbumVo applyAlbumVo = BeanUtil.copyProperties(applyAlbumPo, ApplyAlbumVo.class);// 获取专辑id的列表List<Integer> idList = Collections.singletonList(applyAlbumVo.getAlbumId());// 查询到对应专辑的信息List<AlbumBo> albumBos = musicClient.pageAlbumByIdList(idList);BeanUtil.copyProperties(albumBos.get(0), applyAlbumVo);LambdaQueryWrapper<ApprovalLogAlbumPo> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(ApprovalLogAlbumPo::getApplyAlbumId, id);ApprovalLogAlbumPo approvalLogAlbumPo = approvalLogAlbumMapper.selectOne(queryWrapper);if (approvalLogAlbumPo != null) {// 如果有处理记录AdminPo adminPo = adminMapper.selectById(approvalLogAlbumPo.getAdminId());applyAlbumVo.setApplyDes(approvalLogAlbumPo.getDes());applyAlbumVo.setAdminEmail(adminPo.getEmail());}List<ApplyMusicPo> applyMusicList = applyMusicMapper.selectApplyMusicVoList(id);for (ApplyMusicPo item : applyMusicList) {if (item.getDuration() != null) {int temp = Integer.parseInt(item.getDuration());int minutes = temp / 60;int seconds = temp % 60;// 使用String.format来格式化分钟和秒数,确保它们都是两位数item.setDuration(String.format("%02d:%02d", minutes, seconds));}}applyAlbumVo.setMusicList(applyMusicList);return Result.ok(applyAlbumVo);}

这个也比较简单,和上个查询接口基本同理,就是联的表更多了一点,以及由于feign的查询接口时list类型,用一个对象包装了一下,没有其他的难点了。

音乐审批接口

    @Override@Transactionalpublic Result approvalMusicApply(ApprovalMusicApplyDto dto) {if (BeanUtil.isEmpty(dto)) return Result.error("参数不能为空");String des = dto.getDes();Integer toStatus = dto.getToStatus();// 申请idInteger applyId = dto.getId();if (StringUtils.isBlank(des)) {return Result.error("批注不能为空");}Integer adminId = Integer.valueOf(String.valueOf(session.getAttribute("id")));// 前面和处理音乐人审批逻辑大致相同,只是根据通过和拒绝不同,对专辑状态也进行操作,通过不操作,但拒绝的话,转移也要改成拒绝状态// rr级别 去用update操作一下,where是排他锁,可以查到最新数据LambdaUpdateWrapper<ApplyMusicPo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(ApplyMusicPo::getStatus, toStatus).eq(ApplyMusicPo::getId, applyId).eq(ApplyMusicPo::getStatus, 0);int updateCount = applyMusicMapper.update(updateWrapper);if (updateCount == 0) {// 说明一定被人改变了,而且是已经提交了的,因为特别这需要考虑专辑的状态和音乐的状态,所以这里就不过度增加代码复杂度了// 直接前端刷新一下获取最新数据即可return Result.error("无法执行该操作,已经被其他人处理");}// 在音乐处理日志表插入一条信息ApprovalLogMusicPo approvalLogMusicPo = new ApprovalLogMusicPo();approvalLogMusicPo.setApplyMusicId(applyId);approvalLogMusicPo.setApprovalType(toStatus.byteValue());approvalLogMusicPo.setApprovalTime(LocalDateTime.now());approvalLogMusicPo.setDes(des);approvalLogMusicPo.setAdminId(adminId);approvalLogMusicMapper.insert(approvalLogMusicPo);return Result.ok();}

就简单分为四段,校验参数阶段,执行业务兼判断是否有权限阶段(合在了一起),插入日志阶段

专辑审批接口

    @Override@Transactionalpublic Result approvalAlbumApply(ApprovalAlbumApplyDto dto) {if (BeanUtil.isEmpty(dto)) {return Result.error("参数不能为空");}String des = dto.getDes();Integer applyId = dto.getId();Integer toStatus = dto.getToStatus();if (StringUtils.isBlank(des)) {return Result.error("批注不能为空");}Integer adminId = Integer.valueOf(String.valueOf(session.getAttribute("id")));// 要求过滤一下,到底能不能进行该通过或拒绝操作LambdaQueryWrapper<ApplyMusicPo> queryWrapper1 = Wrappers.lambdaQuery();queryWrapper1.eq(ApplyMusicPo::getApplyAlbumId, applyId).eq(ApplyMusicPo::getStatus, 0);// 看未处理的有多少Long unDoCount = applyMusicMapper.selectCount(queryWrapper1);if (unDoCount != 0) {// 如果有未处理的话return Result.error("必须审批完全部音乐,才可以审批整张专辑");}// 如果想要通过一张专辑,它的所有音乐必须全部通过if (toStatus == 1) {LambdaQueryWrapper<ApplyMusicPo> queryWrapper2 = Wrappers.lambdaQuery();queryWrapper2.eq(ApplyMusicPo::getApplyAlbumId, applyId).eq(ApplyMusicPo::getStatus, 2);Long doRejectCount = applyMusicMapper.selectCount(queryWrapper1);if (doRejectCount > 0) {// 有音乐是拒绝状态return Result.error("该专辑无法通过,因为有音乐未通过,请重新审批并给上整张专辑的批注");}}// 筛完 然后就可以进行业务操作了// rr级别 去用update操作一下,where是排他锁,可以查到最新数据LambdaUpdateWrapper<ApplyAlbumPo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(ApplyAlbumPo::getStatus, toStatus).eq(ApplyAlbumPo::getId, applyId).eq(ApplyAlbumPo::getStatus, 0);int updateCount = applyAlbumMapper.update(updateWrapper);if (updateCount == 0) {// 说明一定被人改变了,而且是已经提交了的,因为特别这需要考虑专辑的状态和音乐的状态,所以这里就不过度增加代码复杂度了// 直接前端刷新一下获取最新数据即可return Result.error("无法执行该操作,帖子已经被其他人处理");}// 在音乐处理日志表插入一条信息ApprovalLogAlbumPo approvalLogAlbumPo = new ApprovalLogAlbumPo();approvalLogAlbumPo.setApplyAlbumId(applyId);approvalLogAlbumPo.setApprovalType(toStatus.byteValue());approvalLogAlbumPo.setApprovalTime(LocalDateTime.now());approvalLogAlbumPo.setDes(des);approvalLogAlbumPo.setAdminId(adminId);approvalLogAlbumMapper.insert(approvalLogAlbumPo);return Result.ok();}

这个比上一条多一点逻辑,但也是四步走,校验参数阶段,校验权限、执行业务阶段(由于需要根据update结果判断权限,所以这两个阶段还是比较耦合),插入日志阶段

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/65747.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《机器学习》——利用OpenCV库中的KNN算法进行图像识别

文章目录 KNN算法介绍下载OpenCV库实验内容实验结果完整代码手写数字传入模型训练 KNN算法介绍 一、KNN算法的基本要素 K值的选择&#xff1a;K值代表选择与新测试样本距离最近的前K个训练样本数&#xff0c;通常K是不大于20的整数。K值的选择对算法结果有重要影响&#xff0c…

[服务器][教程]Ubuntu24.04 Server开机自动挂载硬盘教程

1. 查看硬盘ID ls -l /dev/disk/by-uuid可以看到对应的UUID所对应的分区 2. 创建挂载文件夹 创建好文件夹即可 3. 修改配置文件 sudo vim /etc/fstab把对应的UUID和创建的挂载目录对应即可 其中# Personal mount points下面的是自己新添加的 &#xff1a;分区定位&#xff…

惠州市政数局局长杨伟斌:惠州市公共数据授权运营模式探索

近期&#xff0c;2024数字资产管理大会召开。会上&#xff0c;惠州市政务服务和数据管理局局长杨伟斌在会上做了题为基于“隐私计算区块链”的惠州市公共数据授权运营模式探索主旨演讲&#xff0c;从三个方面展开&#xff0c;一是建制度汇数据&#xff0c;二是夯基础保安全&…

查看 GitHub 仓库的创建时间

查看 GitHub 仓库的创建时间 1. https://api.github.com/repos/{owner}/{repository}2. curl -s https://api.github.com/repos/{owner}/{repository} | jq .created_atReferences 1. https://api.github.com/repos/{owner}/{repository} REST API endpoints for repositories…

ArcGIS中怎么进行水文分析?(思路介绍)

最近有人咨询&#xff0c;ArcGIS中怎么进行水文分析&#xff0c;大致的说一下河网提取的思路哈 解决思路&#xff1a;dem填洼→计算水流方向→计算水流累积矩阵→形成河网 dem填洼 计算水流方向 计算水流累积矩阵 用栅格计算器&#xff0c;设阈值&#xff08;自己多次尝试&…

自动化测试-Pytest测试

目录 pytest简介 基本测试实例 编写测试文件 执行测试 pytest运行时参数 mark标记 Fixture pytest插件 Allure测试报告 测试步骤 pytest简介 Pytest‌是一个非常流行的Python测试框架&#xff0c;它支持简单的单元测试和复杂的功能测试&#xff0c;具有易于上手、功…

2、redis的持久化

redis的持久化 在redist当中&#xff0c;高可用的技术包括持久化&#xff0c;主从复制&#xff0c;哨兵模式&#xff0c;集群。 持久化是最简单的高可用的方法&#xff0c;作用就是备份数据。即将数据保存到硬盘&#xff0c;防止进程退出导致数据丢失。 redis持久化方式&…

[论文阅读] (34)ESWA2024 基于SGDC的轻量级入侵检测系统

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0c;非常欢迎大家给我留言评论&#xff0c;学术路上期…

深入解析:谱分解、SVD与PCA在算法中的应用与实现

特征值分解&#xff08;EVD&#xff09;、奇异值分解&#xff08;SVD&#xff09;和主成分分析&#xff08;PCA&#xff09;是矩阵分解技术的三种重要形式&#xff0c;它们在人工智能中扮演了关键角色。随着数据维度的快速增长和信息复杂度的提升&#xff0c;这些技术为处理高维…

[2025] 如何在 Windows 计算机上轻松越狱 IOS 设备

笔记 1. 首次启动越狱工具时&#xff0c;会提示您安装驱动程序。单击“是”确认安装&#xff0c;然后再次运行越狱工具。 2. 对于Apple 6s-7P和iPad系列&#xff08;iOS14.4及以上&#xff09;&#xff0c;您应该点击“Optinos”并勾选“允许未经测试的iOS/iPadOS/tvOS版本”&…

【笔记】在虚拟机中通过apache2给一个主机上配置多个web服务器

&#xff08;配置出来的web服务器又叫虚拟主机……&#xff09; 下载apache2 sudo apt update sudo apt install apache2 &#xff08;一&#xff09;ip相同 web端口不同的web服务器 进入 /var/www/html 创建站点一和站点二的目录文件&#xff08;目录文件名自定义哈&#x…

vim里搜索关键字

vim是linux文本编辑器的命令&#xff0c;再vi的基础上做了功能增强 使用方法如下 1. / 关键字, 回车即可, 按n键查找关键字下一个位置 2.? 关键字, 回车即可, 按n键查找关键字下一个位置 3.示例

小程序中引入echarts(保姆级教程)

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

15-利用dubbo远程服务调用

本文介绍利用apache dubbo调用远程服务的开发过程&#xff0c;其中利用zookeeper作为注册中心。关于zookeeper的环境搭建&#xff0c;可以参考我的另一篇博文&#xff1a;14-zookeeper环境搭建。 0、环境 jdk&#xff1a;1.8zookeeper&#xff1a;3.8.4dubbo&#xff1a;2.7.…

一个最简单的ios程序(object_c)的编写

前言 如何在苹果系统MacOS创建一个简单的ios&#xff08;iphone&#xff09;程序&#xff0c;貌似非常的简单。但是&#xff0c;作为习惯了Windows开发的程序员来说&#xff0c;有时候还觉得有点麻烦&#xff0c;至少开始有点很不习惯。 本博文试着把这个过程展现一下&#xff…

Rabbitmq追问2

分析rabbitmq 默认使用姿势是什么 direct fanout还是什么 public void convertAndSend(String exchange, String routingKey, Object object, CorrelationData correlationData) throws AmqpException { this.send(exchange, routingKey, this.convertMessageIfNecessary(obje…

[文献阅读]ReAct: Synergizing Reasoning and Acting in Language Models

文章目录 摘要Abstract:思考与行为协同化Reason(Chain of thought)ReAct ReAct如何协同推理 响应Action&#xff08;动作空间&#xff09;协同推理 结果总结 摘要 ReAct: Synergizing Reasoning and Acting in Language Models [2210.03629] ReAct: Synergizing Reasoning an…

Rocky Linux下安装meld

背景介绍&#xff1a; meld是一款Linux系统下的用于 文件夹和文件的比对软件&#xff0c;非常常用&#xff1b; 故障现象&#xff1a; 输入安装命令后&#xff0c;sudo yum install meld&#xff0c;报错。 12-31 22:12:17 ~]$ sudo yum install meld Last metadata expirat…

【亚马逊云】基于Amazon EC2实例部署 NextCloud 云网盘并使用 Docker-compose 搭建 ONLYOFFICE 企业在线办公应用软件

文章目录 1. 部署EC2实例2. 安装 Docker 服务3. 安装docker-compose4. 创建Docker-compose文件5. 创建nginx.conf文件6. 运行docker-compose命令开始部署7. 访问ONLYOFFICE插件8. 访问NextCloud云盘9. 下载并启用ONLYOFFICE插件10. 上传文件测试11. 所遇问题12. 参考链接 1. 部…

揭秘文件上传漏洞之操作原理(Thoughts on File Upload Vulnerabilities)

从上传到入侵&#xff1a;揭秘文件上传漏洞之操作原理 大家好&#xff0c;今天我们来聊一个"老而弥坚"的漏洞类型 —— 文件上传漏洞。虽然这个漏洞存在很多年了&#xff0c;但直到现在依然频频出现在各种漏洞报告中。今天我们就来深入了解一下它的原理和各种校验方…