【使用redisson完成延迟队列的功能】使用redisson配合线程池完成异步执行功能,延迟队列和不需要延迟的队列

1. 使用redisson完成延迟队列的功能

引入依赖

spring-boot-starter-actuator是Spring Boot提供的一个用于监控和管理应用程序的模块
用于查看应用程序的健康状况、审计信息、指标和其他有用的信息。这些端点可以帮助你监控应用程序的运行状态、性能指标和健康状况。
已经有了其他的监控和管理工具,不需要使用Spring Boot Actuator提供的功能。

<!-- redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></exclusion></exclusions>
</dependency>

1.1 延时队列工具类

添加延迟队列时使用,监测扫描时也会用这个工具类进行获取消息

package cn.creatoo.common.redis.queue;import cn.creatoo.common.core.utils.StringUtils;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;/*** 分布式延时队列工具类* @author*/
@Component
@ConditionalOnBean({RedissonClient.class})
public class RedisDelayQueueUtil {private static final Logger log = LoggerFactory.getLogger(RedisDelayQueueUtil.class);@Resourceprivate RedissonClient redissonClient;/*** 添加延迟队列** @param value     队列值* @param delay     延迟时间* @param timeUnit  时间单位* @param queueCode 队列键* @param <T>*/public <T> boolean addDelayQueue(@NonNull T value, @NonNull long delay, @NonNull TimeUnit timeUnit, @NonNull String queueCode) {if (StringUtils.isBlank(queueCode) || Objects.isNull(value)) {return false;}try {RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);delayedQueue.offer(value, delay, timeUnit);//delayedQueue.destroy();log.info("(添加延时队列成功) 队列键:{},队列值:{},延迟时间:{}", queueCode, value, timeUnit.toSeconds(delay) + "秒");} catch (Exception e) {log.error("(添加延时队列失败) {}", e.getMessage());throw new RuntimeException("(添加延时队列失败)");}return true;}/*** 获取延迟队列** @param queueCode* @param <T>*/public <T> T getDelayQueue(@NonNull String queueCode) throws InterruptedException {if (StringUtils.isBlank(queueCode)) {return null;}RBlockingDeque<Map> blockingDeque = redissonClient.getBlockingDeque(queueCode);RDelayedQueue<Map> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);T value = (T) blockingDeque.poll();return value;}/*** 删除指定队列中的消息** @param o 指定删除的消息对象队列值(同队列需保证唯一性)* @param queueCode 指定队列键*/public boolean removeDelayedQueue(@NonNull Object o, @NonNull String queueCode) {if (StringUtils.isBlank(queueCode) || Objects.isNull(o)) {return false;}RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);boolean flag = delayedQueue.remove(o);//delayedQueue.destroy();return flag;}
}

1.2 延迟队列执行器

package cn.creatoo.system.handler;/*** 延迟队列执行器*/
public interface RedisDelayQueueHandle<T> {void execute(T t);}

1.3 实现队列执行器

实现队列执行器接口,在这里写延迟要做的业务逻辑

package cn.creatoo.system.handler.impl;import cn.creatoo.common.core.domain.vo.WaterVo;
import cn.creatoo.system.api.RemoteFileService;
import cn.creatoo.system.handler.RedisDelayQueueHandle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Map;@Component("exposeLinkCloudDelay")
public class ExposeLinkCloudDelay implements RedisDelayQueueHandle<Map> {@Autowiredprivate RemoteFileService remoteFileService;@Overridepublic void execute(Map map) {long dataId = Long.parseLong(map.get("dataId").toString());WaterVo waterVo = new WaterVo();waterVo.setFileLink(map.get("fileLink").toString());waterVo.setType(Integer.parseInt(map.get("type").toString()));waterVo.setDataId(dataId);remoteFileService.waterLink(waterVo);}
}

1.4 延迟队列业务枚举类

package cn.creatoo.common.core.enums;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;/*** 延迟队列业务枚举类* @author shang tf* @data 2024/3/21 14:52*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum FileRedisDelayQueueEnum {EXPOSE_LINK_DELAY("EXPOSE_LINK_DELAY","资源链接处理","exposeLinkDelay"),EXPOSE_LINK_CLOUD_DELAY("EXPOSE_LINK_CLOUD_DELAY","资源链接处理","exposeLinkCloudDelay"),COMPRESSED_LINK_DELAY("COMPRESSED_LINK_DELAY","文件压缩处理","compressedLinkDelay"),UPLOAD_TO_CLOUD_DELAY("UPLOAD_TO_CLOUD_DELAY","资源上传消费端","uploadToCloudDelay"),GET_HASHCODE_DELAY("GET_HASHCODE_DELAY","资源hash值获取","getHashcodeDelay"),UPLOAD_FILE_TO_CABINET("UPLOAD_FILE_CABINET","异步添加文件到数据柜","uploadFileCabinet");/*** 延迟队列 Redis Key*/private String code;/*** 中文描述*/private String name;/*** 延迟队列具体业务实现的 Bean* 可通过 Spring 的上下文获取*/private String beanId;
}

1.5 启动延迟队列监测扫描

package cn.creatoo.system.handler.impl;import cn.creatoo.common.core.enums.FileRedisDelayQueueEnum;
import cn.creatoo.common.redis.queue.RedisDelayQueueUtil;
import cn.creatoo.system.handler.RedisDelayQueueHandle;
import com.alibaba.fastjson2.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author shang tf* @data 2024/3/14 10:45* 启动延迟队列监测扫描* 文件处理的延迟队列线程池*/
@Slf4j
@Component
public class FileRedisDelayQueueRunner implements CommandLineRunner {@Autowiredprivate RedisDelayQueueUtil redisDelayQueueUtil;@Autowiredprivate ApplicationContext context;@Autowiredprivate ThreadPoolTaskExecutor ptask;@Value("${file-thread-pool.core-pool-size:1}")private int corePoolSize;@Value("${file-thread-pool.maximum-pool-size:1}")private int maximumPoolSize;private ThreadPoolExecutor executorService;/*** 程序加载配置文件后,延迟创建线程池*/@PostConstructpublic void init() {executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 30, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1000), new ThreadFactoryBuilder().setNameFormat("delay-queue-%d").build());}@Overridepublic void run(String... args) {ptask.execute(() -> {while (true) {try {FileRedisDelayQueueEnum[] queueEnums = FileRedisDelayQueueEnum.values();for (FileRedisDelayQueueEnum queueEnum : queueEnums) {Object value = redisDelayQueueUtil.getDelayQueue(queueEnum.getCode());if (value != null) {System.out.println("----------------value:" + JSON.toJSONString(value));RedisDelayQueueHandle<Object> redisDelayQueueHandle = (RedisDelayQueueHandle<Object>) context.getBean(queueEnum.getBeanId());executorService.execute(() -> {redisDelayQueueHandle.execute(value);});}}TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {log.error("(FileRedission延迟队列监测异常中断) {}", e.getMessage());}}});log.info("(FileRedission延迟队列监测启动成功)");}
}

1.6 使用延迟队列

使用时在需要延时的地方。
通过注入RedisDelayQueueUtil,使用addDelayQueue方法进行添加延迟任务。

Map<String, String> map = new HashMap<>();
map.put("dataId", examineVo.getId().toString());
map.put("fileLink", resourceLink);
map.put("type", resourceType.toString());
map.put("remark", "资源链接处理");
// 5秒后执行exposeLinkCloudDelay中的方法
redisDelayQueueUtil.addDelayQueue(map, 5, TimeUnit.SECONDS, FileRedisDelayQueueEnum.EXPOSE_LINK_CLOUD_DELAY.getCode());

在这里插入图片描述

2. 使用redisson完成不延时队列的功能

2.1 分布式队列工具类

package cn.creatoo.common.redis.queue;import cn.creatoo.common.core.utils.StringUtils;
import org.redisson.api.RBoundedBlockingQueue;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Objects;/*** 分布式队列工具类*/
@Component
@ConditionalOnBean({RedissonClient.class})
public class RedisBlockQueueUtil {private static final Logger log = LoggerFactory.getLogger(RedisBlockQueueUtil.class);@Resourceprivate RedissonClient redissonClient;//public <T> boolean addQueue(@NonNull T value, @NonNull String queueCode) {if (StringUtils.isBlank(queueCode) || Objects.isNull(value)) {return false;}try {RBoundedBlockingQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);queue.trySetCapacity(10000);queue.put(value);} catch (Exception e) {throw new RuntimeException("(添加redisson队列失败)");}return true;}/*** 获取队列* @param queueCode* @param <T>*/public <T> T getQueuePeek(@NonNull String queueCode) throws InterruptedException {if (StringUtils.isBlank(queueCode)) {return null;}RQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);T obj = (T) queue.peek();return obj;}public <T> T getQueueTake(@NonNull String queueCode) throws InterruptedException {if (StringUtils.isBlank(queueCode)) {return null;}RBoundedBlockingQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);T obj = (T) queue.take();return obj;}}

2.2 队列业务枚举

package cn.creatoo.common.core.enums;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;/*** 队列业务枚举*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum RedisQueueEnum {FLOW_RECORD("redissionQueue:FLOW_RECORD", "流量流水"),USER_LOGIN_RECORD("redissionQueue:USER_LOGIN_RECORD", "用户登录流水"),USER_REGISTER_RECORD("redissionQueue:USER_REGISTER_RECORD", "用户注册流水"),SMS_SEND_RECORD("redissionQueue:SMS_SEND_RECORD", "短信流水");/*** 队列 Redis Key*/private String code;/*** 中文描述*/private String name;}

2.3 启动队列监测扫描

package cn.creatoo.system.handler.impl;import cn.creatoo.common.core.enums.RedisQueueEnum;
import cn.creatoo.common.core.utils.StringUtils;
import cn.creatoo.common.mongodb.model.FlowStatistics;
import cn.creatoo.common.mongodb.model.MessageSendRecord;
import cn.creatoo.common.mongodb.model.UserLogin;
import cn.creatoo.common.mongodb.model.UserRegister;
import cn.creatoo.common.redis.queue.RedisBlockQueueUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** description: 启动队列监测扫描*/
@Slf4j
@Component
public class RedisQueueRunner implements CommandLineRunner {@Autowiredprivate RedisBlockQueueUtil redisBlockQueueUtil;//@Autowired//private IBdStatcountService bdStatcountService;@Autowiredprivate ThreadPoolTaskExecutor ptask;@Resourceprivate MongoTemplate mongoTemplate;//@Autowired//private BdAdminHomeService bdAdminHomeService;@Value("${prodHost.mall}")private String mallHost;ThreadPoolExecutor executorService = new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1000),new ThreadFactoryBuilder().setNameFormat("queue-%d").build());@Overridepublic void run(String... args) throws Exception {ptask.execute(() -> {while (true){try {RedisQueueEnum[] queueEnums = RedisQueueEnum.values();for (RedisQueueEnum queueEnum : queueEnums) {Object value = redisBlockQueueUtil.getQueuePeek(queueEnum.getCode());if (value != null) {executorService.execute(() -> {try {//System.out.println(value.toString());if(queueEnum.getCode().equals(RedisQueueEnum.FLOW_RECORD.getCode())){FlowStatistics flowStatistics = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());/* if(flowStatistics!=null && StringUtils.isNotBlank(flowStatistics.getUrl())){mongoTemplate.insert(flowStatistics, "pv_" + new SimpleDateFormat("yyyy").format(new Date()));// 添加首页统计缓存bdAdminHomeService.addDetailCache(flowStatistics);if(StringUtils.isNotBlank(flowStatistics.getUrl())){bdStatcountService.browseByUrl(flowStatistics.getUrl());}}*/} else if (queueEnum.getCode().equals(RedisQueueEnum.USER_LOGIN_RECORD.getCode())) {UserLogin userLogin = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());mongoTemplate.insert(userLogin, "user_login_" + new SimpleDateFormat("yyyy").format(new Date()));} else if (queueEnum.getCode().equals(RedisQueueEnum.USER_REGISTER_RECORD.getCode())) {UserRegister userRegister = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());mongoTemplate.insert(userRegister, "user_register");} else if (queueEnum.getCode().equals(RedisQueueEnum.SMS_SEND_RECORD.getCode())) {MessageSendRecord sendRecord = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());mongoTemplate.insert(sendRecord, "sms_send_" + new SimpleDateFormat("yyyy").format(new Date()));}} catch (InterruptedException e) {log.error("(Redission队列监测异常中断) {}", e.getMessage());}});}}TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {log.error("(Redission队列监测异常中断) {}", e.getMessage());}}});log.info("(Redission队列监测启动成功)");}
}

2.4 使用

这个是直接执行,没有延迟的功能

   redisBlockQueueUtil.addQueue(userRegister, RedisQueueEnum.USER_REGISTER_RECORD.getCode());

在这里插入图片描述

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

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

相关文章

微信小程序 canvas层级过高覆盖原生组件

一、背景 微信小程序中使用signature第三方插件完成签名效果&#xff0c;但真机调试时发现canvas层级过高遮挡了按钮 二、具体问题 问题原因&#xff1a;签名后点击按钮无法生效 问题代码&#xff1a; <template><view class"sign_page" v-cloak>&l…

排序算法记录(冒泡+快排+归并)

文章目录 前言冒泡排序快速排序归并排序 前言 冒泡 快排 归并&#xff0c;这三种排序算法太过经典&#xff0c;但又很容易忘了。虽然一开始接触雀氏这些算法雀氏有些头大&#xff0c;但时间长了也还好。主要是回忆这些算法干了啥很耗时间。 如果在笔试时要写一个o(nlogn)的…

React【Day1】

B站视频链接 一、React介绍 React由Meta公司开发&#xff0c;是一个用于 构建Web和原生交互界面的库 React的优势 相较于传统基于DOM开发的优势 组件化的开发方式不错的性能 相较于其它前端框架的优势 丰富的生态跨平台支持 React的市场情况 全球最流行&#xff0c;大…

基于modbus TCP实现EPICS与西门子S7 1200系列1215C PLC的通信

PLC介绍 西门子系列PLC在国内的市场占比第一&#xff0c;1200系列中小型PLC&#xff0c;因其众多的产品序列、强大的通讯功能和丰富扩展模块&#xff0c;被使用在工业生产、自动化生产线、智能制造、机器人等各行各业。根据CPU的供电电源的型号和数字量输出的类型&#xff0c;…

基于飞凌嵌入式i.MX6ULL核心板的电梯智能物联网关方案

电梯是现代社会中不可或缺的基础性设施&#xff0c;为人们的生产生活提供了很大的便捷。我国目前正处于城镇化的快速发展阶段&#xff0c;由此带动的城市基础设施建设、楼宇建设、老破小改造等需求也让我国的电梯行业处在了一个高速增长期。截至2023年年底&#xff0c;中国电梯…

蓝桥杯练习题——健身大调查

在浏览器中预览 index.html 页面效果如下&#xff1a; 目标 完成 js/index.js 中的 formSubmit 函数&#xff0c;用户填写表单信息后&#xff0c;点击蓝色提交按钮&#xff0c;表单项隐藏&#xff0c;页面显示用户提交的表单信息&#xff08;在 id 为 result 的元素显示&#…

开源模型应用落地-安全合规篇-模型输出合规性检测(三)

一、前言 为什么我们需要花大力气对用户输入的内容和模型生成的输出进行合规性检测,一方面是严格遵守各项法规要求,具体如下:互联网信息服务深度合成管理规定https://www.gov.cn/zhengce/zhengceku/2022-12/12/content_5731431.htm ​ 其次,受限于模型本身的一些缺陷,…

leetcode 225.用队列实现栈 JAVA

题目 思路 1.一种是用双端队列&#xff08;Deque&#xff09;&#xff0c;直接就可以调用很多现成的方法&#xff0c;非常方便。 2.另一种是用普通的队列&#xff08;Queue&#xff09;,要实现栈的先入后出&#xff0c;可以将最后一个元素的前面所有元素出队&#xff0c;然后…

【图解物联网】第2章 物联网的架构

2.1 物联网的整体结构 实现物联网时&#xff0c;物联网服务大体上发挥着两个作用。 第一是把从设备收到的数据保存到数据库&#xff0c;并对采集的数据进行分析。 第二是向设备发送指令和信息。 本章将会为大家介绍如何构建物联网服务&#xff0c;以…

【Canvas与艺术】绘制暗绿色汽车速度仪表盘

【原型】 【成果】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>暗绿色汽车速度仪表盘</title><style type"t…

堆排序详解

了解堆的操作和向上&#xff08;下&#xff09;调整算法可以看我的上一篇文章&#xff1a; 详解&#xff08;实现&#xff09;堆的接口函数 文章目录 堆是什么&#xff1f;堆排序的原理如何建堆&#xff1f;怎样建堆更快&#xff1f;1.使用向上调整算法建堆时间复杂度分析 2.使…

【HarmonyOS】ArkUI - 状态管理

在声明式 UI 中&#xff0c;是以状态驱动视图更新&#xff0c;如图1所示&#xff1a; 图1 其中核心的概念就是状态&#xff08;State&#xff09;和视图&#xff08;View&#xff09;&#xff1a; 状态&#xff08;State&#xff09;&#xff1a;指驱动视图更新的数据&#xf…

绿色节能|AIRIOT智慧建材能耗管理解决方案

建材供应是建筑业不可或缺的一个重要环节&#xff0c;在环保和企业可持续发展的双重需求下&#xff0c;建材生产商对建材生产过程中的能耗掌握和能耗管理尤其关注。但在实际生产和运营过程中&#xff0c;传统的建材能耗管理方式往往存在如下痛点&#xff1a; 用户管理权限不完善…

[医学分割大模型系列] (1) SAM 分割大模型解析

[医学大模型系列] [1] SAM 分割大模型解析 1. 特点2. 网络结构2.1 Image encoder2.2 Prompt encoder2.3 Mask decoder 3. 数据引擎4. 讨论 论文地址&#xff1a;Segment Anything 开源地址&#xff1a;https://github.com/facebookresearch/segment-anything demo地址&#x…

1、初识JVM

一、JVM是什么&#xff1f; JVM的英文全称是 Java Virtual Machine&#xff0c;其中文译名为Java虚拟机。它在本质上就是是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 JVM执行流程如下 二、JVM有哪些功能&#xff1f; 2.1 解释和运行 对字节码文…

平衡隐私与效率,Partisia Blockchain 解锁数字安全新时代

原文&#xff1a;https://cointelegraph.com/news/exploring-multiparty-computations-role-in-the-future-of-blockchain-privacy&#xff1b; https://medium.com/partisia-blockchain/unlocking-tomorrow-outlook-for-mpc-in-2024-and-beyond-cb170e3ec567 编译&#xff1…

数据仓库系列总结

一、数据仓库架构 1、数据仓库的概念 数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持管理决策。 数据仓库通常包含多个来源的数据&#xff0c;这些数据按照主题进行组织和存储&#x…

蓝桥杯练习——神秘咒语——axios

目标 完善 index.js 中的 TODO 部分&#xff0c;通过新增或者修改代码&#xff0c;完成以下目标&#xff1a; 点击钥匙 1 和钥匙 2 按钮时会通过 axios 发送请求&#xff0c;在发送请求时需要在请求头中添加 Authorization 字段携带 token&#xff0c;token 的值为 2b58f9a8-…

【DataWhale学习】用免费GPU线上跑chatGLM、SD项目实践

用免费GPU线上跑chatGLM、SD项目实践 ​ DataWhale组织了一个线上白嫖GPU跑chatGLM与SD的项目活动&#xff0c;我很感兴趣就参加啦。之前就对chatGLM有所耳闻&#xff0c;是去年清华联合发布的开源大语言模型&#xff0c;可以用来打造个人知识库什么的&#xff0c;一直没有尝试…

基于python+vue 的一加剧场管理系统的设计与实现flask-django-nodejs-php

二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一需求设…