CouponHub项目开发记录-基于责任链来进行创建优惠券模板的参数验证
什么是责任链
责任链是一种设计模式,是让请求沿着一条处理链进行传递,从而让这条责任链上的对象来对该请求进行处理。
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则一定不存在。