网站模板及素材建水县住房和城乡建设局网站
2026/1/10 2:36:29 网站建设 项目流程
网站模板及素材,建水县住房和城乡建设局网站,桂林分销网站开发,网站建设过程总结day10接口 1 查询发放中的优惠券接口说明查询发放中的优惠券请求方式GET请求路径/coupons/list请求参数无返回值[ { id: 110, // 优惠券id name: 年中大促, // 优惠券名称 specific: true, // 优惠券是…day10接口1 查询发放中的优惠券接口说明查询发放中的优惠券请求方式GET请求路径/coupons/list请求参数无返回值[ { id: 110, // 优惠券id name: 年中大促, // 优惠券名称 specific: true, // 优惠券是否限定了课程范围 discountType: , // 折扣类型 thresholdAmount: 0 // 折扣门槛 discountValue: 0, // 折扣值 maxDiscountAmount: 0, // 最大折扣金额 termDays: 0, // 有效天数 termEndTime: , // 过期时间 available: true, // 是否可领取 received: true, // 是否已领取 } ]CouponController.java/** * 查询发放中的优惠券列表 * return */ApiOperation(查询发放中的优惠券列表)GetMapping(list)publicListCouponVOqueryIssuingCoupons(){returncouponService.queryIssuingCoupons();}ICouponService.javaListCouponVOqueryIssuingCoupons();CouponServiceImpl.javaOverridepublicListCouponVOqueryIssuingCoupons(){//1.查询属于手动领取以及发放中的优惠券ListCouponlistlambdaQuery().eq(Coupon::getStatus,CouponStatus.ISSUING).eq(Coupon::getObtainWay,PUBLIC).list();if(CollUtils.isEmpty(list)){returnCollUtils.emptyList();}ListLongidslist.stream().map(Coupon::getId).collect(Collectors.toList());// 2.查询用户领取的并符合条件的优惠券ListUserCouponequserCouponService.lambdaQuery().eq(UserCoupon::getUserId,UserContext.getUser()).in(UserCoupon::getCouponId,ids).list();//2.1当前用户已经领取的数量MapLong,Longmapeq.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId,Collectors.counting()));//2.2当前用户对优惠券已经领取但是没使用的数量MapLong,Longunusedeq.stream().filter(uc-uc.getStatus().equals(UserCouponStatus.UNUSED)).collect(Collectors.groupingBy(UserCoupon::getCouponId,Collectors.counting()));//3.封装优惠券信息并返回ArrayListCouponVOcouponVOSnewArrayList();for(Couponcoupon:list){CouponVOcouponVOBeanUtils.copyBean(coupon,CouponVO.class);//3.是否可以领取被领取数量未达到总发放数量当前用户领取数量小于每人最多领取数量couponVO.setAvailable(coupon.getIssueNum()coupon.getTotalNum()map.getOrDefault(coupon.getId(),0L)coupon.getUserLimit());//4.是否可以使用(未使用的)couponVO.setReceived(unused.getOrDefault(coupon.getId(),0L)0);couponVOS.add(couponVO);}returncouponVOS;}2 手动领取优惠券UserCouponController.java/** * 领取优惠券(方式为手动领取的优惠券) * * param couponId * return */PostMapping({couponId}/receive)ApiOperation(领取优惠券)publicvoidreceiveCoupon(PathVariableLongcouponId){userCouponService.receiveCoupon(couponId);}IUserCouponService.javavoidreceiveCoupon(LongcouponId);UserCouponServiceImpl.javaOverrideTransactionalpublicvoidreceiveCoupon(LongcouponId){CouponcouponcouponMapper.selectById(couponId);if(couponnull){thrownewBizIllegalException(优惠券不存在);}LocalDateTimenowLocalDateTime.now();if(now.isBefore(coupon.getIssueBeginTime())||now.isAfter(coupon.getIssueEndTime())){thrownewBizIllegalException(优惠券不在领取时间范围内);}LonguserIdUserContext.getUser();LongresultredisLuaService.tryReceiveCoupon(couponId,userId,coupon.getUserLimit());if(resultnull){thrownewBizIllegalException(系统繁忙);}if(result-1){thrownewBizIllegalException(超过个人领取上限);}if(result0){thrownewBizIllegalException(库存不足);}try{saveUserCouponWithTx(coupon,userId,now);}catch(Exceptione){redisLuaService.rollbackCoupon(couponId,userId);throwe;}}TransactionalpublicvoidsaveUserCouponWithTx(Couponcoupon,LonguserId,LocalDateTimenow){// 1. 校验每人限领数量兜底IntegercountlambdaQuery().eq(UserCoupon::getUserId,userId).eq(UserCoupon::getCouponId,coupon.getId()).count();if(count!nullcountcoupon.getUserLimit()){thrownewBizIllegalException(该用户领取数量超出限制);}// 2. 乐观更新优惠券发放数量最终防线introwscouponMapper.incrIssueNumWithLimit(coupon.getId());if(rows0){thrownewBizIllegalException(优惠券库存不足);}// 3. 新增用户优惠券addCoupon(coupon.getId(),coupon,now,userId);}privatevoidaddCoupon(LongcouponId,Couponcoupon,LocalDateTimenow,LonguserId){UserCouponuserCouponnewUserCoupon();LocalDateTimetermBeginTimecoupon.getTermBeginTime();LocalDateTimetermEndTimecoupon.getTermEndTime();if(termBeginTimenull){termBeginTimenow;termEndTimetermBeginTime.plusDays(coupon.getTermDays());}userCoupon.setUserId(userId);userCoupon.setCouponId(couponId);userCoupon.setTermBeginTime(termBeginTime);userCoupon.setTermEndTime(termEndTime);userCoupon.setStatus(UserCouponStatus.UNUSED);this.save(userCoupon);}Lua脚本-- KEYS[1] coupon:stock:{couponId}-- KEYS[2] coupon:user:{couponId}-- ARGV[1] userId-- ARGV[2] userLimit-- 1. 查询用户已领取数量localcounttonumber(redis.call(HGET,KEYS[2],ARGV[1])or0)locallimittonumber(ARGV[2])ifcountlimitthenreturn-1-- 超过个人限领end-- 2. 校验库存localstocktonumber(redis.call(GET,KEYS[1]))ifnotstockorstock0thenreturn0-- 库存不足end-- 3. 扣库存redis.call(DECR,KEYS[1])-- 4. 用户领取数量 1redis.call(HINCRBY,KEYS[2],ARGV[1],1)-- 5. 成功return1LUA配置类packagecom.tianji.promotion.config;importcom.tianji.promotion.constants.PromotionConstants;importlombok.RequiredArgsConstructor;importorg.springframework.core.io.ClassPathResource;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.core.script.DefaultRedisScript;importorg.springframework.stereotype.Service;importjava.util.Arrays;importjava.util.List;/** * Redis Lua 执行统一封装 * * 职责 * 1. 负责 Lua 脚本加载 * 2. 统一管理 Redis Key 拼装 * 3. 对业务层屏蔽 Lua 细节 * author ABC */ServiceRequiredArgsConstructorpublicclassRedisLuaService{privatefinalStringRedisTemplateredisTemplate;privatestaticfinalDefaultRedisScriptLongRECEIVE_COUPON_SCRIPT;static{RECEIVE_COUPON_SCRIPTnewDefaultRedisScript();RECEIVE_COUPON_SCRIPT.setLocation(newClassPathResource(redis/lua/receive_coupon.lua));RECEIVE_COUPON_SCRIPT.setResultType(Long.class);}publicLongtryReceiveCoupon(LongcouponId,LonguserId,IntegeruserLimit){StringstockKeyPromotionConstants.COUPON_STOCK_KEYcouponId;StringuserCountKeyPromotionConstants.COUPON_USER_COUNT_KEYcouponId;returnredisTemplate.execute(RECEIVE_COUPON_SCRIPT,List.of(stockKey,userCountKey),userId.toString(),userLimit.toString());}/** * DB 失败回滚 */publicvoidrollbackCoupon(LongcouponId,LonguserId){StringstockKeyPromotionConstants.COUPON_STOCK_KEYcouponId;StringuserCountKeyPromotionConstants.COUPON_USER_COUNT_KEYcouponId;redisTemplate.opsForValue().increment(stockKey);redisTemplate.opsForHash().increment(userCountKey,userId.toString(),-1);}}Redis常量Keypackagecom.tianji.promotion.constants;/** * 优惠券常量类 * * author ax */publicinterfacePromotionConstants{/** * 优惠券的兑换码生成序列号key */StringCOUPON_CODE_SERIAL_KEYcoupon:code:serial:;/** * 优惠券的兑换码兑换序列号key */StringCOUPON_CODE_MAP_KEYcoupon:code:serial:;/** * 优惠券库存 * coupon:stock:{couponId} - int */StringCOUPON_STOCK_KEYcoupon:stock:;/** * 用户已领取数量 * coupon:user:{couponId} - Hash(userId - count) */StringCOUPON_USER_COUNT_KEYcoupon:user:;}CouponMapper.javaUpdate(update coupon set issue_num issue_num 1 where id #{couponId} and issue_num total_num)intincrIssueNumWithLimit(LongcouponId);3 兑换码兑换优惠券UserCouponController.java/** * 兑换码兑换优惠券(方式为兑换码兑换的优惠券) * * return */PostMapping({code}/exchange)ApiOperation(兑换码兑换优惠券)publicvoidexchangeCoupon(PathVariableStringcode){userCouponService.exchangeCoupon(code);}IUserCouponService.javavoidexchangeCoupon(Stringcode);UserCouponServiceImpl.javaOverrideTransactionalpublicvoidexchangeCoupon(Stringcode){//校验兑换码是否被兑换过是否存在longlCodeUtil.parseCode(code);//是否已经兑换过 setbit替换getbitbooleanisExchangeexchangeCodeService.updateExchangeMark(l,true);try{if(isExchange){thrownewBizIllegalException(该兑换码已经兑换过);}ExchangeCodebyIdexchangeCodeService.getById(l);if(byIdnull){thrownewBizIllegalException(该兑换码不存在);}LocalDateTimenowLocalDateTime.now();if(now.isAfter(byId.getExpiredTime())){thrownewBizIllegalException(该兑换码已过期);}//查询优惠券CouponcouponcouponMapper.selectById(byId.getExchangeTargetId());LonguserIdUserContext.getUser();//领取优惠券saveUserCouponWithTx(coupon,userId,now);//更新兑换码状态exchangeCodeService.lambdaUpdate().eq(ExchangeCode::getId,l).set(ExchangeCode::getUserId,userId).set(ExchangeCode::getStatus,ExchangeCodeStatus.USED).update();}catch(Exceptione){exchangeCodeService.updateExchangeMark(l,false);throwe;}}IExchangeCodeServicebooleanupdateExchangeMark(longl,booleanb);ExchangeCodeServiceImplOverridepublicbooleanupdateExchangeMark(longl,booleanb){BooleanisstringRedisTemplate.opsForValue().setBit(COUPON_CODE_MAP_KEY,l,b);returnis!nullis;}CouponMapper.javaUpdate(update coupon set issue_num issue_num 1 where id #{couponId} and issue_num total_num)intincrIssueNumWithLimit(LongcouponId);一种解决方案我们可以借助AspectJ来实现。1引入AspectJ依赖!--aspecj--dependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactId/dependency2暴露代理对象在启动类上添加注解暴露代理对象3使用代理对象最后改造领取优惠券的代码获取代理对象来调用事务方法

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询