async-excel整合站内信通知用户体验感满满

news2024/11/16 0:35:47

前面的文章我们讲过消息中心站内信的实现 【消息中心】
那么本章我们来说说异步导入导出完成后,如何使用消息中心站内信的功能进行通知用户业务处理完成了

在async-excel中异步逻辑处理完成后会调用一个callback方法进行回调,所以我们可以再对async-excel的handler接口再做一层封装,让所有的handler继承自我们封装的这个类
此处我就讲一个导出示例

public abstract class AbstractNoticeExportHandler<T> implements ExportHandler<T> {
    private static final int COMPLETE = 2;
    private static final int FAIL = 3;

    @Override
    public void callBack(ExcelContext ctx, DataParam param) {
        callBack0(ctx, param);
        Notice.NoticeBuilder noticeBuilder = Notice.builder()
                .toUserCode(ctx.getTask().getCreateUserCode());
        //导出成功
        if (ctx.getTask().getStatus() == COMPLETE) {
            //导出成功
            if (StringUtils.isNotEmpty(ctx.getTask().getFileUrl())) {
                noticeBuilder.contentType(NoticeContentType.DOWNLOAD.type)
                        .title("文件导出任务已完成,任务id="+ctx.getTask().getId())
                        .content(ctx.getTask().getFileUrl());
            } else {
                //导出成功,但是没有url代表是导出了0条的情况
                noticeBuilder.contentType(NoticeContentType.TEXT.type)
                        .title("文件导出任务已完成,任务id="+ctx.getTask().getId())
                        .content(String.format("导出%s条", ctx.getSuccessCount()));
            }
        } else if (ctx.getTask().getStatus() == FAIL) {
            //导出失败
            noticeBuilder.contentType(NoticeContentType.TEXT.type)
                    .title("文件导出任务失败,任务id="+ctx.getTask().getId())
                    .content(ctx.getTask().getFailedMessage());
        }
        NoticeHelper.send(noticeBuilder.build());
    }

    public void callBack0(ExcelContext ctx, DataParam param) {

    }
}

然后编写业务导出处理类继承自我们自定义的回调处理

@ExcelHandle
public class UserExportHandler implements AbstractNoticeExportHandler<UserExportModel> {
    
    @Autowired
    IUserService userService;
    
    @Override
    public void init(ExcelContext ctx, DataParam param) {
        ExportContext context = (ExportContext) ctx;
        //此处的sheetNo会被覆盖,为了兼容一个文件多sheet导出
        WriteSheet sheet = EasyExcel.writerSheet(0, "第一个sheet").head(UserExportModel.class).build();
        context.setWriteSheet(sheet);
    }
    
    @Override
    public ExportPage<UserExportModel> exportData(int startPage, int limit, DataExportParam dataExportParam) {
        IPage<User> iPage = new Page<>(startPage, limit);
        IPage page = userService.page(iPage);
        List<UserExportModel> list = ExportListUtil.transform(page.getRecords(), UserExportModel.class);
        ExportPage<UserExportModel> result = new ExportPage<>();
        result.setTotal(page.getTotal());
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setRecords(list);
        return result;
    }
}

业务处理类还是该怎么写怎么写,所以我们来讲讲callback中做了什么事情,首先我们判断导出结果是成功还是失败,有没有异常,根据不同的类型我们包装不同的消息内容类型,如果出现异常了我们包装一个纯文本消息,如果导出正常并且有链接我们包装一个下载链接的消息最后通过消息工具类发送广播消息给mq。这个工具类有就如我们在消息中心的文章中写的那样,我们在发送前先保存下这条消息,并且异步操作,即使失败也不关业务什么事情。

public class NoticeHelper {
    /**
     * 同步日志
     *
     * @param notice
     */
    public static void send(Notice notice) {
        Assert.notNull(notice.getToUserCode(), "接收人不能为空");
        Assert.notNull(notice.getTitle(), "消息标题不能为空");
        Assert.notNull(notice.getContent(), "消息内容不能为空");
        Assert.notNull(NoticeContentType.fromValue(notice.getContentType()), "内容类型不正确");
        notice.setCode(BusinessCodeGenerator.getCode(BusinessCodeEnums.AUTO));
        notice.setHasRead(0);
        notice.setType(0);
        if (StringUtils.isEmpty(notice.getFromUserCode())) {
            notice.setFromUserCode("system");
            notice.setFromUserName("system");
        }
        doSend(notice);
    }
    
    private static void doSend(Notice notice) {
        Map<String, String> contextMap = SystemContext.getContextMap();
        MqSenderPool.senderExecutor.execute(()->{
            try {
                SystemContext.setContextMap(contextMap);
                NoticeSender sender = SpringContextUtil.getBean(NoticeSender.class);
                sender.send(notice);
            }finally {
                SystemContext.clearAll();
            }
        });
    }
}
public class NoticeSender {
    
    private final static Logger log = LoggerFactory.getLogger(NoticeSender.class);
    
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired(required = false)
    NoticeProxy noticeProxy;
    
    public void send(Notice notice) {
        //发送之前保存数据异步处理
        try {
            if (noticeProxy != null) {
                JsonResult<Void> result = noticeProxy.saveMessage(notice);
                if (result.isSuccess()) {
                    rabbitTemplate.convertAndSend(QueueConst.NOTICE_FANOUT_EXCHANGE,
                        QueueConst.NOTICE_FANOUT_BIND_KEY, JSONUtil.toJsonStr(notice));
                } else {
                    log.error("通知保存失败:{}", JSONUtil.toJsonStr(notice));
                    //throw new BizException("消息保存失败");
                }
            }
        } catch (Exception e) {
            log.error("发送通知异常:{}", JSONUtil.toJsonStr(notice));
        }
        
    }
}

NoticeProxy 不多说就是通过rest调用下服务接口保存下消息

在回到之前下消息中心我们写了个消费者,监听广播消息,我消费者可能部署多个节点,所以客户端可能连接在不同的节点上,所以消费者在收到消息的时候判断下目标客户端有没有连接在当前节点,如果不存在消息直接丢弃,如果存在则发送出去。

@Component
@Slf4j
public class NoticeReceiver {
    
    private static ObjectMapper MAPPER = new ObjectMapper();
    
    @RabbitListener(queues = QueueConst.NOTICE_FANOUT_QUEUE)
    @RabbitHandler
    public void receiveTopic(Message message) throws Exception {
        try {
            String receiveMsg = new String(message.getBody());
            Notice notice = JSONUtil.toBean(receiveMsg, Notice.class);
            List<Channel> channels = UserChannelContext.get(notice.getToUserCode());
            if (channels != null && channels.size() > 0) {
                for (Channel channel : channels) {
                    if (channel != null) {
                        channel.writeAndFlush(
                            new TextWebSocketFrame(MAPPER.writeValueAsString(notice)));
                    }
                }
            }
        } catch (Exception e) {
            log.error("notice接收处理错误:{}",e.getMessage());
        }
        
        // 补充:如果用户不在线则直接放弃;
        // 补充:无论如何消息消费后需要返回ack,所以此处直接catch起来
    }

最后的效果如下弹窗消息,并附带下载按钮
在这里插入图片描述
消息下拉框,圆点气泡需要联动推送的弹窗,当接收到消息时弹个窗红点数字加1,前端部分的代码也是相当简单,我就不多说了。UI用的是ant-design,前端根据不同的消息类型进行不同的展示,我们把消息分为

  • 纯文本消息
  • 路由消息
  • 链接跳转消息
  • 下载消息等等
    在这里插入图片描述
    当然下拉框的功能是通过调用单独接口实现,后端直接通知给前端的功能是触发上面那个弹窗,而下拉框是可以前端通过点击事件触发的。

消息中心站内信模块是个核心功能,后续可以依据这个功能还可以做什么呢?

  • 工作台的功能实现
  • 审批流的消息处理
  • 程序的强制更新
  • 任务的下发
  • 简易的IM及时通讯
    有了这个核心,系统的功能就能充满无限的遐想,并且用户体验感满满。

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

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

相关文章

完全二叉树与堆(包含STL堆的用法)

完全二叉树 完全二叉树为一类特殊的二叉树&#xff0c;高度为h的完全二叉树满足如下条件&#xff1a; &#xff08;1&#xff09;所有叶结点都出现在第h或h-1层&#xff1b; &#xff08;2&#xff09;第h-1层的所有叶结点都在非叶结点的右边&#xff1b; &#xff08;3&#…

AAAI 2023|模拟人脑场景感知过程,套娃Transformer讲故事能力更上一层楼

原文链接&#xff1a;https://www.techbeat.net/article-info?id4467 作者&#xff1a;seven_ 视频字幕生成目前已成为工业界AI创作领域非常火热的研究话题&#xff0c;这一技术可以应用在短视频的内容解析和讲解中&#xff0c;AI讲故事的技术已经越来越成熟。而在学术界&…

13、ThingsBoard-如何发送告警邮件

1、概述 很多时候,我们使用thingsboard的时候,会遇到比如一个设备触发了告警,如何将设备的告警消息定义成邮件模板,然后通知租户或者客户管理员,管理员进行处理,这样的需求是非常重要的。 2、实现的步骤 要实现这个需求我总结了几步: 2.1、设备上报的参数与阈值进行…

基于关键点检测的病患步态检测及分析方法

在临床工作中&#xff0c;对患有神经系统或骨骼肌肉系统疾病而可能影响行走能力的患者需要进行步态分析&#xff0c;以评定患者是否存在异常步态以及步态异常的性质和程度 步态评定临床意义 1、评估患者是否存在异常步态以及步态异常的性质和程度 2、为分析异常步态原因和矫正异…

看我们应用性能监控如何几秒钟定位慢访问跟因

背景 某汽车集团的汽车配件电子图册系统是其重要业务系统。最近业务部门反映&#xff0c;汽车配件电子图册调用图纸时&#xff0c;出现访问慢现象。 某汽车集团总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对汽车配件…

二进制?十进制!

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 给定两个十进制整数 : AAA,BBB 你需要把它们的二进制形式以十进制的运算法则相加输出结果。 例如&#xff1a; A3,B2A 3 , B 2A3,B2 的时候&#xff0c;AAA 的二进制表示是 : 111111 , BB…

Linux部署Nexus通过Maven推送及拉取代码

&#x1f60a; 作者&#xff1a; 一恍过去&#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390&#x1f38a; 社区&#xff1a; Java技术栈交流&#x1f389; 主题&#xff1a; Linux部署Nexus通过Maven推送及拉取代码⏱️ 创作时间&#xff1a; 2023年…

myBaits Expert Wheat Exome — 从多个小麦品种中富集超过250Mb的高可信度的外显子

myBaits Expert Wheat Exome 与国际小麦基因组测序联盟(IWGSC)合作开发&#xff0c;使用了IWGSC发布的中国春基因组和注释信息。靶向六倍体小麦中完整的高置信度且有基因注释的外显子区域,能够全面、统一、可靠地深入覆盖大干15 Gb的小麦基因组中超过250 Mb的CDS及其邻近区域。…

硅烷聚乙二醇活性酯;Silane-PEG-NHS;溶于大部分有机溶剂。仅供科研实验使用,不用于诊治

英文名称&#xff1a;Silane-PEG-NHS&#xff0c;Silane-PEG-SCM 中文名称&#xff1a;硅烷聚乙二醇活性酯 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k。。。 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;…

数组常用方法总结 (5) :find / findIndex / filter

find 与前边讲过的 some 类似&#xff0c;用于检测数组的每一项是否符合限定条件。只要遇到一个符合条件的&#xff0c;就会停止循环。在循环中&#xff0c;如果是简单数组&#xff0c;数据不会被改变&#xff0c;如果是对象数组&#xff0c;数据会改变。如果停止了循环&#…

音频(七)——数字麦克风和模拟麦克风(DMIC/AMIC)

数字麦克风与模拟麦克风(DMIC/AMIC) 麦克风(mic)&#xff1a;是将声音信号转换为电信号的能量转换器件&#xff0c;也就是用来采集你说话的声音扬声器(speaker)&#xff1a;是一种把电信号转变为声信号的换能器件&#xff0c;就是把对方说话产生的电信号转换成声音播放出来。简…

比较C++在for循环中的i++和++i以及i++的O2优化的效率:++i真的比i++快吗

比较C在for循环中的i和i以及i的O2优化的效率&#xff1a;i真的比i快吗 前言 对i和i的争论褒贬不一&#xff0c;不知从何时起&#xff08;大概是学C的时候老师就是这么教的&#xff09;我的习惯是在for循环中使用i而不是i for (int i 0; i < n; i) // 典但是看到一些博客…

再说多线程(五)——死锁

在前面四节中&#xff0c;我们一直没有讨论多线程程序的一个负面问题——死锁&#xff0c;有了一定的基础&#xff0c;现在是时候研究一下死锁了。死锁一定是出现在多线程程序中&#xff0c;单线程是不可能造成死锁的&#xff0c;因为你不可能同时加两把锁。死锁有个简单的例子…

《软件工程》课程四个实验的实验报告(《可行性研究与项目计划》《需求分析》《系统设计》《系统实现》)

实验1《可行性研究与项目计划》 实验学时&#xff1a; 2 实验地点&#xff1a; 任意 实验日期&#xff1a; 12月15日 一、实验目的 了解&#xff1a;软件项目可行性研究及项目计划的基本原理与方法&#xff1b;掌握&#xff1a;Visio等工具进行可…

【尚硅谷】Java数据结构与算法笔记06 - 算法复杂度详解

文章目录一、算法的时间复杂度1.1 度量算法执行时间的两种方法1.1.1 事后统计1.1.2 事前估算1.2 时间频度1.2.1 基本介绍1.2.2 举例说明&#xff1a;基本案例1.2.3 举例说明&#xff1a;忽略常数项1.2.4 举例说明&#xff1a;忽略低次项1.2.5 举例说明&#xff1a;忽略系数1.3 …

WebServer传输大文件致客户端自动关闭

程序运行在云服务器上, Ubuntu 20.04LTS系统&#xff0c;用浏览器测试能正常打开页面&#xff0c;请求一般的html文本和几十kb的小图片无问题&#xff0c;接着放了一个1.63MB&#xff08; 1714387字节&#xff09;的网上找的图过去&#xff0c;客户端图没加载完就自动断连了&am…

如何搭建一个专业的企业知识库

当客户跟你达成合作关系后&#xff0c;需要持续的关系维护&#xff0c;在一定的销售点&#xff0c;定期和客户沟通&#xff0c;据调查&#xff0c;赢得一个新客户的成本可能是保留一个现有客户的5到25倍&#xff0c;作为营销策略&#xff0c;客户服务支持必须满足他们的期望。建…

Linux小黑板(7):再谈动静态

"我看到&#xff0c;久违的晴朗啊"一、什么是动静态库在本栏目前面的篇幅也提到过这个概念&#xff0c;因此本小节就小小地回顾一番。在linux下:静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。动态库(.so):程序在运行的时候才去链接动态库的代码&am…

【npm报错】解决invalid json response body at https://registry.npmjs.org

报错信息&#xff1a; npm ERR! code FETCH_ERROR npm ERR! errno FETCH_ERROR npm ERR! invalid json response body at https://registry.npmjs.org/riophae%2fvue-treeselect reason: Invalid response body while trying to fetch https://registry.npmjs.org/riophae%2f…

从粪便菌群移植到下一代有益菌:Anaerobutyricum soehngenii为例

谷禾健康 我们知道&#xff0c;肠道微生物群对人类健康和福祉很重要&#xff0c;调节宿主代谢&#xff0c;塑造免疫系统并防止病原体定植。 通过粪便微生物群移植&#xff08;FMT&#xff09;恢复平衡多样的微生物群&#xff0c;已成为研究疾病发病机制中微生物群因果关系的潜在…