个人做网站如何推广,深圳企业排行榜,wordpress 找回密码页面模板,阿里云建设网站安全吗day09
实现新增优惠卷的功能
/***
***思路分析#xff1a;根据前端传入的dto#xff0c;可以将coupon信息直接存入数据库当中#xff0c;但是对于其限定信息
***还需要进行判断#xff08;也就是标签#xff09;若是有呢#xff0c;还需要加入到coupon_scope表当中根据前端传入的dto可以将coupon信息直接存入数据库当中但是对于其限定信息 ***还需要进行判断也就是标签若是有呢还需要加入到coupon_scope表当中而在本方法中 ***转化成po使用stream流进行映射注入相关的scope信息是因为CouponScopes ***实体类上有这个注解能实现链式注入Accessors(chain true) ***/ Transactional public void saveCoupon(CouponFormDTO dto) { //1.保存优惠卷信息 //转成po Coupon coupon BeanUtils.copyBean(dto, Coupon.class); //保存 save(coupon); if (!dto.getSpecific()){ //没有范围限定 return; } //2.保存限定范围 Long couponId coupon.getId(); ListLong scopes dto.getScopes(); if (CollUtils.isEmpty(scopes)){ throw new BadRequestException(限定范围不能为空); } //转换po ListCouponScope list scopes.stream().map(bizId - new CouponScope().setBizId(bizId).setCouponId(couponId)).collect(Collectors.toList()); //保存 scopeService.saveBatch(list); }实现分页查询优惠券功能/*** ****思路分析该代码的思路也就是跟分页查询差不多根据筛选条件从数据库中查出page ***拿到record最后返回即可 ***/ public PageDTOCouponPageVO queryCouponByPage(CouponQuery query) { // 从查询条件中获取过滤参数 Integer status query.getStatus(); // 优惠券状态 Integer type query.getType(); // 优惠券类型 String name query.getName(); // 优惠券名称 // 执行分页查询使用LambdaQueryWrapper构建查询条件 // 根据传入的参数动态添加查询条件参数不为null时才添加对应条件 PageCoupon page lambdaQuery() .eq(type ! null, Coupon::getType, type) // 按类型查询 .eq(status ! null, Coupon::getStatus, status) // 按状态查询 .like(StrUtil.isNotBlank(name), Coupon::getName, name) // 按名称模糊查询 .page(query.toMpPageDefaultSortByCreateTimeDesc()); // 执行分页查询按创建时间降序排序 // 获取查询结果记录 ListCoupon records page.getRecords(); // 如果结果为空返回空分页对象 if (CollUtils.isEmpty(records)) return PageDTO.empty(page); // 将实体对象列表转换为VO对象列表 ListCouponPageVO list BeanUtils.copyList(records, CouponPageVO.class); // 返回分页结果 return PageDTO.of(page,list); }实现优惠券的发放功能/*** ****思路分析该功能的实现就是相当于一个更新操作将优惠券的状态改为发放。拿到优惠券信息 ***只有暂停发放的优惠券或者是待发放的优惠券才能发放同时判断是不是立刻发放或这发放时间小于 ***等于当前时间才发放最后更新优惠券状态为发放并且设置发放时间。注此处还未时间定时发放功能 ***推断后续应该会有定时任务同时指定发放还需要生成验证码后续实现 ***/ public void beginIssue(CouponIssueFormDTO dto) { //查询优惠券 Coupon coupon getById(dto.getId()); if (coupon null){ throw new BadRequestException(优惠券不存在); } //判断优惠券状态是否是暂停还是待发放 if (!CouponStatus.PAUSE.equals(coupon.getStatus()) !CouponStatus.DRAFT.equals(coupon.getStatus())){ throw new BizIllegalException(优惠券状态错误); } //判断是否是立刻发放 LocalDateTime issueBeginTime dto.getIssueBeginTime(); LocalDateTime now LocalDateTime.now(); //如果发放时间为null或者当前时间小于等于发放时间 boolean isBegin issueBeginTime null || !issueBeginTime.isAfter(now); //更新优惠券 Coupon c BeanUtils.copyBean(dto, Coupon.class); if (isBegin){ c.setStatus(CouponStatus.ISSUING); c.setIssueBeginTime(now); }else { c.setStatus(CouponStatus.UN_ISSUE); } updateById(c); }实现生成兑换码功能思路分析:说一下该算法生成兑换码思路由于唯一性要求很容易想到使用id技术而id技术又有自增id雪花算法uuid这三个。而我们的兑换码是26个英文字母再加上2-9个数字组成也就是32个也就是base32算将五个二进字字符视为一组将该组的二进制计算为10进组对应其角标最后又由于要求兑换码不能超过十个字符而每个字符由5个字节也就是一组确定。因此不能兑换码不能超过50个字节其中uuid占用128个字节雪花算法占用68个字节因此最终选定自增id自增id从1增加到Integer的最大值可以达到40亿以上个数字而占用的字节仅仅4个字节也就是32个bit位距离50个bit位的限制还有很大的剩余符合要求。这样就解决了唯一性可读性数据量大这三个要求。考虑到兑换码的状态只是兑换和没兑换这跟前面的签到功能是一样的。因此很容易想到使用redis中的位图去解决。将兑换码的唯一标识也就是自增id去对应位图上的位置当该位置为1时就表名已经兑换解决了重兑和高效的问题。而如何检验重兑呢也很简单只要使用base32将兑换码解码等操作解析出原来的自增id去位图比对即可。最后就剩一个防止爆刷也就是为了防止别人能够猜出来这里采用的是加权算法将自增id32位每4位分为一组共8组都转为10进制每一组给不同权重把每一组数加权求和得到的结果就是签名。当然为了避免秘钥被人猜测出规律我们可以准备16组秘钥。在兑换码自增id前拼接一个4位的新鲜值可以是随机的。这个值是多少就取第几组秘钥。这样就进一步增加了兑换码的复杂度。最后把加权和也就是签名也转二进制拼接到最前面最终的兑换码就是这样因此在解码的时候因为有50个字节需要找到自增长id对应的序列号也就是最后32个字节功能实现1.引入base32工具类和签名工具package com.tianji.promotion.utils; import cn.hutool.core.text.StrBuilder; /** * 将整数转为base32字符的工具因为是32进制所以每5个bit位转一次 */ public class Base32 { private final static String baseChars 6CSB7H8DAKXZF3N95RTMVUQG2YE4JWPL; public static String encode(long raw) { StrBuilder sb new StrBuilder(); while (raw ! 0) { int i (int) (raw 0b11111); sb.append(baseChars.charAt(i)); raw raw 5; } return sb.toString(); } public static long decode(String code) { long r 0; char[] chars code.toCharArray(); for (int i chars.length - 1; i 0; i--) { long n baseChars.indexOf(chars[i]); r r | (n (5*i)); } return r; } public static String encode(byte[] raw) { StrBuilder sb new StrBuilder(); int size 0; int temp 0; for (byte b : raw) { if (size 0) { // 取5个bit int index (b 3) 0b11111; sb.append(baseChars.charAt(index)); // 还剩下3位 size 3; temp b 0b111; } else { int index temp (5 - size) | (b (3 size) ((1 5 - size) - 1)) ; sb.append(baseChars.charAt(index)); int left 3 size; size 0; if(left 5){ index b (left - 5) ((1 5) - 1); sb.append(baseChars.charAt(index)); left left - 5; } if(left 0){ continue; } temp b ((1 left) - 1); size left; } } if(size 0){ sb.append(baseChars.charAt(temp)); } return sb.toString(); } public static byte[] decode2Byte(String code) { char[] chars code.toCharArray(); byte[] bytes new byte[(code.length() * 5 )/ 8]; byte tmp 0; byte byteSize 0; int index 0; int i 0; for (char c : chars) { byte n (byte) baseChars.indexOf(c); i; if (byteSize 0) { tmp n; byteSize 5; } else { int left Math.min(8 - byteSize, 5); if(i chars.length){ bytes[index] (byte) (tmp left | (n ((1 left) - 1))); break; } tmp (byte) (tmp left | (n (5 - left))); byteSize left; if (byteSize 8) { bytes[index] tmp; byteSize (byte) (5 - left); if (byteSize 0) { tmp 0; } else { tmp (byte) (n ((1 byteSize) - 1)); } } } } return bytes; } }package com.tianji.promotion.utils; import com.tianji.common.constants.RegexConstants; import com.tianji.common.exceptions.BadRequestException; /** * h1 stylefont-weight:5001.兑换码算法说明/h1 * p兑换码分为明文和密文明文是50位二进制数密文是长度为10的Base32编码的字符串 /p * h1 stylefont-weight:5002.兑换码的明文结构/h1 * p stylepadding: 0 15px14(校验码) 4 (新鲜值) 32(序列号) /p * ul stylepadding: 0 15px * li序列号一个单调递增的数字可以通过Redis来生成/li * li新鲜值可以是优惠券id的最后4位同一张优惠券的兑换码就会有一个相同标记/li * li载荷将新鲜值4位拼接序列号32位得到载荷/li * li校验码将载荷4位一组每组乘以加权数最后累加求和然后对2^14求余得到/li * /ul * h1 stylefont-weight:5003.兑换码的加密过程/h1 * ol typea stylepadding: 0 15px * li首先利用优惠券id计算新鲜值 f/li * li将f和序列号s拼接得到载荷payload/li * li然后以f为角标从提前准备好的16组加权码表中选一组/li * li对payload做加权计算得到校验码 c /li * li利用c的后4位做角标从提前准备好的异或密钥表中选择一个密钥key/li * li将payload与key做异或作为新payload2/li * li然后拼接兑换码明文f (4位) payload236位/li * li利用Base32对密文转码生成兑换码/li * /ol * h1 stylefont-weight:5004.兑换码的解密过程/h1 * ol typea stylepadding: 0 15px * li首先利用Base32解码兑换码得到明文数值num/li * li取num的高14位得到c1取num低36位得payload /li * li利用c1的后4位做角标从提前准备好的异或密钥表中选择一个密钥key/li * li将payload与key做异或作为新payload2/li * li利用加密时的算法用payload2和s1计算出新校验码c2把c1和c2比较一致则通过 /li * /ol */ public class CodeUtil { /** * 异或密钥表用于最后的数据混淆 */ private final static long[] XOR_TABLE { 61261925471L, 61261925523L, 58169127203L, 64169927267L, 64169927199L, 61261925629L, 58169127227L, 64169927363L, 59169127063L, 64169927359L, 58169127291L, 61261925739L, 59169127133L, 55139281911L, 56169127077L, 59169127167L }; /** * fresh值的偏移位数 */ private final static int FRESH_BIT_OFFSET 32; /** * 校验码的偏移位数 */ private final static int CHECK_CODE_BIT_OFFSET 36; /** * fresh值的掩码4位 */ private final static int FRESH_MASK 0xF; /** * 验证码的掩码14位 */ private final static int CHECK_CODE_MASK 0b11111111111111; /** * 载荷的掩码36位 */ private final static long PAYLOAD_MASK 0xFFFFFFFFFL; /** * 序列号掩码32位 */ private final static long SERIAL_NUM_MASK 0xFFFFFFFFL; /** * 序列号加权运算的秘钥表 */ private final static int[][] PRIME_TABLE { {23, 59, 241, 61, 607, 67, 977, 1217, 1289, 1601}, {79, 83, 107, 439, 313, 619, 911, 1049, 1237}, {173, 211, 499, 673, 823, 941, 1039, 1213, 1429, 1259}, {31, 293, 311, 349, 431, 577, 757, 883, 1009, 1657}, {353, 23, 367, 499, 599, 661, 719, 929, 1301, 1511}, {103, 179, 353, 467, 577, 691, 811, 947, 1153, 1453}, {213, 439, 257, 313, 571, 619, 743, 829, 983, 1103}, {31, 151, 241, 349, 607, 677, 769, 823, 967, 1049}, {61, 83, 109, 137, 151, 521, 701, 827, 1123}, {23, 61, 199, 223, 479, 647, 739, 811, 947, 1019}, {31, 109, 311, 467, 613, 743, 821, 881, 1031, 1171}, {41, 173, 367, 401, 569, 683, 761, 883, 1009, 1181}, {127, 283, 467, 577, 661, 773, 881, 967, 1097, 1289}, {59, 137, 257, 347, 439, 547, 641, 839, 977, 1009}, {61, 199, 313, 421, 613, 739, 827, 941, 1087, 1307}, {19, 127, 241, 353, 499, 607, 811, 919, 1031, 1301} }; /** * 生成兑换码 * * param serialNum 递增序列号 * return 兑换码 */ public static String generateCode(long serialNum, long fresh) { // 1.计算新鲜值 fresh fresh FRESH_MASK; //取最后4位,确保新鲜值在0-15之间 // 2.拼接payloadfresh4位 serialNum32位 long payload fresh FRESH_BIT_OFFSET | serialNum;//将新鲜值左移32位并拼接 // 3.计算验证码 long checkCode calcCheckCode(payload, (int) fresh); System.out.println(checkCode checkCode); // 4.payload做大质数异或运算混淆数据 payload ^ XOR_TABLE[(int) (checkCode FRESH_MASK)]; // 5.拼接兑换码明文: 校验码14位 payload36位 long code checkCode CHECK_CODE_BIT_OFFSET | payload; // 6.转码 return Base32.encode(code); } private static long calcCheckCode(long payload, int fresh) { // 1.获取码表 int[] table PRIME_TABLE[fresh]; // 2.生成校验码payload每4位乘加权数求和取最后13位结果 long sum 0; int index 0; while (payload 0) { sum (payload 0xf) * table[index]; payload 4; } return sum CHECK_CODE_MASK; } public static long parseCode(String code) { if (code null || !code.matches(RegexConstants.COUPON_CODE_PATTERN)) { // 兑换码格式错误 throw new BadRequestException(无效兑换码); } // 1.Base32解码 long num Base32.decode(code); // 2.获取低36位payload long payload num PAYLOAD_MASK; // 3.获取高14位校验码 int checkCode (int) (num CHECK_CODE_BIT_OFFSET); // 4.载荷异或大质数解析出原来的payload payload ^ XOR_TABLE[(checkCode FRESH_MASK)]; // 5.获取高4位fresh int fresh (int) (payload FRESH_BIT_OFFSET FRESH_MASK); // 6.验证格式 if (calcCheckCode(payload, fresh) ! checkCode) { throw new BadRequestException(无效兑换码); } return payload SERIAL_NUM_MASK; } }2.完善优惠券发放功能/*** ****思路分析这里说一下为什么是异步方法因为当优惠券第一次发放的时候同步生成兑换码而由于 ****兑换码的生成数量可能较多因此发放的时候可能会拖慢发放的进度因此这里采用线程池去异步 ****处理 ****/ public void beginIssue(CouponIssueFormDTO dto) { //查询优惠券 Coupon coupon getById(dto.getId()); if (coupon null){ throw new BadRequestException(优惠券不存在); } //判断优惠券状态是否是暂停还是待发放 if (!CouponStatus.PAUSE.equals(coupon.getStatus()) !CouponStatus.DRAFT.equals(coupon.getStatus())){ throw new BizIllegalException(优惠券状态错误); } //判断是否是立刻发放 LocalDateTime issueBeginTime dto.getIssueBeginTime(); LocalDateTime now LocalDateTime.now(); //如果发放时间为null或者当前时间小于等于发放时间 boolean isBegin issueBeginTime null || !issueBeginTime.isAfter(now); //更新优惠券 Coupon c BeanUtils.copyBean(dto, Coupon.class); if (isBegin){ c.setStatus(CouponStatus.ISSUING); c.setIssueBeginTime(now); }else { c.setStatus(CouponStatus.UN_ISSUE); } updateById(c); //是否需要生成兑换码 //判断优惠券的类型是兑换码的类型且状态必须为待发放的状态 if(coupon.getObtainWay() ObtainType.ISSUE CouponStatus.DRAFT.equals(coupon.getStatus())){ coupon.setIssueEndTime(c.getIssueEndTime()); codeService.asynCenerateCode(coupon); }添加的代码块启动类上添加启动异步的注解/** ***注册线程池 ***/ Configuration Slf4j public class PromotionConfig { Bean public Executor generateExchangeCodeExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); // 核心线程数 executor.setMaxPoolSize(5); // 最大线程数 executor.setQueueCapacity(200); // 队列容量 executor.setThreadNamePrefix(exchange-code-handler-); // 线程名称前缀 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略 executor.initialize(); return executor; } }/** ***常量类 **/ public interface PromotionConstants { String COUPON_CODE_SERIAL_KEY coupon:code:serial; }/*** ****思路分析首先通过构造器将redis和key进行绑定后续调用不在需要注入key ***然后通过id自长的方式得到序列号由于兑换码要生成多条因此直接得到最后一个自增长的 ****序列号再通过for循环去拿到第一个自增长的序列号并生成兑换码同时将exchangeCode缺少的属性 ***补上最后放入list中打包一起存入数据库当中 ***/ private BoundValueOperationsString, String serialOps; //该构造器的作用就是将这个redis与key绑定为了后续调用不再需要注入key public ExchangeCodeServiceImpl(StringRedisTemplate redisTemplate) { this.serialOps redisTemplate.boundValueOps(COUPON_CODE_SERIAL_KEY); } /** * 异步生成优惠券代码的方法重写 * 该方法用于异步生成优惠券的特定代码可能是为了提高系统性能和响应速度 * * param coupon 优惠券对象包含需要生成代码的相关信息 */ Override Async(generateExchangeCodeExecutor) public void asynCenerateCode(Coupon coupon) { Integer totalNum coupon.getTotalNum(); //获取redis自增序列号 Long result serialOps.increment(totalNum); if (result null) return; ListExchangeCode list new ArrayList(totalNum); int maxSerialNum result.intValue(); for (int serialNum maxSerialNum - totalNum 1; serialNum maxSerialNum; serialNum) { //生成兑换码 String code CodeUtil.generateCode(serialNum, coupon.getId()); ExchangeCode e new ExchangeCode(); e.setId(serialNum); e.setCode(code); e.setExchangeTargetId(coupon.getId()); e.setExpiredTime(coupon.getIssueEndTime()); list.add(e); } //保存数据库 saveBatch(list); }练习1.实现修改优惠券功能/*** ****思路分析由于修改是另类的新增因此可以按照新增的操作去写只需要在操作数据库的时候 ****将所需要修改的优惠券的coupon扔进去即可。如果是限定还需要删除旧数据再更新新数据 ****/ Transactional public void updateCouponById(CouponFormDTO dto) { Coupon coupon lambdaQuery().eq(Coupon::getId, dto.getId()).one(); if (coupon null){ throw new BadRequestException(优惠券不存在); } if (!CouponStatus.DRAFT.equals(coupon.getStatus())){ throw new BadRequestException(优惠券状态不正确); } Coupon updateCoupon BeanUtils.copyBean(dto, Coupon.class); //更新 lambdaUpdate().eq(Coupon::getId, dto.getId()) .update(updateCoupon); if (!dto.getSpecific()){ //没有范围限定 return; } //2.保存限定范围 Long couponId coupon.getId(); ListLong scopes dto.getScopes(); if (CollUtils.isEmpty(scopes)) { throw new BadRequestException(限定范围不能为空); } //转换po ListCouponScope list scopes.stream().map(bizId - new CouponScope().setBizId(bizId).setCouponId(couponId)).collect(Collectors.toList()); //保存 scopeService.updateBatchById(list); }2.实现删除优惠券功能/** * 根据优惠券ID删除优惠券 * param id 优惠券ID * throws BadRequestException 当优惠券不存在或状态不正确时抛出 */ Override Transactional // 声明事务确保方法执行的事务性 public void deleteCouponById(Long id) { // 根据ID查询优惠券 Coupon coupon lambdaQuery().eq(Coupon::getId, id).one(); // 如果优惠券不存在抛出异常 if (coupon null){ throw new BadRequestException(优惠券不存在); } // 检查优惠券状态 if (!CouponStatus.DRAFT.equals(coupon.getStatus())){ throw new BadRequestException(优惠券状态不正确); } // 执行删除操作 lambdaUpdate() .eq(Coupon::getId, id) .remove(); // 如果优惠券不是限定类型直接返回 if (!coupon.getSpecific()) return; //如果有限定还需要去scope里面删 scopeService.lambdaUpdate().eq(CouponScope::getCouponId, id).remove(); }3.根据id查询优惠券private final CategoryClient categoryClient; /*** ****思路分析由于只传入了id因此需要去数据库查询到对应的优惠券信息然后将其转化成vo ****而对应scope的处理就需要去数据库拿到bizId如果通过远程调用否方式拿到所有的分类 ***拿到分类后还需要将其转化为mapkey就是bizId值就是分类的名称。拿到这个后在通过 ***遍历bizId的方式将对应的标签注入到scopeList中最后填充到vo中返回 ***/ Transactional public CouponDetailVO queryCouponById(Long id) { OptionalCoupon coupon lambdaQuery().eq(Coupon::getId, id).oneOpt(); if (coupon.isEmpty()){ throw new BadRequestException(优惠券不存在); } //将po属性赋值给vo CouponDetailVO vo BeanUtils.copyBean(coupon.get(), CouponDetailVO.class); //补全缺失的scope ListCouponScope list scopeService.lambdaQuery().eq(CouponScope::getCouponId, id).list(); ListLong bizId list.stream().map(CouponScope::getBizId).collect(Collectors.toList()); ListCategoryBasicDTO category categoryClient.getAllOfOneLevel(); MapLong, CouponScopeVO map category.stream().collect(Collectors.toMap(CategoryBasicDTO::getId, c - new CouponScopeVO(c.getId(), c.getName()))); ListCouponScopeVO scope new ArrayList(); bizId.forEach(b - { CouponScopeVO couponScopeVO map.get(b); if (couponScopeVO ! null){ scope.add(couponScopeVO); } }); vo.setScopes(scope); return vo; }4.实现定时发放优惠券功能/*** ****思路分析该功能和昨天写的定时任务类似只需要将数据从数据库中分页查出来然后将 ****数据的状态和发放时间修改并且存入集合中最后批处理即可 ***/ XxlJob(timedCouponIssuanceTask) public void timedCouponIssuanceTask() { //根据筛选条件拿到对应的待发放的优惠券的分页查询列表 int index XxlJobHelper.getShardIndex() ; int shardTotal XxlJobHelper.getShardTotal(); int pageNo index 1; int pageSize 1000; // 查询待发放的优惠券 ListCoupon list new ArrayList(); while (true){ ListCoupon coupons couponService.queryByStatusAndIssueBeginTime(pageNo , pageSize); if (CollUtils.isEmpty(coupons)) break; //分片轮播修改状态 coupons.forEach(e - { e.setStatus(CouponStatus.ISSUING); e.setIssueBeginTime(LocalDateTime.now()); }); list.addAll(coupons); pageNo shardTotal; } // 修改优惠券状态为已发放 couponService.updateBatchById(list); }/*** ****其中分页查询的代码块 ***/ public ListCoupon queryByStatusAndIssueBeginTime(Integer pageNo ,Integer pageSize) { LocalDateTime now LocalDateTime.now(); PageCoupon page lambdaQuery().eq(Coupon::getStatus, CouponStatus.UN_ISSUE) .le(Coupon::getIssueBeginTime, now).page(new Page(pageNo, pageSize)); ListCoupon records page.getRecords(); if (CollUtils.isEmpty(records)) return null; return records; }xxl-job的配置5.实现定时结束发放优惠券功能XxlJob(timedCouponSuspendTask) public void timedCouponSuspendTask() { //根据筛选条件拿到对应的待发放的优惠券的分页查询列表 int index XxlJobHelper.getShardIndex() ; int shardTotal XxlJobHelper.getShardTotal(); int pageNo index 1; int pageSize 1000; // 查询待发放的优惠券 ListCoupon list new ArrayList(); while (true){ ListCoupon coupons couponService.queryByStatusAndIssueEndTime(pageNo , pageSize); if (CollUtils.isEmpty(coupons)) break; //分片轮播修改状态 coupons.forEach(e - { e.setStatus(CouponStatus.PAUSE); e.setIssueEndTime(LocalDateTime.now()); }); list.addAll(coupons); pageNo shardTotal; } // 修改优惠券状态为已暂停 couponService.updateBatchById(list); }public ListCoupon queryByStatusAndIssueEndTime(int pageNo, int pageSize) { LocalDateTime now LocalDateTime.now(); PageCoupon page lambdaQuery().eq(Coupon::getStatus, CouponStatus.ISSUING) .le(Coupon::getIssueEndTime, now).page(new Page(pageNo, pageSize)); ListCoupon records page.getRecords(); if (CollUtils.isEmpty(records)) return null; return records; }6.实现暂停发放优惠券功能public void pauseIssue(Long id) { // 根据ID获取优惠券信息 Coupon coupon getById(id); // 如果优惠券不存在抛出异常 if (coupon null){ throw new BadRequestException(优惠券不存在); } //判断优惠券状态只有发放中才能暂停 if (!CouponStatus.ISSUING.equals(coupon.getStatus())){ throw new BizIllegalException(优惠券状态错误); } //修改优惠券状态 Coupon update new Coupon(); update.setId(id); update.setStatus(CouponStatus.PAUSE); updateById(update);7.实现分页查询兑换码功能/*** ****定义一个返回参数的类 ***/ Data ApiModel(description 兑换码分页数据) NoArgsConstructor AllArgsConstructor public class ExchangeCodeVo { // 兑换码码 String code; // 兑换码对应的ID Integer id; }private final IExchangeCodeService exchangeCodeService; GetMapping(/page) ApiOperation(分页查询兑换码) public PageDTOExchangeCodeVo queryExchangeCodePage(Valid CodeQuery query){ return exchangeCodeService.queryExchangeCodePage(query); }public PageDTOExchangeCodeVo queryExchangeCodePage(CodeQuery query) { PageExchangeCode page lambdaQuery().eq(ExchangeCode::getExchangeTargetId, query.getCouponId()) .eq(ExchangeCode::getStatus, query.getStatus()) .page(query.toMpPage()); ListExchangeCode records page.getRecords(); if (CollUtils.isEmpty(records)) return PageDTO.empty(page); ListExchangeCodeVo vo BeanUtils.copyList(records, ExchangeCodeVo.class); return PageDTO.of(page, vo); }