产品需要实现一个待办提醒功能,就是核心业务发生变更即提醒业务员去处理相关业务。譬如:订单上传了支付凭证,那么就会提醒相关业务员去待办列表操办。
其实表设计主要是两张表sys_todo、sys_todo_detail
一张是待办核心表,主要是记录待办类型,待办业务id等。
- CREATE TABLE `sys_todo` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
- `model` int NOT NULL DEFAULT '0' COMMENT '归属模块: 0-crm 1-srm',
- `type` int NOT NULL DEFAULT '0' COMMENT '待办类型',
- `biz_id` bigint NOT NULL DEFAULT '0' COMMENT '业务id',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='待办统计表';
另一张是待办详情表,主要是记录这个待办能做哪些操作。
- CREATE TABLE `sys_todo_detail` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
- `todo_id` bigint NOT NULL DEFAULT '0' COMMENT 'sys_todo表id',
- `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题',
- `url` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '地址',
- `is_pop` int NOT NULL DEFAULT '0' COMMENT '是否弹窗 0-否 1-是',
- `content` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '内容',
- `sort_num` int NOT NULL DEFAULT '0' COMMENT '排序',
- PRIMARY KEY (`id`),
- KEY `idx_todo_id` (`todo_id`) USING BTREE
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='待办统计详情表';
举例:sys_todo 有个待办类型是【上传支付凭证】,id=1,sys_todo_detail 待办详情表,就有todo_id 为1的内容,譬如【查看】、【审核】操作的两条记录。
当然,这些表具体实现得根据具体业务设计来实现。
业务埋点,也就是在每一个相关业务代码里面,加上待办的埋点,让相关待办业务内容插入待办表。譬如:【上传支付凭证】,就得在上传支付凭证的代码最后上传成功的地方,写上上传待办的业务代码。
这里我在项目里面,定义好切面类。
- @Component
- @Aspect
- public class SrmTodoAspect {
-
- //这里拦截mybatis-plus底层方法
- @Pointcut("(execution(* com.baomidou.mybatisplus.extension.service.IService.update*(..))" +
- "||execution(* com.baomidou.mybatisplus.extension.service.IService.save*(..))" +
- "||execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update*(..))" +
- "||execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert(..)))" )
- public void todo(){};
-
-
- //策略A
- @Resource
- private ATodoStrategy aTodoStrategy;
-
- //策略B
- @Resource
- private BTodoStrategy bTodoStrategy;
-
- //线程池
- @Resource
- private TaskExecutor taskExecutor;
-
- @Around("todo()")
- public Object around(ProceedingJoinPoint point) throws Throwable {
- Object[] args = point.getArgs();
- Object obj = point.proceed();
-
- Object arg = args[0];
- List
- if(Objects.isNull(arg)){
- return obj;
- }
-
- if(arg instanceof ArrayList){
- paramList = (List)arg;
- }else {
- paramList = Arrays.asList(args);
- }
-
- if (CollectionUtil.isEmpty(paramList)){
- return obj;
- }
-
- /*
- * 这里用到捕获异常很关键,不能因待办出现异常而影响主流程,
- * 用线程池避免待办处理过慢影响主流程性能
- */
- taskExecutor.execute(()->{
- try{
- Thread.sleep(1000L);
-
- dealTodoStrategy(paramList);
- }catch (Exception e){
-
- }
- });
-
- return obj;
- }
-
- /*
- * TodoContext 是策略模式的上下文
- * aTodoStrategy、bTodoStrategy 是具体的策略
- */
- private void dealTodoStrategy(List {
- TodoContext context = null;
- Object param = paramList.get(0);
- if(param instanceof A){
- context = new TodoContext(aTodoStrategy);
- }else if(param instanceof B) {
- context = new TodoContext(bTodoStrategy);
- }
- if(context != null){
- context.executeStrategy(paramList);
- }
- }
-
- }
特别说一句:这里其实最初的设计并不是用切面,而是用mybatis拦截器 Interceptor 去实现获取到insert或update后的实体的,但是拦截mybatis-plus的saveBatch方法能拿到List实体,但是没有id。因此不能很好处理待办业务。原因:大家可以去看下CSDN。
策略接口(TodoStrategy)
- public interface TodoStrategy {
- void dealTodo(List list);
- }
Context类
- public class TodoContext {
- private TodoStrategy strategy;
-
- public TodoContext(TodoStrategy strategy){
- this.strategy = strategy;
- }
-
- public void executeStrategy(List list){
- strategy.dealTodo(list);
- }
- }
策略实现类 (ATodoStrategy )
- @Service
- public class ATodoStrategy implements TodoStrategy {
-
-
- @Override
- public void dealTodo(List list) {
-
- if(CollectionUtil.isEmpty(list)){
- return;
- }
-
- TodoForm todoForm = null;
-
- List
enquiryList = (List)list; - List
enquiryIdList = - enquiryList.stream().map(Enquiry::getId).collect(Collectors.toList());
-
- //根据id找到数据库实体类
- enquiryList = enquiryService.findList(new EnquiryQuery().setIdList(enquiryIdList));
-
- //遍历实体类,然后根据不同的实体状态去实现待办规则新增或消除功能
- for(Enquiry enquiry : enquiryList){
- Long id = enquiry.getId();
-
- //新增去报价待办
- if(EnquiryStatusEnum.WAIT_QUOTE.getKey().equals(enquiry.getStatus())){
-
- }
-
- //消除去报价待办
- if(EnquiryStatusEnum.HAD_QUOTE.getKey().equals(enquiry.getStatus())){
-
- }
-
- }
-
- }
- }
待办实现方式两种:①埋点 ②切面Aspect + 策略模式
个人比较推荐② ,因为这个代码设计好处如下:
1.不需要“过分”埋点。埋点的话,容易漏埋点。譬如新增代码业务代码在插入操作后埋点了,但是突然另一个业务也有插入操作,但是忘记埋点,那么待办就容易遗漏。
2.代码集中维护,模块分层清晰。也就是待办新增或消除逻辑,统一写在策略模式里面了,只需要去策略模式里面维护即可。这样也能减少在业务代码过多埋点造成代码臃肿。