CouponHub项目开发记录-基于责任链来进行创建优惠券模板的参数验证

发布于:2025-09-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

什么是责任链

责任链是一种设计模式,是让请求沿着一条处理链进行传递,从而让这条责任链上的对象来对该请求进行处理。

Controller层
在这里插入图片描述
进入service层中的创建优惠券模板方法
在这里插入图片描述
首先可以看到他调用了责任链上下文对象的handler处理方法,我们再进入对象内部
在这里插入图片描述

  • 首先可以看到他注入了一个ApplicationContext对象,通过该对象可以获取到SpringIOC当中的Bean。
  • 接着他定义了一个Map作为容器保存一个责任链,key为mark,value创建的责任链对象集合

在这里插入图片描述
接着再看其中的run方法

  • 首先他去通过applicationContext去获取实现了责任链接口的类构成的bean
  • 然后去遍历获取到的这个存放着所有实现了该接口的bean集合,并判断责任链容器中是否有带该mark标识的bean,如果有则直添加到其中,如果没有,创建一个List集合,然后再放入容器当中

在这里插入图片描述
再接着看它的handler方法

  • 首先根据传入的mark到容器中去找这一组责任链
  • 然后遍历这一组责任链上的对象分别调用他们的handler方法

在这里插入图片描述
看这些责任链对象的具体实现
在这里插入图片描述
直接实现MerchantAdminAbstractChainHandler接口,实现里面的handler方法。

// 新增优惠券模板信息到数据库
        CouponTemplateDO couponTemplateDO = BeanUtil.toBean(requestParam, CouponTemplateDO.class);
        couponTemplateDO.setStatus(CouponTemplateStatusEnum.ACTIVE.getStatus());
        couponTemplateDO.setShopNumber(UserContext.getShopNumber());
        couponTemplateMapper.insert(couponTemplateDO);

        // 因为模板 ID 是运行中生成的,@LogRecord 默认拿不到,所以我们需要手动设置
        LogRecordContext.putVariable("bizNo", couponTemplateDO.getId());

        // 缓存预热:通过将数据库的记录序列化成 JSON 字符串放入 Redis 缓存
        CouponTemplateQueryRespDTO actualRespDTO = BeanUtil.toBean(couponTemplateDO, CouponTemplateQueryRespDTO.class);
        Map<String, Object> cacheTargetMap = BeanUtil.beanToMap(actualRespDTO, false, true);
        Map<String, String> actualCacheTargetMap = cacheTargetMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue() != null ? entry.getValue().toString() : ""
                ));
        String couponTemplateCacheKey = String.format(MerchantAdminRedisConstant.COUPON_TEMPLATE_KEY, couponTemplateDO.getId());

        // 通过 LUA 脚本执行设置 Hash 数据以及设置过期时间
        String luaScript = "redis.call('HMSET', KEYS[1], unpack(ARGV, 1, #ARGV - 1)) " +
                "redis.call('EXPIREAT', KEYS[1], ARGV[#ARGV])";

        List<String> keys = Collections.singletonList(couponTemplateCacheKey);
        List<String> args = new ArrayList<>(actualCacheTargetMap.size() * 2 + 1);
        actualCacheTargetMap.forEach((key, value) -> {
            args.add(key);
            args.add(value);
        });

        // 优惠券活动过期时间转换为秒级别的 Unix 时间戳
        args.add(String.valueOf(couponTemplateDO.getValidEndTime().getTime() / 1000));

        // 执行 LUA 脚本
        stringRedisTemplate.execute(
                new DefaultRedisScript<>(luaScript, Long.class),
                keys,
                args.toArray()
        );

之后就是将该优惠券模板放入数据库当中,然后再存入到Redis当中进行缓存预热。

在这里插入图片描述
之后还有创建延时修改优惠券的时间,并通过消息队列进行发送,优惠券到期以后自动修改优惠券状态。

还有为了解决缓存穿透问题而将创建的优惠券模板id放入布隆过滤器当中。

布隆过滤器的原理

布隆过滤器是通过N个哈希函数和一个位图数组构成。当将id放入布隆过滤器后,会通过N个哈希函数去计算哈希值,再与位图数组的长度进行取模运算。得到在位图数组中的位置。然后下次再去查询数据库之前会先查看布隆过滤器中是否有该数据。如果每个对应的位置都为一,则可能存在(因为哈希冲突)。如果存在一个为0则一定不存在。