码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 切面Aspect + 策略模式实现待办提醒功能


    1.背景

    产品需要实现一个待办提醒功能,就是核心业务发生变更即提醒业务员去处理相关业务。譬如:订单上传了支付凭证,那么就会提醒相关业务员去待办列表操办。

    2.表设计

    其实表设计主要是两张表sys_todo、sys_todo_detail

    一张是待办核心表,主要是记录待办类型,待办业务id等。

    1. CREATE TABLE `sys_todo` (
    2. `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
    3. `model` int NOT NULL DEFAULT '0' COMMENT '归属模块: 0-crm 1-srm',
    4. `type` int NOT NULL DEFAULT '0' COMMENT '待办类型',
    5. `biz_id` bigint NOT NULL DEFAULT '0' COMMENT '业务id',
    6. PRIMARY KEY (`id`)
    7. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='待办统计表';

    另一张是待办详情表,主要是记录这个待办能做哪些操作。

    1. CREATE TABLE `sys_todo_detail` (
    2. `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
    3. `todo_id` bigint NOT NULL DEFAULT '0' COMMENT 'sys_todo表id',
    4. `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题',
    5. `url` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '地址',
    6. `is_pop` int NOT NULL DEFAULT '0' COMMENT '是否弹窗 0-否 1-是',
    7. `content` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '内容',
    8. `sort_num` int NOT NULL DEFAULT '0' COMMENT '排序',
    9. PRIMARY KEY (`id`),
    10. KEY `idx_todo_id` (`todo_id`) USING BTREE
    11. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='待办统计详情表';

    举例:sys_todo 有个待办类型是【上传支付凭证】,id=1,sys_todo_detail 待办详情表,就有todo_id 为1的内容,譬如【查看】、【审核】操作的两条记录。

    当然,这些表具体实现得根据具体业务设计来实现。

    3.如何实现

    3.1 业务埋点

    业务埋点,也就是在每一个相关业务代码里面,加上待办的埋点,让相关待办业务内容插入待办表。譬如:【上传支付凭证】,就得在上传支付凭证的代码最后上传成功的地方,写上上传待办的业务代码。

    3.2 切面Aspect + 策略模式

    3.2.1 先定义好切面

    这里我在项目里面,定义好切面类。

    1. @Component
    2. @Aspect
    3. public class SrmTodoAspect {
    4. //这里拦截mybatis-plus底层方法
    5. @Pointcut("(execution(* com.baomidou.mybatisplus.extension.service.IService.update*(..))" +
    6. "||execution(* com.baomidou.mybatisplus.extension.service.IService.save*(..))" +
    7. "||execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update*(..))" +
    8. "||execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert(..)))" )
    9. public void todo(){};
    10. //策略A
    11. @Resource
    12. private ATodoStrategy aTodoStrategy;
    13. //策略B
    14. @Resource
    15. private BTodoStrategy bTodoStrategy;
    16. //线程池
    17. @Resource
    18. private TaskExecutor taskExecutor;
    19. @Around("todo()")
    20. public Object around(ProceedingJoinPoint point) throws Throwable {
    21. Object[] args = point.getArgs();
    22. Object obj = point.proceed();
    23. Object arg = args[0];
    24. List paramList = null;
    25. if(Objects.isNull(arg)){
    26. return obj;
    27. }
    28. if(arg instanceof ArrayList){
    29. paramList = (List)arg;
    30. }else {
    31. paramList = Arrays.asList(args);
    32. }
    33. if (CollectionUtil.isEmpty(paramList)){
    34. return obj;
    35. }
    36. /*
    37. * 这里用到捕获异常很关键,不能因待办出现异常而影响主流程,
    38. * 用线程池避免待办处理过慢影响主流程性能
    39. */
    40. taskExecutor.execute(()->{
    41. try{
    42. Thread.sleep(1000L);
    43. dealTodoStrategy(paramList);
    44. }catch (Exception e){
    45. }
    46. });
    47. return obj;
    48. }
    49. /*
    50. * TodoContext 是策略模式的上下文
    51. * aTodoStrategy、bTodoStrategy 是具体的策略
    52. */
    53. private void dealTodoStrategy(List paramList) {
    54. TodoContext context = null;
    55. Object param = paramList.get(0);
    56. if(param instanceof A){
    57. context = new TodoContext(aTodoStrategy);
    58. }else if(param instanceof B) {
    59. context = new TodoContext(bTodoStrategy);
    60. }
    61. if(context != null){
    62. context.executeStrategy(paramList);
    63. }
    64. }
    65. }
    66. 特别说一句:这里其实最初的设计并不是用切面,而是用mybatis拦截器 Interceptor 去实现获取到insert或update后的实体的,但是拦截mybatis-plus的saveBatch方法能拿到List实体,但是没有id。因此不能很好处理待办业务。原因:大家可以去看下CSDN。

      3.2.2 定义策略模式

      策略接口(TodoStrategy) 

      1. public interface TodoStrategy {
      2. void dealTodo(List list);
      3. }

      Context类

      1. public class TodoContext {
      2. private TodoStrategy strategy;
      3. public TodoContext(TodoStrategy strategy){
      4. this.strategy = strategy;
      5. }
      6. public void executeStrategy(List list){
      7. strategy.dealTodo(list);
      8. }
      9. }

      策略实现类 (ATodoStrategy )

      1. @Service
      2. public class ATodoStrategy implements TodoStrategy {
      3. @Override
      4. public void dealTodo(List list) {
      5. if(CollectionUtil.isEmpty(list)){
      6. return;
      7. }
      8. TodoForm todoForm = null;
      9. List enquiryList = (List)list;
      10. List enquiryIdList =
      11. enquiryList.stream().map(Enquiry::getId).collect(Collectors.toList());
      12. //根据id找到数据库实体类
      13. enquiryList = enquiryService.findList(new EnquiryQuery().setIdList(enquiryIdList));
      14. //遍历实体类,然后根据不同的实体状态去实现待办规则新增或消除功能
      15. for(Enquiry enquiry : enquiryList){
      16. Long id = enquiry.getId();
      17. //新增去报价待办
      18. if(EnquiryStatusEnum.WAIT_QUOTE.getKey().equals(enquiry.getStatus())){
      19. }
      20. //消除去报价待办
      21. if(EnquiryStatusEnum.HAD_QUOTE.getKey().equals(enquiry.getStatus())){
      22. }
      23. }
      24. }
      25. }

      4.总结

      待办实现方式两种:①埋点 ②切面Aspect + 策略模式

      个人比较推荐② ,因为这个代码设计好处如下:

      1.不需要“过分”埋点。埋点的话,容易漏埋点。譬如新增代码业务代码在插入操作后埋点了,但是突然另一个业务也有插入操作,但是忘记埋点,那么待办就容易遗漏。

      2.代码集中维护,模块分层清晰。也就是待办新增或消除逻辑,统一写在策略模式里面了,只需要去策略模式里面维护即可。这样也能减少在业务代码过多埋点造成代码臃肿。

    67. 相关阅读:
      Scratch 第十四课-打地鼠游戏
      AIR32F103(十一) 在AIR32F103上移植微雪墨水屏驱动
      web.xml中Servlet中init-param的作用说明
      【Python编程】Python实现生产者消费者模式(多线程爬虫)
      [SpringMVC笔记] SpringMVC-06-JSON数据传递参数
      ICMP差错包
      windows设置开机启动程序
      LabVIEW数据采集-模拟输出
      [附源码]计算机毕业设计springboot疫情管理系统
      【C++】STL容器——string类的例题应用(9)
    68. 原文地址:https://blog.csdn.net/taipoucha5799/article/details/134494906
      • 最新文章
      • 【JVM】编译执行与解释执行的区别是什么?JVM 使用哪种方式?
        用 Hashids 优雅解决 C 端自增 ID 暴露问题
        V8引擎 精品漫游指南--Ignition篇(上) 指令 栈帧 槽位 调用约定 内存布局 基础内容
        LLVM Pass快速入门(四):代码插桩
        milkup:桌面端 markdown AI续写和即时渲染
        基于项目工程构建SBOM(软件物料清单)的研究
        鸿蒙应用开发UI基础第二节:鸿蒙应用程序框架核心解析与实操
        .NET 中如何快速实现 List 集合去重?
        扣子Coze实战:从0到1打造抖音+小红书热点监控智能体
        浅谈数据访问层
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      小工具 小游戏
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

      京公网安备 11010502049817号