当前位置: 首页 > article >正文

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

前言

        先简单交代和阐述一下业务背景和逻辑,该系统是一个综合类的音乐系统,上传音乐时,逻辑和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 转 Vo
        List<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 转 Vo

        ApplyAlbumVo 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
    @Transactional
    public Result approvalMusicApply(ApprovalMusicApplyDto dto) {

        if (BeanUtil.isEmpty(dto)) return Result.error("参数不能为空");
        String des = dto.getDes();
        Integer toStatus = dto.getToStatus();
        // 申请id
        Integer 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
    @Transactional
    public 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.kler.cn/a/461241.html

相关文章:

  • websocket-sharp:.NET平台上的WebSocket客户端与服务器开源库
  • 数字PWM直流调速系统设计(论文+源码)
  • Leetcode打卡:我的日程安排表II
  • Ansys Discovery 中的网格划分方法:探索模式
  • k8s系列--docker拉取镜像导入k8s的containerd中
  • 物联网控制期末复习
  • 【电机控制】基于STC8H1K28的六步换向——方波驱动(软件篇)
  • 跨年烟花C++代码
  • INT303 Big Data Analytics 笔记
  • 单元测试学习2.0+修改私有属性
  • 用VSCode+远程拉仓库上传Git仓库方法(进阶版)
  • [算法] [leetcode-70] 爬楼梯
  • 8086汇编(16位汇编)学习笔记06.串操作、流程转移指令
  • 《鸿蒙之光HarmonyOS NEXT原生应用开发入门》简介
  • Synopsys软件基本使用方法
  • deepin系统Docker使用指南:常用命令精讲
  • 建筑机器人崛起 | KMDA-7611助力智能喷涂一体机器人
  • 【数据结构】单向循环链表的使用
  • 01-2023年上半年软件设计师考试java真题解析
  • 小程序 手写tab超出滑动。view超出可以横滑动
  • Kafka高性能设计
  • 手写顺序流程图组件
  • Windows onnxruntime编译openvino
  • 部分开源数据整理
  • win32汇编环境下,对话框程序中生成listview列表控件,点击标题栏自动排序的示例
  • STM32 高级 物联网通讯之LoRa通讯