From e287bcea12c79957fea77fa96865646605645a9d Mon Sep 17 00:00:00 2001 From: Y1NanPing <735289578@qq.com> Date: Fri, 1 Aug 2025 20:58:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC12=E5=A4=A9=20=E4=B8=8B=E8=AE=A2?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/cache/GuiGuCacheAspect.java | 22 +- .../common/config/pool/ThreadPoolConfig.java | 46 ++-- .../common/config/redis/RedissonConfig.java | 52 ++++ .../common/cache/GuiGuCacheAspect.class | Bin 4816 -> 4935 bytes .../common/config/pool/ThreadPoolConfig.class | Bin 2926 -> 2216 bytes .../tingshu/album/AlbumFeignClient.java | 14 +- .../album/impl/AlbumDegradeFeignClient.java | 13 +- .../album/impl/AlbumDegradeFeignClient.class | Bin 2489 -> 2981 bytes .../tingshu/user/client/UserFeignClient.java | 14 +- .../client/impl/UserDegradeFeignClient.java | 19 ++ .../client/impl/UserDegradeFeignClient.class | Bin 1762 -> 2583 bytes service/pom.xml | 1 + .../account/api/UserAccountApiController.java | 17 ++ .../account/service/UserAccountService.java | 2 + .../service/impl/UserAccountServiceImpl.java | 11 + .../tingshu/ServiceAlbumApplication.java | 21 +- .../album/api/TrackInfoApiController.java | 32 +++ .../album/service/AlbumInfoService.java | 6 + .../album/service/TrackInfoService.java | 6 + .../service/impl/AlbumInfoServiceImpl.java | 17 ++ .../service/impl/TrackInfoServiceImpl.java | 93 ++++++- .../album/task/RebuildBloomFilterTask.java | 80 ++++++ .../tingshu/album/task/ReviewTask.java | 51 ++-- .../tingshu/ServiceAlbumApplication.class | Bin 966 -> 2186 bytes .../album/api/AlbumInfoApiController.class | Bin 5738 -> 5738 bytes .../album/api/TrackInfoApiController.class | Bin 7317 -> 8772 bytes .../album/service/AlbumInfoService.class | Bin 1607 -> 1763 bytes .../service/impl/AlbumInfoServiceImpl.class | Bin 13856 -> 14502 bytes .../service/impl/TrackInfoServiceImpl.class | Bin 17554 -> 22040 bytes .../tingshu/album/task/ReviewTask.class | Bin 4888 -> 5261 bytes service/service-cdc/pom.xml | 27 ++ .../com/atguigu/tingshu/CDCApplicaiton.java | 19 ++ .../tingshu/listener/AlbumListener.java | 36 +++ .../tingshu/listener/UserListener.java | 35 +++ .../listener/VipServiceConfigListener.java | 36 +++ .../src/main/resources/bootstrap.properties | 8 + .../order/api/OrderInfoApiController.java | 16 ++ .../order/service/OrderInfoService.java | 3 + .../service/impl/OrderInfoServiceImpl.java | 182 +++++++++++++ .../service/impl/OrderInfoServiceImpl.class | Bin 1122 -> 9069 bytes service/service-search/pom.xml | 8 - .../search/receiver/SearchReciever.java | 68 ++--- .../tingshu/search/service/SearchService.java | 3 + .../search/service/impl/ItemServiceImpl.java | 3 + .../service/impl/SearchServiceImpl.java | 73 +++++- .../com/atguigu/tingshu/search/test01.java | 18 +- .../search/receiver/SearchReciever.class | Bin 2705 -> 4770 bytes .../search/service/SearchService.class | Bin 1513 -> 1607 bytes .../search/service/impl/ItemServiceImpl.class | Bin 5835 -> 5908 bytes .../service/impl/SearchServiceImpl.class | Bin 48945 -> 53137 bytes .../com/atguigu/tingshu/search/test01.class | Bin 1046 -> 1763 bytes .../user/api/UserInfoApiController.java | 100 ++++--- .../api/UserListenProcessApiController.java | 11 +- .../api/VipServiceConfigApiController.java | 26 ++ .../tingshu/user/service/UserInfoService.java | 3 + .../service/impl/UserInfoServiceImpl.java | 247 ++++++++++-------- .../user/api/UserInfoApiController.class | Bin 2902 -> 3848 bytes .../api/UserListenProcessApiController.class | Bin 3164 -> 3277 bytes .../api/VipServiceConfigApiController.class | Bin 814 -> 2518 bytes .../service/impl/UserInfoServiceImpl.class | Bin 11871 -> 13263 bytes 60 files changed, 1174 insertions(+), 265 deletions(-) create mode 100644 common/service-util/src/main/java/com/atguigu/tingshu/common/config/redis/RedissonConfig.java create mode 100644 service/service-album/src/main/java/com/atguigu/tingshu/album/task/RebuildBloomFilterTask.java create mode 100644 service/service-cdc/pom.xml create mode 100644 service/service-cdc/src/main/java/com/atguigu/tingshu/CDCApplicaiton.java create mode 100644 service/service-cdc/src/main/java/com/atguigu/tingshu/listener/AlbumListener.java create mode 100644 service/service-cdc/src/main/java/com/atguigu/tingshu/listener/UserListener.java create mode 100644 service/service-cdc/src/main/java/com/atguigu/tingshu/listener/VipServiceConfigListener.java create mode 100644 service/service-cdc/src/main/resources/bootstrap.properties diff --git a/common/service-util/src/main/java/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.java b/common/service-util/src/main/java/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.java index c0c3a96..a222a46 100644 --- a/common/service-util/src/main/java/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.java +++ b/common/service-util/src/main/java/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.java @@ -38,41 +38,47 @@ public class GuiGuCacheAspect { List argsList = Arrays.asList(pjp.getArgs()); String args = "none"; if (CollUtil.isNotEmpty(argsList)) { - //将argsList转换成字符串,用_拼接 args = argsList.stream() .map(Object::toString) .collect(Collectors.joining("_")); } - //用写这个注解的prefix属性拼接上参数args,得到缓存数据Key String dataKey = guiGuCache.prefix() + args; + //1.2 从Redis中获取缓存数据 Object result = redisTemplate.opsForValue().get(dataKey); + //1.3 命中缓存直接返回即可 if (result != null) { return result; } + //2.尝试获取分布式锁 //2.1 构建锁Key String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX; + //2.2 基于RedissonClient创建锁对象 RLock lock = redissonClient.getLock(lockKey); - boolean flag= lock.tryLock(ALBUM_LOCK_WAIT_PX1, ALBUM_LOCK_EXPIRE_PX2, TimeUnit.SECONDS); + //2.3 尝试获取锁-设置锁过期时间 + boolean flag = + lock.tryLock(ALBUM_LOCK_WAIT_PX1, ALBUM_LOCK_EXPIRE_PX2, TimeUnit.SECONDS); + //3.获取锁成功,执行目标方法,将结果缓存到Redis中,业务执行结束,释放锁 - if(flag){ + if (flag) { + //3.1 执行查询数据库方法 try { - //3.1 执行查询数据库方法 result = pjp.proceed(); - //设置超时时间,防止缓存雪崩 + //3.2 将查询结果放入缓存中,将结果返回 long ttl = RedisConstant.ALBUM_TIMEOUT + RandomUtil.randomInt(600); + redisTemplate.opsForValue().set(dataKey, result, ttl, TimeUnit.SECONDS); return result; } finally { //3.3 释放锁 lock.unlock(); - } - }else{ + } else { //4.获取锁失败,自旋 TimeUnit.MILLISECONDS.sleep(30); return this.doBasicProfiling(pjp, guiGuCache); } } catch (Throwable e) { + //5.如果Redis服务可不用,兜底处理:直接执行目标方法 log.error("Redis服务不可用,请检查Redis服务是否正常!"); return pjp.proceed(); } diff --git a/common/service-util/src/main/java/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.java b/common/service-util/src/main/java/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.java index ee4d346..96f3c38 100644 --- a/common/service-util/src/main/java/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.java +++ b/common/service-util/src/main/java/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.java @@ -20,29 +20,29 @@ public class ThreadPoolConfig { @Autowired(required = false) ZipkinHelper zipkinHelper; - /** - * 基于JDK(JUC)提供线程池Class - */ - @Bean - public Executor threadPoolExecutor() { - //1.获取当前服务器核心数确定核心线程数 - int cpuCoreCount = Runtime.getRuntime().availableProcessors(); - int threadCount = cpuCoreCount * 2; - //2.通过构造方法创建线程池对象 - ThreadPoolExecutor threadPoolExecutor = - new ThreadPoolExecutor( - threadCount, - threadCount, - 0, - TimeUnit.SECONDS, - new ArrayBlockingQueue<>(200), - Executors.defaultThreadFactory(), - new ThreadPoolExecutor.CallerRunsPolicy() - ); - //3.可选:提交创建核心线程 - threadPoolExecutor.prestartCoreThread(); - return threadPoolExecutor; - } +// /** +// * 基于JDK(JUC)提供线程池Class +// */ +// @Bean +// public Executor threadPoolExecutor() { +// //1.获取当前服务器核心数确定核心线程数 +// int cpuCoreCount = Runtime.getRuntime().availableProcessors(); +// int threadCount = cpuCoreCount * 2; +// //2.通过构造方法创建线程池对象 +// ThreadPoolExecutor threadPoolExecutor = +// new ThreadPoolExecutor( +// threadCount, +// threadCount, +// 0, +// TimeUnit.SECONDS, +// new ArrayBlockingQueue<>(200), +// Executors.defaultThreadFactory(), +// new ThreadPoolExecutor.CallerRunsPolicy() +// ); +// //3.可选:提交创建核心线程 +// threadPoolExecutor.prestartCoreThread(); +// return threadPoolExecutor; +// } /** diff --git a/common/service-util/src/main/java/com/atguigu/tingshu/common/config/redis/RedissonConfig.java b/common/service-util/src/main/java/com/atguigu/tingshu/common/config/redis/RedissonConfig.java new file mode 100644 index 0000000..80ff9cf --- /dev/null +++ b/common/service-util/src/main/java/com/atguigu/tingshu/common/config/redis/RedissonConfig.java @@ -0,0 +1,52 @@ +package com.atguigu.tingshu.common.config.redis; + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * redisson配置信息 + */ +@Data +@Configuration +@ConfigurationProperties("spring.data.redis") +public class RedissonConfig { + + private String host; + + private String password; + + private String port; + + private int timeout = 3000; + private static String ADDRESS_PREFIX = "redis://"; + + /** + * 自动装配 + */ + @Bean + RedissonClient redissonSingle() { + Config config = new Config(); + + if (StringUtils.isBlank(host)) { + throw new RuntimeException("host is empty"); + } + + //设置看门狗时间=锁释放时间 + config.setLockWatchdogTimeout(10000); + SingleServerConfig serverConfig = + config.useSingleServer() + .setAddress(ADDRESS_PREFIX + this.host + ":" + port) + .setTimeout(this.timeout); + if (StringUtils.isNotBlank(this.password)) { + serverConfig.setPassword(this.password); + } + return Redisson.create(config); + } +} diff --git a/common/service-util/target/classes/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.class b/common/service-util/target/classes/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.class index 73c2c213c400928ea2137e646104c4e54f4767d3..de2c655317d2c877a1d37f13852c0b58d7ccc85d 100644 GIT binary patch delta 1090 zcmZXSS!`5g6vuzxozC1lcWzsXXqcuTpg?JB8jRXlG#D!-1w;c0!R=vyv4Pepc1mM- zV60kjt=fxg!F}K8X6b;mE@-v5RI4Zm?zq+m-+WU4cd9<#XZ%IF{a zhJf?=)y6U`^nZ(%^i*aXoE@P&(OSSX#PbQ=&Cd*_cSxS6;GiQ*wRFqW^pR zW0BbGs8UK-{LHeb+OB4x5q;~?2kKV#~$S|o5ww#;K{y| zq8*`vF_#bd$Y$K*V?OEI<2fe&nM)o=JdSdK!D(q)-O{`Ge{#brR!51!H@|Dfk z9yNU9y9Ed5e`f@Sarj>So9du{gIAYIC7Wv+6X|p^ZS#|1u0N}ArhEE|rly81n{9sa zXB1Xt)UNQfsACz1r=&+DpU(NNc})&i>$SST-Tnca?AQzGQje9I3790 z?d9q$+akmx)n44ma)Tl6byw&AqvsFz(msuw2e`GYykdZk?v=gloO*wj2Q4PkcN1ni z{RB5O#H%?8mvb~jCs(jvkax;?Bq#F-rfH@`d>$_I)o&>$vw&HY$tve8&d?4jSc*kGDi4Y2H>#@BDj2v*irF=+xRl9_eFe za7ZQx{p2A{SJF}WEDq%|xl~z#0NxY5FPg6IJ`f$|Qw?}r!QbZi34YLDSN4zmtjXek E0Sp-OvH$=8 delta 1034 zcmZ9K*>hA?5XOIJw!7qJ0ue2Shlo}sU=nM9ibkVUVvVK{120e(ij_w@Jm`TFdyIauu; z_kky7GmampkNG<8hyV(yBW50gnfH$Pn+&cXZrth|OMj z!L=@}JmT>vkNMShsrhlkS)JL{*>pbJl}j(ZwOF__dsDu{(Wxgqp2Rmq^M#zDvaL|e zrMvUDG<2rh3b|ZI@iLnY{t3Ie>=`y%JnQiseg1{cMP)DWqQxeU&1@N7;q*q6pSyg) zmlj`n9OUa^r`$HF?_6Sh@9_gI276Uu$*SUQUAZaz=+`FelZRb?;%AFrJTe^d+mm~o zV}^(^Hoy9FD`v!gH&pvqlvgJ#{xno6tnxQjoKsTA(kH^zEX44V;)MLmVeSTQ{9i7r zy@f5iRVUK&Mm}DId z&}}o!VF`0t#ymk>OgoLN@W9iZ#s4OO=UOrUhL_~#12wzi~ete|$aksR#CBd1; zbDomH=Tx?i^87ICg9l{z?*UC0=sx8l><^J#8Y6t7#HU&Zw5F@&&$PbbTix%FijRi& QI4AVi)&4jB;3TPk08GZ%@&Et; diff --git a/common/service-util/target/classes/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.class b/common/service-util/target/classes/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.class index 6f36dc4b5f74cfdf50da3c2eea5b9dbddc616956..223435cc1fffb8d61d04466c1c30bcda05a52e29 100644 GIT binary patch delta 743 zcmYLHOHWfl6#nKudi$8FrL?rRRja6_atnyqN&z25r3hF+(bgBWT%}1vv9v}sk%hmY z&f0{;jd7*XT!O8qcLZa&ymo=bUfOeBWe#_+NRPpMQPW1~82KP3OA! zE@c)yke%?r$Kgi@!|oNMo~hQ1T(w%6Ek8CGlvuWF&Y}c4_F9x(M42o;`pd9dVUCE^ z+C#1BhS4xG#pPnDTwk@C`#BDvLp0?8J1Abu5xWPS9EWj4e35%8iE(rxE;OZwl5P&2 z$RkR`rlE%+CHk1J`LRtXlGmGPX+6%-j}r_bI!(M#0^*sHWGBUzGU!U-l!if$G=`q5 zPu&9;;ZSj!p+7buww0K7%&06IwRFqTS&nnah)?R6>pU)KxX3YvtO(k2_7pC0Okh$x zu>BNadr&;lI@{G!wXsxZU|9xNeX(W~7cwGgFNkkiuV~sI${GxjU;)xQPM47>Rw_nq zrm?h=t5(XTRYZ}3MAt9TMbd0Su#|#fija%e%k<|8n1)P1g^l8jn0ENrW{F^BfEQON z@*S+=D&{C-xQ6RAc82g*DD(mOO=u?ELm_ohnIGPQC%*yjlpZ$GZiRsEVcjM|x@=;f z-WD}+NLNf8ZIzGdTS(A`UK4!;2fX?Cdn7k7Y~qamhNerH6)&CXHA_ppb>1H>8mY*m zK$_fS@&+LZK8)ffZjmd?sO94}t#;gLIV2q2@dG&8Qkxikhw-;mWcY8J-YMp(CZYIm S$X$9}C{fW#)C3lA502j+1B2=S delta 1253 zcmZ8gOLH4V5dLQMSUn~ok}ady@(Y|qa94!LuX|=6#QyGy z{QB>Y4*^W$qx+Gs#LvO>p)k6UP|?HjG?EPc;!`D&ST;9|m5S*ysKvS5mHcdxVNiUc z^zcW>l!{*QQ!qI`<2ZWbyk(bfnU$M`x=}YGIE)lW0R0U9^g`xp1cMk-G0gD{o@E%2 z6ynQ3sK3V0y=L6d>z2E`?il*o1-%O?j^UWyy6aEh`=8I020zUUiI`t2=Jac8pFegD;&~RWWw| z!CW&diL*G(F)o{Ys%ChRi?(eQ%{w%JEY5N~C$WQsUDmgrgeEv%kkBE5yn*HPs$MqT zhRmDdI48j_0xuaxb;dF`jiOPuD{Hk7USvq>HBn>9P!=z9%;3Bb>?9p#Ip%Od{Km${ zvY6*skVSf_$joisbe9ch!>s78X;*e@Nv=yAui~<>lokIKhO;8#>l1$|sb~R9DvBJ- zSP`o4UMP=ia>TERk9`wFwaW22-Vne0=A&=nZ57u!bi5;m{EMLi);J8@5L^C#qH))| zQ`x$>8_0%vxmuS4&55rAN#@9JJ*brPE;RVVXWs)bW>Iitnci58yM zd@W3B@#z*`()=ytI__7r9SnMXv~ZE^d};ItEVgmAh0>#j#GmR^^RevGu51-c*z!~# zpvTowoxm&_xbr{NA-unLJ;UDYfpEOAjW@qXSzZx);)-IFe3yI*KG;r{>+Q2%5A diff --git a/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/AlbumFeignClient.java b/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/AlbumFeignClient.java index fd50ec3..4013307 100644 --- a/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/AlbumFeignClient.java +++ b/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/AlbumFeignClient.java @@ -2,16 +2,14 @@ package com.atguigu.tingshu.album; import com.atguigu.tingshu.album.impl.AlbumDegradeFeignClient; import com.atguigu.tingshu.common.result.Result; -import com.atguigu.tingshu.model.album.AlbumInfo; -import com.atguigu.tingshu.model.album.BaseCategory1; -import com.atguigu.tingshu.model.album.BaseCategory3; -import com.atguigu.tingshu.model.album.BaseCategoryView; +import com.atguigu.tingshu.model.album.*; import com.atguigu.tingshu.vo.album.AlbumStatVo; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import java.util.List; +import java.util.Map; /** *

@@ -30,9 +28,11 @@ public interface AlbumFeignClient { */ @GetMapping("/albumInfo/getAlbumInfo/{id}") public Result getAlbumInfo(@PathVariable Long id); + /** * 后续改为Redis缓存 * 根据三级分类ID查询分类视图 + * * @param category3Id * @return */ @@ -44,11 +44,13 @@ public interface AlbumFeignClient { @GetMapping("/albumInfo/getAlbumStatVo/{albumId}") - public ResultgetAlbumStatVo(@PathVariable Long albumId); + public Result getAlbumStatVo(@PathVariable Long albumId); @GetMapping("/category/findAllCategory1") - Result> findAllCategory1(); + Result> findAllCategory1(); + @GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}") + public Result> findWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount); } diff --git a/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.java b/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.java index a4dec8c..4a5c3ec 100644 --- a/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.java +++ b/service-client/service-album-client/src/main/java/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.java @@ -3,15 +3,13 @@ package com.atguigu.tingshu.album.impl; import com.atguigu.tingshu.album.AlbumFeignClient; import com.atguigu.tingshu.common.result.Result; -import com.atguigu.tingshu.model.album.AlbumInfo; -import com.atguigu.tingshu.model.album.BaseCategory1; -import com.atguigu.tingshu.model.album.BaseCategory3; -import com.atguigu.tingshu.model.album.BaseCategoryView; +import com.atguigu.tingshu.model.album.*; import com.atguigu.tingshu.vo.album.AlbumStatVo; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; +import java.util.Map; @Component @Slf4j @@ -47,4 +45,11 @@ public class AlbumDegradeFeignClient implements AlbumFeignClient { log.error("[专辑服务]提供远程调用方法findAllCategory1执行服务降级"); return null; } + + @Override + public Result> findWaitBuyTrackList(Long trackId, Integer trackCount) { + log.error("[专辑服务]提供远程调用方法findWaitBuyTrackList执行服务降级"); + return null; + } + } diff --git a/service-client/service-album-client/target/classes/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.class b/service-client/service-album-client/target/classes/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.class index fb595b320f00a45937b09671bf4d1398c0909c96..6a985bcb6bfe33a52b5c0cb697295ae864303acc 100644 GIT binary patch delta 919 zcma))O-~b16o%hBwVigH1jA>7Rw*hi#kOdxDD{hiN=>6CHgP@j(G<-z!NJ71aIFy% zGb@b?Ss1of76J?R`V0I6g8T{fy?1K4iI_Oa+;h*&InR5}y=SxQeV_I9=hkNcmvFOb z-8CXn92Ka%dN-7xue-DNoVr(9dQx3YYd=EaBD@)FT37cZEn$T zhDWMJ0n?N6E&nzZ8-f2qREYc6j%8hID}!Yb`XFxM(7EgLIxou{WoTOTE<$;0eWbAReZ1V z?N+?Z&nlTwMui0(;51(YX9&x;V86`xvpv{h!jAj{R?uOEHtZ~6=K>hNO%X4vvOmJ? z?At_#0r5eJ?ujHQGN_4U^^(VQm9e(Ud8%A!mHZ{D_n#y!rO|ZQxDFd{!-|Amnsk>Y{+P_(QwN1Oj3F zotf_j`mY{G)O1Xv>tYYbrrgdL8*|I+yTmnjmaL@I9T z{!v;b8mb&orHNy{L@9o9{JT-hRnlq+O-*G0Q}1>Hi@(KSv;|V+XM5Hg{4Pv6UXdE_ RECtTEr{NqI^s?|0_y;kmE>-{l diff --git a/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/UserFeignClient.java b/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/UserFeignClient.java index 7b8ec66..b48302f 100644 --- a/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/UserFeignClient.java +++ b/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/UserFeignClient.java @@ -1,6 +1,7 @@ package com.atguigu.tingshu.user.client; import com.atguigu.tingshu.common.result.Result; +import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.user.client.impl.UserDegradeFeignClient; import com.atguigu.tingshu.vo.user.UserInfoVo; import org.springframework.cloud.openfeign.FeignClient; @@ -19,10 +20,11 @@ import java.util.Map; * * @author atguigu */ -@FeignClient(value = "service-user",path = "/api/user",fallback = UserDegradeFeignClient.class) +@FeignClient(value = "service-user", path = "/api/user", fallback = UserDegradeFeignClient.class) public interface UserFeignClient { /** * 根据用户ID查询用户信息 + * * @param userId * @return */ @@ -35,4 +37,14 @@ public interface UserFeignClient { @RequestBody List needCheckPayStateTrackIdList ); + + @GetMapping("/vipServiceConfig/getVipServiceConfig/{id}") + public Result getVipServiceConfig(@PathVariable Long id); + + @GetMapping("/userInfo/isPaidAlbum/{albumId}") + public Result isPaidAlbum(@PathVariable Long albumId); + + @GetMapping("/userInfo/findUserPaidTrackList/{albumId}") + public Result> findUserPaidTrackList(@PathVariable Long albumId); + } diff --git a/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.java b/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.java index aa4a96e..1ab1b00 100644 --- a/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.java +++ b/service-client/service-user-client/src/main/java/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.java @@ -2,6 +2,7 @@ package com.atguigu.tingshu.user.client.impl; import com.atguigu.tingshu.common.result.Result; +import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.user.client.UserFeignClient; import com.atguigu.tingshu.vo.user.UserInfoVo; import lombok.extern.slf4j.Slf4j; @@ -25,4 +26,22 @@ public class UserDegradeFeignClient implements UserFeignClient { log.error("[用户服务]提供远程调用方法userIsPaidTrack执行服务降级"); return null; } + + @Override + public Result getVipServiceConfig(Long id) { + log.error("[用户服务]提供远程调用方法userIsPaidTrack执行服务降级"); + return null; + } + + @Override + public Result isPaidAlbum(Long albumId) { + log.error("[用户服务]提供远程调用方法isPaidAlbum执行服务降级"); + return null; + } + + @Override + public Result> findUserPaidTrackList(Long albumId) { + log.error("[用户服务]提供远程调用方法findUserPaidTrackList执行服务降级"); + return null; + } } diff --git a/service-client/service-user-client/target/classes/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.class b/service-client/service-user-client/target/classes/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.class index 37d703fd4b01f34456d956c812253a9e1cb48411..31de6ac92b540b8f76fbdc51dec2e1ea4346bfad 100644 GIT binary patch delta 940 zcmaiyO-~b16o%hB?a)%jk05QefELl#kK#~7MGFX&B_rPTq5$_nh3%2cGxDKK|PI0AK>e ztyoobL~&H0=nad^pwpPQoYHjV-b!`%$>z6>r@JrLzpQP1f3f~`XETbFzzwMzU2pN1mm+v|cZ`t)# zXVEUYwI!!4pgAQ0*L$WFyxCB~thyz;Vy=*8wrUHL1%a-BX@Q%vEGP_$QJ!*jw(?^Ad!IRJqax?pfn5uSB)a2@m>>51$q=41pM#%ZpG zI_tV$a5Rv z@4L86$rZmP=4L6A?X~-ufaoHxkf+G%!B!IS>G0a3cwd z-d)3GX1b@RXJ;Q-#>^^?x96dT;y%7r&8)pZu(Rj%6e zX0F=TS(ZcJcXv4ENQ_%FZ6!p+-?C#8PX7O+>yMSJnY^+vCf1+y#Ff&APswF;Dr7n1 Ie8OJ9FW_7qlmGw# diff --git a/service/pom.xml b/service/pom.xml index 563fc12..5631fea 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -20,6 +20,7 @@ service-order service-payment service-user + service-cdc diff --git a/service/service-account/src/main/java/com/atguigu/tingshu/account/api/UserAccountApiController.java b/service/service-account/src/main/java/com/atguigu/tingshu/account/api/UserAccountApiController.java index ba52aea..b493aa1 100644 --- a/service/service-account/src/main/java/com/atguigu/tingshu/account/api/UserAccountApiController.java +++ b/service/service-account/src/main/java/com/atguigu/tingshu/account/api/UserAccountApiController.java @@ -1,11 +1,18 @@ package com.atguigu.tingshu.account.api; import com.atguigu.tingshu.account.service.UserAccountService; +import com.atguigu.tingshu.common.login.GuiGuLogin; +import com.atguigu.tingshu.common.result.Result; +import com.atguigu.tingshu.common.util.AuthContextHolder; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.math.BigDecimal; + @Tag(name = "用户账户管理") @RestController @RequestMapping("api/account") @@ -14,6 +21,16 @@ public class UserAccountApiController { @Autowired private UserAccountService userAccountService; + @GuiGuLogin + @Operation(summary = "查询当前用户账户可用余额") + @GetMapping("/userAccount/getAvailableAmount") + public Result getAvailableAmount(){ + Long userId = AuthContextHolder.getUserId(); + BigDecimal Amount = userAccountService.getAvailableAmount(userId); + return Result.ok(Amount); + } + + } diff --git a/service/service-account/src/main/java/com/atguigu/tingshu/account/service/UserAccountService.java b/service/service-account/src/main/java/com/atguigu/tingshu/account/service/UserAccountService.java index 4ef6495..01bbbf0 100644 --- a/service/service-account/src/main/java/com/atguigu/tingshu/account/service/UserAccountService.java +++ b/service/service-account/src/main/java/com/atguigu/tingshu/account/service/UserAccountService.java @@ -22,4 +22,6 @@ public interface UserAccountService extends IService { * @param orderNo 订单编号 */ void saveUserAccountDetail(Long userId, String title, BigDecimal amount, String orderNo); + + BigDecimal getAvailableAmount(Long userId); } diff --git a/service/service-account/src/main/java/com/atguigu/tingshu/account/service/impl/UserAccountServiceImpl.java b/service/service-account/src/main/java/com/atguigu/tingshu/account/service/impl/UserAccountServiceImpl.java index f1cfad4..d1c2575 100644 --- a/service/service-account/src/main/java/com/atguigu/tingshu/account/service/impl/UserAccountServiceImpl.java +++ b/service/service-account/src/main/java/com/atguigu/tingshu/account/service/impl/UserAccountServiceImpl.java @@ -6,6 +6,7 @@ import com.atguigu.tingshu.account.service.UserAccountService; import com.atguigu.tingshu.common.constant.SystemConstant; import com.atguigu.tingshu.model.account.UserAccount; import com.atguigu.tingshu.model.account.UserAccountDetail; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -58,4 +59,14 @@ public class UserAccountServiceImpl extends ServiceImpl().eq(UserAccount::getUserId, userId) + ); + BigDecimal availableAmount = userAccount.getAvailableAmount(); + return availableAmount; + + } } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/ServiceAlbumApplication.java b/service/service-album/src/main/java/com/atguigu/tingshu/ServiceAlbumApplication.java index 56d3f89..faffa69 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/ServiceAlbumApplication.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/ServiceAlbumApplication.java @@ -1,5 +1,11 @@ package com.atguigu.tingshu; +import com.atguigu.tingshu.common.constant.RedisConstant; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @@ -10,10 +16,21 @@ import org.springframework.scheduling.annotation.EnableScheduling; @EnableDiscoveryClient @EnableFeignClients @EnableScheduling //开启定时任务功能 -public class ServiceAlbumApplication { - +@Slf4j +public class ServiceAlbumApplication implements CommandLineRunner { + @Autowired + private RedissonClient redissonClient; public static void main(String[] args) { SpringApplication.run(ServiceAlbumApplication.class, args); } + @Override + public void run(String... args) throws Exception { + RBloomFilter bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER); + if (!bloomFilter.isExists()){ + log.info("初始化布隆过滤器,将布隆过滤器配置信息写入redis中采用hash结构存储"); + bloomFilter.tryInit(10000, 0.03); + + } + } } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/api/TrackInfoApiController.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/api/TrackInfoApiController.java index 2c9ce8c..f840dc6 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/api/TrackInfoApiController.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/api/TrackInfoApiController.java @@ -7,6 +7,7 @@ import com.atguigu.tingshu.common.login.GuiGuLogin; import com.atguigu.tingshu.common.result.Result; import com.atguigu.tingshu.common.util.AuthContextHolder; import com.atguigu.tingshu.model.album.TrackInfo; +import com.atguigu.tingshu.model.user.UserInfo; import com.atguigu.tingshu.query.album.TrackInfoQuery; import com.atguigu.tingshu.vo.album.AlbumTrackListVo; import com.atguigu.tingshu.vo.album.TrackInfoVo; @@ -162,6 +163,37 @@ public class TrackInfoApiController { return Result.ok(trackStatVo); } + /** + * 基于用户选择声音作为起始标准获取未购买声音数量,得到分集购买列表 + * + * @param trackId + * @return [{name:"本集",price:0.1,trackCount:1},{name:"后10集",price:1,trackCount:10},...,{name:"全集",price:4.3,trackCount:43},] + */ + @GuiGuLogin + @Operation(summary = "基于用户选择声音作为起始标准获取未购买声音数量,得到分集购买列表") + @GetMapping("/trackInfo/findUserTrackPaidList/{trackId}") + public Result>> findUserTrackPaidList(@PathVariable Long trackId){ + Long userId = AuthContextHolder.getUserId(); + List> list =trackInfoService.findUserTrackPaidList(trackId,userId); + return Result.ok(list); + } + /** + * 基于选择购买声音ID作为起始获取指定数量未购买声音列表 + * + * @param trackId + * @param trackCount + * @return + */ + @GuiGuLogin + @Operation(summary = "基于选择购买声音ID作为起始获取指定数量未购买声音列表") + @GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}") + public Result> findWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount){ + Long userId = AuthContextHolder.getUserId(); + List list =trackInfoService.findWaitBuyTrackList(trackId,trackCount,userId); + return Result.ok( list); + + } + diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/AlbumInfoService.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/AlbumInfoService.java index 2cb5807..aa5fe2b 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/AlbumInfoService.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/AlbumInfoService.java @@ -30,4 +30,10 @@ public interface AlbumInfoService extends IService { List findUserAllAlbumList(Long userId); AlbumStatVo getAlbumStatVo(Long albumId); + /** + * 查询指定状态专辑ID列表 + * @param status + * @return + */ + List findAlbumListByStatus(String status); } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/TrackInfoService.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/TrackInfoService.java index 98b6f11..cd8d8b9 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/TrackInfoService.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/TrackInfoService.java @@ -6,6 +6,7 @@ import com.atguigu.tingshu.vo.album.*; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.List; import java.util.Map; public interface TrackInfoService extends IService { @@ -37,4 +38,9 @@ public interface TrackInfoService extends IService { TrackStatVo getTrackStatVo(Long trackId); void updateStat(TrackStatMqVo mqVo); + + List> findUserTrackPaidList(Long trackId, Long userId); + + List findWaitBuyTrackList(Long trackId, Integer trackCount, Long userId); + } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.java index d330573..0b0895c 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.java @@ -241,4 +241,21 @@ public class AlbumInfoServiceImpl extends ServiceImpl findAlbumListByStatus(String status) { + List albumInfos = albumInfoMapper.selectList( + new LambdaQueryWrapper() + .eq(AlbumInfo::getStatus, status) + .select(AlbumInfo::getId) + ); + if(CollUtil.isNotEmpty(albumInfos)){ + return albumInfos.stream().map(AlbumInfo::getId).collect(Collectors.toList()); + } + return null; + + + } } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.java index 393859d..c8b584b 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.java @@ -1,6 +1,7 @@ package com.atguigu.tingshu.album.service.impl; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import com.atguigu.tingshu.album.mapper.AlbumInfoMapper; import com.atguigu.tingshu.album.mapper.AlbumStatMapper; @@ -15,11 +16,13 @@ import com.atguigu.tingshu.model.album.AlbumInfo; import com.atguigu.tingshu.model.album.AlbumStat; import com.atguigu.tingshu.model.album.TrackInfo; import com.atguigu.tingshu.model.album.TrackStat; +import com.atguigu.tingshu.model.user.UserInfo; import com.atguigu.tingshu.query.album.TrackInfoQuery; import com.atguigu.tingshu.user.client.UserFeignClient; import com.atguigu.tingshu.vo.album.*; import com.atguigu.tingshu.vo.user.UserInfoVo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -30,9 +33,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import static com.atguigu.tingshu.common.constant.SystemConstant.ALBUM_PAY_TYPE_REQUIRE; @@ -290,7 +291,7 @@ public class TrackInfoServiceImpl extends ServiceImpl track.getOrderNum() > tracksForFree) - .forEach(track-> track.setIsShowPaidMark(payStatusMap.get(track.getTrackId()) == 0)); + .forEach(track -> track.setIsShowPaidMark(payStatusMap.get(track.getTrackId()) == 0)); } } return pageInfo; @@ -299,7 +300,7 @@ public class TrackInfoServiceImpl extends ServiceImpl() @@ -325,8 +325,7 @@ public class TrackInfoServiceImpl extends ServiceImpl() @@ -339,7 +338,83 @@ public class TrackInfoServiceImpl extends ServiceImpl> findUserTrackPaidList(Long trackId, Long userId) { + //根据声音id去查询声音的属性 + TrackInfo trackInfo = trackInfoMapper.selectById(trackId); + //查出来声音是哪个专辑的 + Long albumId = trackInfo.getAlbumId(); + //获取声音的排序值 + Integer orderNum = trackInfo.getOrderNum(); + //2.根据"起始"声音序号+专辑ID获取所有“待购买”声音列表(可能包含已购买声音) + List waitBuyTrackList = trackInfoMapper.selectList( + new LambdaQueryWrapper() + .eq(TrackInfo::getAlbumId, albumId) + .ge(TrackInfo::getOrderNum, orderNum) + .select(TrackInfo::getId) + ); + //3.远程调用用户服务获取已购买声音ID列表 + List userPaidTrackIdList = userFeignClient.findUserPaidTrackList(albumId).getData(); + if (CollUtil.isNotEmpty(userPaidTrackIdList)) { + //4.如果存在已购声音ID,则将已购声音ID从待购买声音列表中移除,得到未购买声音数量 + waitBuyTrackList = waitBuyTrackList.stream() + .filter(f -> !userPaidTrackIdList.contains(f.getId())) + .collect(Collectors.toList()); + } + //5.基于未购买声音数量,生成分集购买列表 + //查出来未存在的专辑数量 + int size = waitBuyTrackList.size(); + AlbumInfo albumInfo = albumInfoMapper.selectById(albumId); + BigDecimal price = albumInfo.getPrice(); + //5.1 创建分集购买集合List + List> list = new ArrayList<>(); + //5.2 构建"本集"分集购买对象 + Map currMap = new HashMap<>(); + currMap.put("name", "本集"); + currMap.put("price", price); + currMap.put("trackCount", 1); + list.add(currMap); + //5.3 构建其他"后*集"分集购买对象 未购买数量:23 43 + for (int i = 10; i <= 50; i += 10) { + Map map = new HashMap<>(); + if (size >= i) { + map.put("name", "后" + i + "集"); + map.put("price", price.multiply(new BigDecimal(i))); + map.put("trackCount", i); + list.add(map); + } else { + map.put("name", "全" + size + "集"); + map.put("price", price.multiply(new BigDecimal(size))); + map.put("trackCount", size); + list.add(map); + break; + } + } + return list; + } + @Override + public List findWaitBuyTrackList(Long trackId, Integer trackCount, Long userId) { + //1.根据选择声音ID查询声音,得到专辑ID跟序号 + TrackInfo trackInfo = trackInfoMapper.selectById(trackId); + Long albumId = trackInfo.getAlbumId(); + Integer orderNum = trackInfo.getOrderNum(); + + //2.远程调用用户服务获取已购买声音ID列表 + List userPaidTrackIdList = userFeignClient.findUserPaidTrackList(albumId).getData(); + + //3.获取指定数量未购买声音列表 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(TrackInfo::getAlbumId, albumId) + .eq(TrackInfo::getOrderNum, orderNum) + .last("limit " + trackCount) + .select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId); + if (CollUtil.isNotEmpty(userPaidTrackIdList)) { + queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList); + } + List trackInfoList = trackInfoMapper.selectList(queryWrapper); + return trackInfoList; + } } diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/task/RebuildBloomFilterTask.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/task/RebuildBloomFilterTask.java new file mode 100644 index 0000000..e3857bc --- /dev/null +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/task/RebuildBloomFilterTask.java @@ -0,0 +1,80 @@ +package com.atguigu.tingshu.album.task; + +import com.atguigu.tingshu.album.service.AlbumInfoService; +import com.atguigu.tingshu.common.constant.RedisConstant; +import com.atguigu.tingshu.common.constant.SystemConstant; +import com.atguigu.tingshu.model.album.AlbumInfo; +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Slf4j +public class RebuildBloomFilterTask { + + @Autowired + private RedissonClient redissonClient; + @Autowired + private AlbumInfoService albumInfoService; + + + + @Scheduled(cron = "0 0 2 1 * ?") + public void rebuildBloomFilter() { + //1.获取到原有布隆过滤器对象 得到配置信息:期望数据量,现有数量预估值,误判率 + RBloomFilter oldBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER); + long count = oldBloomFilter.count(); + long expectedInsertions = oldBloomFilter.getExpectedInsertions(); + double falseProbability = oldBloomFilter.getFalseProbability(); + //2.如果现有数量预估值大于期望数据规模,进行扩容 + if(count>=expectedInsertions){ + log.info("当前布隆过滤器已满,开始扩容"); + //2.1 新建布隆过滤器对象,初始化配置信息不变 + RBloomFilter newBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER + ":new"); + newBloomFilter.tryInit(expectedInsertions * 2, falseProbability); + //2.2 将现有过审专辑ID添加到新布隆过滤器对象中 + List albumList = albumInfoService.findAlbumListByStatus(SystemConstant.ALBUM_STATUS_PASS); + for (Long albumId : albumList) { + newBloomFilter.add(albumId); + } + log.info("新布隆过滤器已创建完成"); + //2.3 删除旧布隆过滤器对象(配置&位图) + oldBloomFilter.delete(); + //2.4 重命名布隆过滤器,改为原有名称 + newBloomFilter.rename(RedisConstant.ALBUM_BLOOM_FILTER); + log.info("扩容结束"); + }else{ + log.info("开始重建布隆过滤器"); + //3.如果现有数量预估值小于期望数据规模,进行重建 + //3.1 新建布隆过滤器对象,初始化配置信息不变 + RBloomFilter newBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER + ":new"); + newBloomFilter.tryInit(expectedInsertions, falseProbability); + try { + //3.2 将现有过审专辑ID添加到新布隆过滤器对象中 + } catch (Exception e) { + throw new RuntimeException(e); + } + List albumIdList = albumInfoService.findAlbumListByStatus(SystemConstant.ALBUM_STATUS_PASS); + for (Long albumId : albumIdList) { + newBloomFilter.add(albumId); + } + //3.3 删除旧布隆过滤器对象(配置&位图) + oldBloomFilter.delete(); + + //3.4 重命名布隆过滤器,改为原有名称 + newBloomFilter.rename(RedisConstant.ALBUM_BLOOM_FILTER); + log.info("重建布隆过滤器结束"); + + } + + + + + } +} diff --git a/service/service-album/src/main/java/com/atguigu/tingshu/album/task/ReviewTask.java b/service/service-album/src/main/java/com/atguigu/tingshu/album/task/ReviewTask.java index 28d7e6e..52f59c1 100644 --- a/service/service-album/src/main/java/com/atguigu/tingshu/album/task/ReviewTask.java +++ b/service/service-album/src/main/java/com/atguigu/tingshu/album/task/ReviewTask.java @@ -8,6 +8,8 @@ import com.atguigu.tingshu.model.album.TrackInfo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -27,34 +29,41 @@ public class ReviewTask { @Autowired private AuditService auditService; + @Autowired + private RedissonClient redissonClient; /** * 每5秒检查一次审核结果 */ @Scheduled(cron = "0/5 * * * * ?") public void checkReviewResultTask() { - log.info("开始检查审核任务"); - //1.查询审核状态为"审核中"声音列表 - List trackInfoList = trackInfoMapper.selectList( - new LambdaQueryWrapper() - .eq(TrackInfo::getStatus, SystemConstant.TRACK_STATUS_REVIEWING) - .select(TrackInfo::getId, TrackInfo::getReviewTaskId) - .last("limit 100") - ); - //2.遍历列表,查询审核结果 - if (CollUtil.isNotEmpty(trackInfoList)) { - for (TrackInfo trackInfo : trackInfoList) { - String suggestion = auditService.getReviewResult(trackInfo.getReviewTaskId()); - if (StringUtils.isNotBlank(suggestion)) { - //3.根据审核任务结果更新声音审核状态 - if ("pass".equals(suggestion)) { - trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS); - } else if ("block".equals(suggestion)) { - trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS); - } else if ("review".equals(suggestion)) { - trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL); + + RLock lock = redissonClient.getLock("reviewtask:lock"); + boolean b = lock.tryLock(); + if (b) { + log.info("开始检查审核任务"); + //1.查询审核状态为"审核中"声音列表 + List trackInfoList = trackInfoMapper.selectList( + new LambdaQueryWrapper() + .eq(TrackInfo::getStatus, SystemConstant.TRACK_STATUS_REVIEWING) + .select(TrackInfo::getId, TrackInfo::getReviewTaskId) + .last("limit 100") + ); + //2.遍历列表,查询审核结果 + if (CollUtil.isNotEmpty(trackInfoList)) { + for (TrackInfo trackInfo : trackInfoList) { + String suggestion = auditService.getReviewResult(trackInfo.getReviewTaskId()); + if (StringUtils.isNotBlank(suggestion)) { + //3.根据审核任务结果更新声音审核状态 + if ("pass".equals(suggestion)) { + trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS); + } else if ("block".equals(suggestion)) { + trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS); + } else if ("review".equals(suggestion)) { + trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL); + } + trackInfoMapper.updateById(trackInfo); } - trackInfoMapper.updateById(trackInfo); } } } diff --git a/service/service-album/target/classes/com/atguigu/tingshu/ServiceAlbumApplication.class b/service/service-album/target/classes/com/atguigu/tingshu/ServiceAlbumApplication.class index 1f84c33d1205bb28feba0f31916fda753f8a06e7..ce62a14787f6708ccb6dd277bba3abb6dbf2e9ef 100644 GIT binary patch literal 2186 zcmaJ?ZBrXn6n<`a*#K#aDNw=kCe{KC-C$p*m5ULbZM^J-WhQ>KQb46f{Qg9HI;dZ9Zc-j<6D@!7bWnj)&39aI^H`m!)q%Dt&ynwD$hi{pG(NuWUT}x^!2 z_vG8fppp9dE2X_h9Yn-%A~95|q;syn-_fifb=kpiW?@>Ag7K zN1byPuPGRcAdX>%BTomL)O1(aiwt#iJU#*p$AW?VVlG#1AWbSLYPbDcB|%lCSu;t) z;?qUT;x2QFBl7cvB zgq$5nxte}!N-LBxsh?QTc|kIvqhOkLvCCyUGt^DPx>;t~`jjZzI;Zpr=A-+dc2as_ z7^!@+l1?=`MWP*#^>~?F$7mwJew6K4MF+YmiZd!@MEs``8R}x5+l%UiCC_?%TlV>p zoK&ta--#%2E6a_XRkog#-@c_oro$EEJNhi2DH0&LU|ss*Q#*5UA}WP$FGkl`2yK#k zsCh$@#YuOLzH4cGgT`ta>2{;{C`l@fjK-S&U%`H)j}XS_EvJDdBA(avxmZ9kzWdVC#yA;g-XHeqlbr)uEEtoCwlA#yD38S^$cne9&c8YA}PJPWK(p)A@80;U!6!Lh5BnjQsVD@38c987$ rpdf&v${Jq%89fpWDk^&WXdWh?*GLNCZSO#$9Zp delta 103 zcmaE*^Gat!z96I2Aj^l&h>e8;x(wAg!AMp#4KpCtV2D_;@)bx+v5OxmE|mf% zLU}+;NkmG~(iQ>vP%*2k{(`s#qMtdm1)~(h66j_w}|DvRYNX$;wUJirwD2hPu`Q)u7qs zZg7^gxEl(>J5Kx)nP=Z_YnDS3!pCWNYjsG^kJ@Q3JKR)P<1HxUpN6w~Mtp%@5EX0X zc=6b0dMpyE1H*F*bG?7{k1cjjFL#_R%q7Sa>|NFf+F_H66Hhe(`!&psRDK zcRJ8_X>sVz(&XKRS-(nK9Plr9^*wvk6PO*CS+M>$fD=Q?|d;@K76C4%}u?NOhN^<)3AFrd#^r{=+K|V zr)#~dx+O&IvI+5x$EiNSyHxR3jLn3Ki_FXo&OKd$dsh_IF#Z990xFwY_%%&0*v zzcs8!9e-8pJ&r9tksHmffRmkbWqt;%{GDLri&H%sSQo>p57Ef%CO#KkRpbaG&5T57 zOawmS#{##6n8H&F6F>Ig6SVT+seg-vbRtMbyJe4VgFw8-eXf*(!nAC6GeK zUPV0in7}24z$M{0PO#R-j8AcL1ZHoR%Cju z4ja&kE=Gbvsz^5@LdZBP+kg$;25W^{{ib!X#?xW?lOv6FaNE!q+Rh_Ayzl_@V?P3Jq;_^mFQf2bVC&gWC0FiE5n| z!c!cHZ{(V*Gh3cCMfYCQRAVYfGZZuR&I;3FboFNnoUl-f~%qFLvs3hi!t52 z=u#?Tm3B+tY{Jpb_u;g};ou}tpx|=t1`fmPV!AMxY4W$Y7MeU9&U+uOiw&^iCS4ZF7_14;MQ_d?l-1f&t^A&T0D;_ni z*nGu|Njl;}xs5w3ZW|*!<+dNf#s1L*=5Dyy2NVES`;O6|LeylcU79tC&DlA#c-M-( zJ7$pN=L0LmEvPOVKU2qfQ ed#LUv3m&qC1&>1Ik7>Ah51tS?@HF%;{qjHV67#+Q delta 1442 zcmZux$xc*36g|}wp^>H$(gu-kOpF-l2)NS(jENf)NgN0^;4C-~K;sPJjPvwt5pe)# zaSD;dDRJe>-*CsuFHp~|di^K}i&s_ezB`4zX*{Pk z;?$P>>uaJh1ceRUeaOQso^4w$--FsCMUQH}Del3_&roAJq?xNE01{~7DhcR-R&3<1 zP>4-@li)TM@NPT$GnO5+w9yh&bOhS@_^?yAGKZ&KbljcB9_-~|Un^7^`*A=rC=@Fk6l^XIY?d-fH+L7tVGG6)4I@u6 z=qNpo@lh;uoPDa4^QUx$MiJvHu%2rp8*aipGFnM2!gxLT2WmVtH8?>GGKCtP)M(_? zgI4{2%|3H@+7bQ9-u24kOaEiUd1(5y`IJm+~mQ` z`j$qOBjPovR)ug|k9oAL+|pks$?dYB6nLZ-z(4^5@dcSIT;+T)}^`#%&MHhJ+Jb>^uE{NzD$4- zFR2>QP4Wv$mIc0JNwYH%U!*b2CNm&ELG}0P)6wET`0qZXD*ay)5(5rfq5~NY#%9>OXwA2;^qo?%~sx|F^aeR J^3I#m`~OZ$uO9#a diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/service/AlbumInfoService.class b/service/service-album/target/classes/com/atguigu/tingshu/album/service/AlbumInfoService.class index f72b6a724c8ea3dd2ae9dea6cb9435be364af4d9..f5c873ed4735a94c6cc073b829d846897a21ba6e 100644 GIT binary patch delta 203 zcmX@k^O%?G)W2Q(7#J9A8MHQX9cQT*P0P$nam-07&GpGFE^(?1E=epYEoNj;(eTMi zEKAhSNz6;v4=yRn%uBb{gb0s)W2Q(7#J9A8Pqm%9cP&w#JZf3WAZQ7Jwgmj45B~@CI)2&6)>#|0DTAx AcmMzZ diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.class b/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.class index 8b349ab6dd6db0186c8cbbe63a9b7306cbbe03d7..4aae1f27be7beb8e00e3727405c032e61fd905dc 100644 GIT binary patch delta 3896 zcmbVO33yc18U4>JnT1ETnLt^R03l&A2?Rn!ECj@WB@jTM7y@V#NJt1F!7Nymc2HVH z#r4@GwF+&is6`tZ8b}ZX*_5`ncF|gDk=9x(tqWbLh@N}j%wz~@^)p{)=AHlEd;aq; z_rANW{?Qw}CoXg!5RpQBu**9>`SN7(VyGdrkqSuHx3aZiMa!zj zhP5O-~5b8S&$RW-yO$S!sx0P)sTw@j;F-nyo^ z$}N|OPbYUVSQ46oHY^cebx4f=;g=YM$fk|eeFBq4ODF&vRYkYaB z>A1>3kzz8!ev0ZVn<;4qB->2#4>Q|Gdfdw6=M2m=-|}Uk#Ekb(bj-EJ2b)#?fmmQ} z@Q=dh&F%h-)cLs95T|6hP~t_Y?DS#@mb$Raz;(FZeBHkwuEIbimYb7PQXN&0e0uRe z;7;h+m~z@ZIAtXR)R`(-)!cH`dMiVakpVLvgUpjdjec>>I%|*#5u3+1*{YNdxTLY6 zX3~^N9p!0ud;ESc4&WIV4jMRwXFJvocO&_zR{}U@;74dr#B-3<0rRvo#oRh#ar_A{ zevF^E@KZx>#tWu1**@o>t^Ui=2HxG=*&Kqe&OBxI_^ z)c6^LX$$s0<(0?qN+M3jj3k-;GlzKo$keEjKbhk)=MDR_67;5lAD}(KiMPDs!Wjdj z73tqDT6!#V$cT5;@Vf?bq?MD;dhvHDaA9tOQ<@U-FGyiTLy~zcGijjC2CG}#&`{l^ zYg^sy!Z|ZOE9iJ1l4g#}NHdpYC5KjsM+U3Ha68x}M{)(I=5ri>S87|iR9s@~ls%W& z4#~5Z*g~@L65IXqK#whCIYA9-dyHY*D%AC|#S7SDO4@2l%Oe7Rok5D(rL>}+-yG(5 z4%YD7uqL1ZjYPm&+#q(f*p?~|af@`+jj{vvW<%h{kXo>e>8KC`O=u>O$ocS6_0)n^ zO?Mvaa3hlyFK^=uSTA&Hit}G76%83~?-Oa}s2~98d`~D}t$2J;~mrlRb>fnewO`16y#nc4Ock+)H69eyb#E>^@2l zN416A_waK+C)l9+!xX=c)>BP~k5 z@YAKkR^^UY>F#Gh~BVeRqh+;{2m$X$ayNN z=TumavZ)SKefMIx@21Hky4xJl&a!wnvf$R(Ei9x;NtQT1ly))AF~f7iIcHLIH}%;` zcm!Lu^w-AJQhIUTC@tMeR%ujRson~F`N=$N!?!7J5o_?BXtGqZTVvmij&1Krn`&gM zE*@JD8r3Oxw8=Dk;SQOc<3qNF`3}O-gh356oP=qFxf=T82ong$ zXy|km4t;_liv4Bv%|ErHGAQIJ?&gPRzk;95be!kjCbRgP@55!Cn9wGL!B8iz@L{q> zV>wjl!<8Cj5KQx7x&{LXuJ)l=15dXc6KSWZ55A9~o7I~x)F$M6h3(LhOQG)s5l9Z87q&K1Lqu2_oU5oPgnKd00RY2^TZ;=KbIDLjSM%=Ea74 zjRqclJVf~3q^oH5P7*|_AukX_je5y`Sz9xbw zUo}A_Tv^<$Uh5Hgz4lrR-Pj1-615G(d#&$qd3R{vhxuhbEZl=d&ye08bI%I&+_M}t zcv5Y7{lXb9KJ&_gfLUG;7dj_%c^2`SYn?^BWa+a=2Ji?eweJ_tUg>ubrE_vAYH_YQ zIjx&qygr+cN`f5cZQ!|>lX8H5DRQC=z%Kld$6^9s(jC|>HhSU>Kh~4Jb$udN%SdxQ zU6o(tN?n%vbR}+d@&#huMu$^``tu|Q_M)r<6BQ_bx*J2qTN)J^T~@rW`K?{0IN#b!yN@89XE) delta 3336 zcma)833OD|8UDY_Oy11ok!?aaU_t^R3zNj80izUxMbw(Ggd&Ktg+QiBAOy(*Z3PC4 zwt`E6Ypbbcv5TNhZ7?)o5R?`bXlub%yUDhRNX{|m%$xVV z|K9uk-@m+f?^A2{w)jt9?ms3XRruEd|Ah1;KeCW*VYH1L)sa3mJjTXY^X|Bfh4$)VEXCO_1^G z!iZe8B;V)NUT?N>i^|N%b5}zqa3o*t$xTl5Vumwul-iy#0<~&?Mj<|<&Sd0e&&KVx zc%;BWwIB1O+m8jf)52Xg7GjY~9X>a4iH)VGQ%%FO-SvZi#DFc)2TQPS*0%IXR5s;l2vVPn!1=%nU<1>ANg?t zFI#xUmV5B3dM@uo#wkbwz_A}|(x2G)DPB|C#_dWw`|($qXkkW*N7|VC>ekj!o5_5r-NFazazW7jA!M{d;b>J; zm>ymxJ{hI6#U@OUVkr?|IiC}GUtw%@vg|5bpB%c%c3ggVl`Slrud?lsoddS8;{*)| z4HzS~&!X`owqXJmUQ27Dv@#;_?g>)N0j(9y{O2(Li?Ndbwle{%&_V>PMyt5=;)HB* zi&vy~VUY`HR!hgP59ybUO1fRzj$kHuya1Wm>Y#?JMS2`Xxa?F{(NjwPGaB!r!FH4IhmL+dm za`S&>nI%5{(o?XR!@dNpWf<+ebd8c3(Y?IBxJa-9TPSX&tYQ|L)b8LE26Zl&bB7LU zBIW3eI9bq7TC9_$`zS2IjNXqgF%+MS#h3AbxQ)YMF07}s`*_{YNsyy*OeavyDb8*f zNcI+!>|8Eq%42S9Y{fR?#>Rtqh{AOIPD%9G!<4?67Dly|+z;}4gfIbL;lOsuFp-=s zewdNel}x@(N8-5H5Oc8sJ4__mQiq+oCeH{B#Cam#_O?mJkDDezHU5r zmwrHxyRL0CJvNsDn`E8oF^_DKMUg()F0-9JA?Nv+p7Rks+NL{D_ubh@-%XR;PUvpZvZV`EPaq)j)nQ}S;zTO%<_yQI1sIaxPlQt+S@ z7C(;>3DTp^mwVz`?nBdZnxE5RdXk*Zj+RCDt`0A*EZ<7?gPgNT{P+Qr`vQ*QCFn!jNu1`o7w|Lu9B<&w2>K;{9f!{0cLq5t m&vT`-=573m_Q}yje>O0R-n#<*ErKpmgr9Ezj(^~vGV=fJK%b-l diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.class b/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.class index 7c48afafe8e04502d24c6d0fb8f59bb491c1adfa..e742b58bf2c9f6aa3e9b43457506bf898086b5d7 100644 GIT binary patch delta 8210 zcmb_h31Ade(ypqWnVw8KlRGd8A%O(Q1wjze1XRF-Kwwcs4ljlfU?dkgRPY!NL~(Z! zY%c^4)LkwW6$~V(;I;0mc)Po<=&Fd%ZUUzO- z%(vdhNzWZ@f1HSh8-q*|ra?9b((J@E^P%F|fz%v+&f@3U$0W0>P2RyY)NV-ZX;)KQhJ8tc?5r8@opN&GM^A0Ac~0rb(Cm|{t3tKGnsSr>#gy7<_lU;E z;C#ojPswfa7fi{WtD}QWbH)W5eEd1Nz5F$wO|H6NZMB!bMF>7zdEX~17RB=_{j*uDH|@jgkDbW<`c z>1MA840lVwl1%&UK=%~eOHX*|STAo-Jb9Mn+nm|W#JH~|{p_5~-u@jvo=0oca&(W3)q2rh=1hk^Ip=K1oH+ktLwRWr}$Emf9 z$lAE-<~j8&zBHZ#DOC~791|Wb( zPPb&DoMBJzF(6qg>7Y+0%M??lS~5+}vhVD1y5}5A&Xx1*?|Wpsf==0I+7pVpZyb~T zmYZ+b*f(#oE3>2CbgikPbLw?af!(W5miONbi=| zvaN^+$~bbmsMOGn!K`X&Y(%m%4e2b=QcMhU$QnDpBqQrLGtNg&lpkNtG(3h)0kY@y z%CJw!$;elWr~YIyB1HEZCDYC>$xiGLrk)6O7}~R|p+|bOZ*7767!CZ zzz^2VF0HJun_V9144rKci8imLx^e#G#$ZE3btBU#)gniv15|>p-io?e^-eSz^h(rH zkHX!kwdDRMEzxKAczZ#=D+l+i3D(Y-8O)D%*)CDfAHsC}VI9y8=JvHG3nxS(p<&@}5uLmtCfOV99Rz(0;Uddz=hP zbkqJM`P6=|q(}+0HY`+HT5-B-w+BKKA7Px$!?JSwC8` zSN7Rg_TQC!&__NwWXUVCBgtTu*vs+*_P`z)9(400!?4#6$n0mh!DVHT^{~0y>5jwZcGBBFo#XR(dgolwQNoY$y4f2JyWm1feLG>bl=+4L37 zq3X2b z_`(QwHL8_5We-O6(tyY$^DLPsjbuT#(ktH@rA>yopGtK?E>{1ZJ3-_Eq(9Xw1sEfX?{ z76Iuslt|Z7I$Y8dt}LS8Y8#G*cB0}aa*QqmUKN#M2?K}d(dBXlkjGJPxl*nomnbL+ zhFk_1VO*DkWZFd^sDNR7Hkb|gpRA%ybZv1-8@*@HgIF+PqJFd1p9@Ctb?CQbpm#w+P#T1%hCvZUHy6t)8B&8HE#!Q;8@}`Cf%wYnr`9>TFsRR)i~P5<7o%z*LY$KfY&4Y${ydpmG+mp@Oz-lt(LZO&eYA^tDVuW z0f{OR8GgjF;);D@~Qgmylt0BV_-fy}McFf~14vv8}xxah)<=?Sq4 z{EmXvQ90gr!8X~+9+l&bl@nGve@yotQBd5%1mEgIX9nWcX5!MD$qkf`=uII%Ps1ze z*_4TGx`d}Aoq{x$XV7@ilen7Z@GNTJIdmC^=qhaEOSz7gb3Lu)26~Vi0jrswzK}lSi)aroVCIYQLVgJ+^QCA9m+>$Tb0uHSQ~64s%UAJ2{C^`~&3Eu3yuUBt z)x20|>aFjp}}R5W8rCO0{VAViehJF><?!#0J1O>xSThO-ps( zbi;t$^pq-3+;5NANum>^!=q2mABk+&VhC}54 zxn(rvERz~E?<#cb{dBI$tM<~Vd&w;eaB5Je9Mwrw$8x$=8!EhAwmK!t8_*7YU|Sz4 zS*K#lTqsM_tXtg!S%o>v=DB2i3&a>r^t()tpRn zQ3l{iq&sg*LkxK+BZ zhn6+C%xC5vM0ch0o|}09MNcCR4oF;z`wI74S;@Fx>iKHdCfp|sdOGnb-0Nn&f6W89 z|E@SyvRY~Na++=oUrlF$?4iliTp%k!W@~b!8)SGm=$@J$>;XLhWR52DO^`Vtb2XXn z1(^i0mnJ<)p5Z;#K^xv`rs$h`y&)<=!3SXRw>bU2i>6hIm!P@0+F@XC?_D$o_dcmo z&?<$?X?Rg8a!;IE5D7kDQ`f*V$6s{f+A7;*9>kVnmx zM?ge#S=NAXtPK|~mldF*=E@QfFjwP~EA){s0#BN-)FE#chz@yEK}5YLl^{CAod65!uuFYQF-(hh-ftEMG#SO^aO}#G-wluXf$ZG z+S|jw(%deMxx;(Cf3f$%M{4w4i3G`tSh;N|k5vHOzT~bCdK=K9QjSA7~>A=HWb`jy1W(&>&;m zWW2p?M0)r%^%XJ3c^g(2?v!wl5n>u^Nf z#E3mWdWjiP4#+U(c1!v>2VRC`B8DPt?CmgI3Vv=M;oHtm@E! zui_C6gJA>UQCyL03r}ek?W@?AdD3E?uWE4`Ex`%+`2K%}s6Qq-S$F8?F#ZH0dF54{ z9-R-uy)-~TOz~}zGdIfFDx4gf%Xk@Z4*##o>sS+cLw{O;PWdZQSpJ5|Zzx0lj)`~V zU9f*7AIm55FZnD&ecpxoLcWSw`$qn)sqc|P`{e*q3?oAfmtm@r>^Hp5oP1=&8&+h_ z-@zA72BTJLq(uY+h>;PQ>uzLqDl@WUDfuP>%TbeK^j0Pq`TDo7{w;_|DT+0~D0V0V RCra}9@JN57)HsR){{st`7}Wp( delta 4403 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 VPed8p7N)invX@djbf@k@#zFrcl%} zw%BS_Y~vGc#3IpFtQ(ZjNhXv6*0r@%ts8Z#ign-Xf-R-zy_SZ{Gi$d(Qvd zdzL$QR_(3{KK$IogFu@K3!rPzd zRakC-;$;7(gv#4n5te>g{%QWnmp& zOA^?qX=0u|q?W4%_7kevTBPtbEoxd2q>g6It65^t_FU#`RW#en)W!BORc7z^_=+1X zJTy91OEs@$nSI2wds>I0pg*=Xma3T7)a0yPp}CUR*{i+teVvLXVW^qBYA`Y|5FHG0 z6=8!Fnyb0Se#CoQ=nbqi=+a!r8}0SJYeTD8ZP2Y*!<+31Uvp?J>kQUw-pX|QS>LQ+ zh_!6cY^2Ao^H-=%cG%w(*sO`M#lG2pxf!Kjvz54g%3oJBz}pN4HA$pBF11g2r`rep zlkG);w$L`V8{DAT!Q1T{1J_M?r=(yYl1v8KN!s9Dnwz-Uek!oI>)pBJ%nK6bR?T~; zk^?8BsjgHcb$!z0HidtK(?}-o&za6Q*{ulV`po3^oOYpe_#w@QuP|eSy29Uy@`pCV8J}R~3|)d&p`g#CK`Ij9CvzcUITDXePo5 znFy0jCUJuk_Kt$N>Pz-W!R#r=`Lf1ORgkapHG{8fzQMzzrwc|@wa-bW;-ddIM+&t; zo;9O4gzi>Fe+=>`{%r6U&0qPOU0Zn4Iy}kGKb&&?Q!_%mn19>n3a`23*&ye5&R|Tl zj|ap?g6Ph8JUZweDw;Hu#~vuE@hG3Yw`f7y$8=}xWMDkb7@>GXa6sPQbam2lFVlt4 z|E2SZ?03B$bw*DlC2xQ280tQk=9K(8q{y}KnEW~fVnF&^d|QCxJF@ISyw&4^4AGsb zJ%V8sW|`z6T$g2vqOE+G#||^Ynw6!}gBZ*`N?rFD4HH}n*1`;nPf+JSMoYJMg4aM- zw{L8p^qKPk`8ll-DMC|fH(Ba&0~eL^Q2P^)V*d^qfBtIIHK2TEpF z+~uaZ(%W``@;d7iu2kw1si@A{FOuC^GAI2>b3doW^B(In8SWkCQNf?JzTo1Mg1>0p z=i>VXk6IZQ-z#|BI_l!P+-5l>8i(n2dFPpS3C=TZ7o2D66Fh1ibG_H6MHsgpbcHUr z-||#f$V#4SiQqg{gWx<>rQkf(6v26>0=KCT9p!kpb#jb{j!Oc)ZmZ-bY1#{ul$N~l zE1nVqrzBG`&rvVASA~Va@@MFj?^!zKKlayBc{ktESKl~HCmji_G2?&CM2 w^YGgju7ux-{Cj!oWgmZ#&iOk}^DoZQ@Sh9fGp?AI&gUdIP!J^_ukus&Jb0`!6#xJL delta 2000 zcmZ8hd0bRg6#njP@A2je_ylwi1yW=gkr5YEG&j^x+yYZv8)XD^7?2r7GaF4Um&oO2 zNx7jZx#aH1;#%5bnPscCr_gTtr}eWs@4@J=<~Q%X?>qOL@7{aPxwkTTa~I{(-}Pqz z48WZY%A72tyR?u&%_%J}O!K;D_necKQ(9Q)D$kS=%FwRy)Y8V9wN1O0Hf`VCc(A%@ zNA1mqhQ>A3R)nLq1eK!=BE&w{+Y-gm7VSh6%VyESVHh55gHfePyTU~=?w^iW_!0!J4lG8kO*82X4iQXAngCYq8N zGQ+W!9V?8)}qL&zHN;hRNI4HWK zh%>9XNz43aRIV+e+d@#b;XVHhr91ji%D76;53)=|ij zFq&fw#)@0!*l-48Qu1UgMqxb1qsSF)Epes^49WV&##(Hai5!!VFFckkIS*4fred0? zx7e*S@VEpg$4tx;w=Ev43$rB@aulIh6ia!Lb15NZPOn!%2|N-?Im$3kT$Os~dYe<# zT|pk^b1XmvZQJYe<@%hy`Cb_dDcv*OrL*SBc%s=gUB;6PmU7pEVpoNXr<$!jwJa>< zSca#?6ge?;1y)LUhT~aGV~7{dR`KG995uZJt2x$SErVR-^cD>*EpX|(&X!S0GZngg z*#$DD%2-d!AlAGL1uL+TLuhfWHJ_3)_h^@|sI*{Iv8O;A-Nf-C= z%N(zeaY9IfwWG$jacn2PG9-mni^C!I4lA&W<5g{iyhU}Wo3bQh&x52}Y%`r+SK1JI z4fFVleTy`u*Esf)l3i&ZyPsM?er8<(|Zc3g&YGa?k6KtRGtj#|_myQoyNW9#`&7W##PYq&1qOOCJbwHOq7KB_v*jPJD4e9y5S z)539sVPKM2YKatAc$63t7MJ*=f}ilSggqRU*iZhGX*by(kE>kYi_0tFcZLM9I5fd1 z;SX^ntcTx-XjsANNEx(~Mkk=2u!X)2x{Z45e>M|wv+i+&cOkehF+hn>CwbX6Szq@P3YI`lCh$6kwvtFX{shb)HCUF$HCp$bI=#xa^b zZw#qFmKwn1I!rg9pULJo)uNyZS?U~{DFAmBc&b_+fUgE!)QShDMY^ety12y@lBzBt zleX5>I`|EkSo6;&ZTBWM_z0xZcsDwaX{dnT0YB2Q z8a=R)u9Pk4h27+S2pQBFeQC*=c#|$wEsZH8UV&`9K^7B*Pr?OUgpo!rw4`?qPz2VG>5&Lw^Z*Qh$`FVRxbYNfprwZzb3F=y~6Z%i#(> zz!f4|skYHfG#8tc62S5t!vK2#tHffpQ`~cU+Tx7^Y7k>rpVwI%6R}Xcx{jDQqbB$_ z$UAV2B41En3Se^;E)%X&x9Iph;jQWp9iJe)Q{AoOy@YGjeLCK%*ZnYAG^KU)4Js`s z98{_x98{W1c&BOh1Bq-IFa8N3ha8N3qa8N3ea8M~k@6W>x zIGC>|MVdB6Mpls9~ckH|r*LloqD=qjOvwQ8lr{Do}#-=)gRxO)Fw z#Z%t3Ux+H#;`lL0T6eMksGIOH*$nvP{*nEZ_-FKVrFDEx2kRSriyv?ksf{hPNvA>f SOAGszLKG7J4YzO`wtoSqU)=xz diff --git a/service/service-cdc/pom.xml b/service/service-cdc/pom.xml new file mode 100644 index 0000000..ec0d2ef --- /dev/null +++ b/service/service-cdc/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.atguigu.tingshu + service + 1.0 + + + service-cdc + + + 17 + 17 + UTF-8 + + + + io.github.xizixuejie + canal-spring-boot-starter + 0.0.17 + + + + \ No newline at end of file diff --git a/service/service-cdc/src/main/java/com/atguigu/tingshu/CDCApplicaiton.java b/service/service-cdc/src/main/java/com/atguigu/tingshu/CDCApplicaiton.java new file mode 100644 index 0000000..02219c4 --- /dev/null +++ b/service/service-cdc/src/main/java/com/atguigu/tingshu/CDCApplicaiton.java @@ -0,0 +1,19 @@ +package com.atguigu.tingshu; + +import io.xzxj.canal.spring.annotation.EnableCanalListener; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * @author: atguigu + * @create: 2025-03-21 15:46 + */ +@EnableCanalListener +@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) +public class CDCApplicaiton { + + public static void main(String[] args) { + SpringApplication.run(CDCApplicaiton.class, args); + } +} diff --git a/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/AlbumListener.java b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/AlbumListener.java new file mode 100644 index 0000000..884ddab --- /dev/null +++ b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/AlbumListener.java @@ -0,0 +1,36 @@ +package com.atguigu.tingshu.listener; + +import com.atguigu.tingshu.model.album.AlbumInfo; +import io.xzxj.canal.core.annotation.CanalListener; +import io.xzxj.canal.core.listener.EntryListener; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Set; + +/** + * @author: atguigu + * @create: 2025-03-22 08:46 + */ +@Slf4j +@CanalListener(destination = "tingshuTopic", schemaName = "tingshu_album", tableName = "album_info") +public class AlbumListener implements EntryListener { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 监听到转机信息表更新 + * @param before 变更前数据 + * @param after 变更后的数据 + * @param fields 变更字段 + */ + @Override + public void update(AlbumInfo before, AlbumInfo after, Set fields) { + log.info("[cdc]监听到变更数据,变更前:{},变更后:{}", before, after); + Long id = after.getId(); + String redisKey = "album:info:"+id; + redisTemplate.delete(redisKey); + } +} diff --git a/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/UserListener.java b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/UserListener.java new file mode 100644 index 0000000..9ebd855 --- /dev/null +++ b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/UserListener.java @@ -0,0 +1,35 @@ +package com.atguigu.tingshu.listener; + +import com.atguigu.tingshu.model.user.UserInfo; +import io.xzxj.canal.core.annotation.CanalListener; +import io.xzxj.canal.core.listener.EntryListener; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Set; + +/** + * @author: atguigu + * @create: 2025-03-21 15:47 + */ +@Slf4j +@CanalListener(destination = "tingshuTopic", schemaName = "tingshu_user", tableName = "user_info") +public class UserListener implements EntryListener { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 监听用户表更新回调方法 + * @param before + * @param after + * @param fields + */ + @Override + public void update(UserInfo before, UserInfo after, Set fields) { + log.info("[cdc]监听到变更数据:{}", after); + String redisKey = "user:info:"+after.getId(); + redisTemplate.delete(redisKey); + } +} diff --git a/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/VipServiceConfigListener.java b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/VipServiceConfigListener.java new file mode 100644 index 0000000..b8f20d1 --- /dev/null +++ b/service/service-cdc/src/main/java/com/atguigu/tingshu/listener/VipServiceConfigListener.java @@ -0,0 +1,36 @@ +package com.atguigu.tingshu.listener; + +import com.atguigu.tingshu.model.user.VipServiceConfig; +import io.xzxj.canal.core.annotation.CanalListener; +import io.xzxj.canal.core.listener.EntryListener; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Set; + +/** + * @author: atguigu + * @create: 2025-03-22 08:46 + */ +@Slf4j +@CanalListener(destination = "tingshuTopic", schemaName = "tingshu_user", tableName = "vip_service_config") +public class VipServiceConfigListener implements EntryListener { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 监听到VIP套餐信息表更新 + * @param before 变更前数据 + * @param after 变更后的数据 + * @param fields 变更字段 + */ + @Override + public void update(VipServiceConfig before, VipServiceConfig after, Set fields) { + log.info("[cdc]监听到变更数据,变更前:{},变更后:{}", before, after); + Long id = after.getId(); + String redisKey = "user:vipconfig:"+id; + redisTemplate.delete(redisKey); + } +} diff --git a/service/service-cdc/src/main/resources/bootstrap.properties b/service/service-cdc/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..88efb3c --- /dev/null +++ b/service/service-cdc/src/main/resources/bootstrap.properties @@ -0,0 +1,8 @@ +spring.application.name=service-canal +spring.profiles.active=dev +spring.main.allow-bean-definiton-overriding=true +spring.cloud.nacos.discovery.server-addr=192.168.200.6:8848 +spring.cloud.nacos.config.server-addr=192.168.200.6:8848 +spring.cloud.nacos.config.prefix=${spring.application.name} +spring.cloud.nacos.config.file-extension=yaml +spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml diff --git a/service/service-order/src/main/java/com/atguigu/tingshu/order/api/OrderInfoApiController.java b/service/service-order/src/main/java/com/atguigu/tingshu/order/api/OrderInfoApiController.java index 3354847..2b58f73 100644 --- a/service/service-order/src/main/java/com/atguigu/tingshu/order/api/OrderInfoApiController.java +++ b/service/service-order/src/main/java/com/atguigu/tingshu/order/api/OrderInfoApiController.java @@ -1,8 +1,15 @@ package com.atguigu.tingshu.order.api; +import com.atguigu.tingshu.common.login.GuiGuLogin; +import com.atguigu.tingshu.common.result.Result; import com.atguigu.tingshu.order.service.OrderInfoService; +import com.atguigu.tingshu.vo.order.OrderInfoVo; +import com.atguigu.tingshu.vo.order.TradeVo; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -16,5 +23,14 @@ public class OrderInfoApiController { private OrderInfoService orderInfoService; + @GuiGuLogin + @Operation(summary = "三种商品(VIP会员、专辑、声音)订单结算") + @PostMapping("/orderInfo/trade") + public Result trade(@RequestBody TradeVo tradeVo) { + OrderInfoVo orderInfoVo =orderInfoService.trade(tradeVo); + return Result.ok(orderInfoVo); + } + + } diff --git a/service/service-order/src/main/java/com/atguigu/tingshu/order/service/OrderInfoService.java b/service/service-order/src/main/java/com/atguigu/tingshu/order/service/OrderInfoService.java index 322c51b..a9dbb8b 100644 --- a/service/service-order/src/main/java/com/atguigu/tingshu/order/service/OrderInfoService.java +++ b/service/service-order/src/main/java/com/atguigu/tingshu/order/service/OrderInfoService.java @@ -1,9 +1,12 @@ package com.atguigu.tingshu.order.service; import com.atguigu.tingshu.model.order.OrderInfo; +import com.atguigu.tingshu.vo.order.OrderInfoVo; +import com.atguigu.tingshu.vo.order.TradeVo; import com.baomidou.mybatisplus.extension.service.IService; public interface OrderInfoService extends IService { + OrderInfoVo trade(TradeVo tradeVo); } diff --git a/service/service-order/src/main/java/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.java b/service/service-order/src/main/java/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.java index c3db232..5bb11d1 100644 --- a/service/service-order/src/main/java/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.java +++ b/service/service-order/src/main/java/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.java @@ -1,13 +1,36 @@ package com.atguigu.tingshu.order.service.impl; +import cn.hutool.core.lang.Assert; +import com.atguigu.tingshu.album.AlbumFeignClient; +import com.atguigu.tingshu.common.constant.SystemConstant; +import com.atguigu.tingshu.common.result.Result; +import com.atguigu.tingshu.common.util.AuthContextHolder; +import com.atguigu.tingshu.model.album.AlbumInfo; +import com.atguigu.tingshu.model.album.TrackInfo; import com.atguigu.tingshu.model.order.OrderInfo; +import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.order.mapper.OrderInfoMapper; import com.atguigu.tingshu.order.service.OrderInfoService; +import com.atguigu.tingshu.user.client.UserFeignClient; +import com.atguigu.tingshu.vo.order.OrderDerateVo; +import com.atguigu.tingshu.vo.order.OrderDetailVo; +import com.atguigu.tingshu.vo.order.OrderInfoVo; +import com.atguigu.tingshu.vo.order.TradeVo; +import com.atguigu.tingshu.vo.user.UserInfoVo; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; +import org.aspectj.weaver.ast.Or; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + @Slf4j @Service @SuppressWarnings({"all"}) @@ -15,6 +38,165 @@ public class OrderInfoServiceImpl extends ServiceImpl orderDetailVoList = new ArrayList<>(); + //1.5.初始化订单减免明细列表 + List orderDerateVoList = new ArrayList<>(); + //todo 流水号 签名之类的还未添加 + //1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员 + String itemType = tradeVo.getItemType(); + //2. 处理购买项目类型:VIP套餐 + if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) { + //2.1 远程调用用户服务得到VIP套餐信息 + VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData(); + Assert.notNull(vipServiceConfig, "VIP套餐{}不存在", tradeVo.getItemId()); + //2.2 计算原金额、订单金额、减免金额 + originalAmount = vipServiceConfig.getPrice(); + orderAmount = vipServiceConfig.getDiscountPrice(); + //2.3 封装商品信息列表 + OrderDetailVo orderDetailVo = new OrderDetailVo(); + //套餐的ID + orderDetailVo.setItemId(tradeVo.getItemId()); + //套餐的名字 + orderDetailVo.setItemName("VIP套餐:" + vipServiceConfig.getName()); + //套餐图片的url + orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl()); + //套餐的折后价 + orderDetailVo.setItemPrice(orderAmount); + orderDetailVoList.add(orderDetailVo); + //2.4 如果存在优惠,封装优惠列表 + //bigdecimal是不能用equals判断的 因为1.10和1.1不相等 + //compareTo(orderAmount)==0是相等 + //compareTo(orderAmount)==-1小于 + //compareTo(orderAmount)==1是大于 如果大于的话说明有了折扣 + if (originalAmount.compareTo(orderAmount) == 1) { + derateAmount = originalAmount.subtract(orderAmount); + //存在优惠 我们要封装优惠列表 + OrderDerateVo orderDerateVo = new OrderDerateVo(); + orderDerateVo.setDerateAmount(derateAmount); + orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT); + orderDerateVo.setRemarks("VIP套餐限时优惠:" + derateAmount); + orderDerateVoList.add(orderDerateVo); + } + + } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) { + //3. 处理购买项目类型:专辑 + //3.1 远程调用用户服务,判断是否重复购买专辑,如果已购过,则业务终止 + Long albumId = tradeVo.getItemId(); + Boolean data = userFeignClient.isPaidAlbum(albumId).getData(); + //如果data为true的话就走断言 + Assert.isFalse(data, "用户已购买专辑{}", albumId); + //3.2 远程调用专辑服务,获取专辑价格以及折扣(普通用户折扣,VIP会员折扣) + AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData(); + BigDecimal price = albumInfo.getPrice(); + BigDecimal discount = albumInfo.getDiscount(); + BigDecimal vipDiscount = albumInfo.getVipDiscount(); + //判断用户是否为vip 如果是vip就走vip折扣 如果不是就走普通用户的折扣 + Boolean isVIP = false; + UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData(); + Assert.notNull(userInfoVo, "用户{}不存在", userId); + //判断一下用户是否为vip以及vip的过期时间是否超过了当时的时间 + if (userInfoVo.getIsVip().intValue() == 1 + && userInfoVo.getVipExpireTime().after(new Date())) { + isVIP = true; + } + //3.4 计算相关价格 + //3.4.1 暂时将订单价=原价 + originalAmount = price; + orderAmount = originalAmount; + //当你是普通客户 但是专辑是有普通用户折扣的时候 计算一下专辑价格 + //discount.doubleValue()!=-1 + //BigDecimal.doubleValue() 是将 BigDecimal 转换为 double 类型的操作 + + //如果是普通用户且有折扣,则订单价=原价 100 *折扣 6 + //..divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP)是保留两位小数的标准写法 + if (!isVIP && discount.doubleValue() != -1) { + orderAmount =originalAmount.multiply(discount) + .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP); + } + //如果是会员,则按照会员折扣走 + if (isVIP && vipDiscount.doubleValue() != -1) { + orderAmount = originalAmount.multiply(vipDiscount) + .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP); + } + //3.5 封装商品信息列表 + OrderDetailVo orderDetailVo = new OrderDetailVo(); + orderDetailVo.setItemId(albumId); + orderDetailVo.setItemName("专辑:"+albumInfo.getAlbumTitle()); + orderDetailVo.setItemUrl(albumInfo.getCoverUrl()); + orderDetailVo.setItemPrice(originalAmount); + orderDetailVoList.add(orderDetailVo); + + //2.4 如果存在优惠,封装优惠列表 + if(originalAmount.compareTo(orderAmount)==1){ + OrderDerateVo orderDerateVo = new OrderDerateVo(); + orderDerateVo.setRemarks(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT); + orderDerateVo.setDerateType("VIP套餐限时优惠:" + derateAmount); + orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount)); + orderDerateVoList.add(orderDerateVo); + } + + } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) { + //4.处理购买项目类型:声音 + //4.1 远程调用专辑服务,获取未购买声音列表 + Long trackId = tradeVo.getItemId(); + List trackInfoList = + albumFeignClient.findWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData(); + Assert.notNull(trackInfoList, "不存在待结算声音",trackId); + //4.2 远程调用专辑服务获取,专辑价格(声音单价)声音不支持折扣 + AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData(); + Assert.notNull(albumInfo, "专辑{}不存在", albumInfo.getId()); + BigDecimal price = albumInfo.getPrice(); + //4.3 计算相关价格 + originalAmount = price.multiply(new BigDecimal(trackInfoList.size())); + orderAmount = originalAmount; + //4.4 封装订单明细列表 + orderDetailVoList = trackInfoList.stream().map( + trackInfo -> { + OrderDetailVo orderDetailVo = new OrderDetailVo(); + orderDetailVo.setItemId(trackInfo.getId()); + orderDetailVo.setItemName("声音:" + trackInfo.getTrackTitle()); + orderDetailVo.setItemUrl(trackInfo.getCoverUrl()); + orderDetailVo.setItemPrice(price); + return orderDetailVo; + } + ).collect(Collectors.toList()); + + } + //5.封装订单VO对象 + //5.1 封装相关价格信息 + orderInfoVo.setOriginalAmount(originalAmount); + orderInfoVo.setOrderAmount(orderAmount); + orderInfoVo.setDerateAmount(derateAmount); + //5.2 封装商品相关集合 + orderInfoVo.setOrderDetailVoList(orderDetailVoList); + orderInfoVo.setOrderDerateVoList(orderDerateVoList); + //5.3 TODO 封装其他信息:流水号、时间戳及签名、项目类型等 + + //6.响应订单VO对象 + return orderInfoVo; + + + } } diff --git a/service/service-order/target/classes/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.class b/service/service-order/target/classes/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.class index 57eeac05bfacaf0f87fe163e9e8988aabdc506db..b3a1e5e86d787079b8542ebe13acf299a2ee985e 100644 GIT binary patch literal 9069 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 delta 277 zcmX|*y-vbV6vuzJq`jBY(ozr}H zm7qWQK)n}sSE>s`olB|FDL~GZFhj2#9uy8#Gdspring-boot-starter-data-elasticsearch - - org.apache.kafka - kafka-clients - - - org.apache.kafka - kafka-streams - io.github.biezhi TinyPinyin diff --git a/service/service-search/src/main/java/com/atguigu/tingshu/search/receiver/SearchReciever.java b/service/service-search/src/main/java/com/atguigu/tingshu/search/receiver/SearchReciever.java index 2928f2d..63f966f 100644 --- a/service/service-search/src/main/java/com/atguigu/tingshu/search/receiver/SearchReciever.java +++ b/service/service-search/src/main/java/com/atguigu/tingshu/search/receiver/SearchReciever.java @@ -71,37 +71,45 @@ public class SearchReciever { channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } -// /** -// * 监听更新声音/搜索统计信息 -// * -// * @param mqVo -// * @param message -// * @param channel -// */ -// -// @RabbitListener(bindings = @QueueBinding( -// exchange = @Exchange(value = MqConst.EXCHANGE_TRACK, durable = "true"), -// value = @Queue(value = MqConst.QUEUE_ALBUM_ES_STAT_UPDATE, durable = "true"), -// key = MqConst.ROUTING_TRACK_STAT_UPDATE) -// ) -// @SneakyThrows -// public void updateStat(TrackStatMqVo mqVo, Message message, Channel channel){ -// if (mqVo != null)log.info("更新声音/专辑统计信息:{}", mqVo); -// //1.幂等性处理 -// //1.1 从MQ消息对象中获取消息唯一标识 -// String key = RedisConstant.BUSINESS_PREFIX + "db:" + mqVo.getBusinessNo(); -// //1.2 尝试采用set nx写入Redis -// Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 5, TimeUnit.MINUTES); -// -// //1.3 写入成功消息才进行业务处理,写入失败(消息重复被MQ服务器投递)忽略消息 -// if (flag) { -// //2.业务处理 -// searchService.upperAlbum(mqVo.getAlbumId()); -// -// } -// channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); -// } + /** + * 监听更新声音/搜索统计信息 + * + * @param mqVo + * @param message + * @param channel + */ + @RabbitListener(bindings = @QueueBinding( + exchange = @Exchange(value = MqConst.EXCHANGE_TRACK, durable = "true"), + value = @Queue(value = MqConst.QUEUE_ALBUM_ES_STAT_UPDATE, durable = "true"), + key = MqConst.ROUTING_TRACK_STAT_UPDATE) + ) + @SneakyThrows + public void updateStat(TrackStatMqVo mqVo, Message message, Channel channel) { + if (mqVo != null) { + log.info("更新声音/专辑统计信息:{}", mqVo); + //1.幂等性处理 + //1.1 从MQ消息对象中获取消息唯一标识 + String key = RedisConstant.USER_TRACK_REPEAT_STAT_PREFIX + "es:" + mqVo.getBusinessNo(); + //1.2 尝试采用set nx写入Redis + Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.MINUTES); + //1.3 写入成功消息才进行业务处理,写入失败(消息重复被MQ服务器投递)忽略消息 + if (flag) { + try { + //2.业务处理 + searchService.updateAlbumStat(mqVo); + } catch (Exception e) { + redisTemplate.delete(key); + //如果异常将无法处理消息发送到死信(异常)交换机->死信队列->消费者处理进入存入消费者异常消息表->人工处理 + channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); + } + } + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + } } +} + + + diff --git a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/SearchService.java b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/SearchService.java index bc9e9fa..89f5e31 100644 --- a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/SearchService.java +++ b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/SearchService.java @@ -4,6 +4,7 @@ import co.elastic.clients.elasticsearch.core.SearchResponse; import com.atguigu.tingshu.model.search.AlbumInfoIndex; import com.atguigu.tingshu.model.search.SuggestIndex; import com.atguigu.tingshu.query.search.AlbumIndexQuery; +import com.atguigu.tingshu.vo.album.TrackStatMqVo; import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo; import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo; @@ -37,4 +38,6 @@ public interface SearchService { void updateLatelyAlbumRanking(Integer top); List getRankingList(Long category1Id, String dimension); + + void updateAlbumStat(TrackStatMqVo mqVo); } diff --git a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.java b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.java index 6a1e5ec..6c97428 100644 --- a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.java +++ b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.java @@ -9,6 +9,7 @@ import com.atguigu.tingshu.user.client.UserFeignClient; import com.atguigu.tingshu.vo.album.AlbumStatVo; import com.atguigu.tingshu.vo.user.UserInfoVo; import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -28,6 +29,8 @@ public class ItemServiceImpl implements ItemService { private AlbumFeignClient albumFeignClient; @Autowired private Executor threadPoolTaskExecutor; + @Autowired + private RedissonClient redissonClient; @Override diff --git a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.java b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.java index 622a6d1..dc14418 100644 --- a/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.java +++ b/service/service-search/src/main/java/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.java @@ -17,10 +17,12 @@ import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption; import co.elastic.clients.elasticsearch.core.search.Hit; import co.elastic.clients.elasticsearch.core.search.Suggestion; +import co.elastic.clients.json.JsonData; import com.alibaba.fastjson.JSON; import com.atguigu.tingshu.album.AlbumFeignClient; import com.atguigu.tingshu.common.constant.RedisConstant; +import com.atguigu.tingshu.common.constant.SystemConstant; import com.atguigu.tingshu.model.album.*; import com.atguigu.tingshu.model.search.AlbumInfoIndex; import com.atguigu.tingshu.model.search.AttributeValueIndex; @@ -30,12 +32,15 @@ import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository; import com.atguigu.tingshu.search.repository.SuggestIndexRepository; import com.atguigu.tingshu.search.service.SearchService; import com.atguigu.tingshu.user.client.UserFeignClient; +import com.atguigu.tingshu.vo.album.TrackStatMqVo; import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo; import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo; import com.atguigu.tingshu.vo.user.UserInfoVo; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.elasticsearch.core.suggest.Completion; @@ -68,17 +73,21 @@ public class SearchServiceImpl implements SearchService { private UserFeignClient userFeignClient; @Autowired private ElasticsearchClient elasticsearchClient; - private static final String SUGGEST_INDEX = "suggestinfo"; @Autowired private SuggestIndexRepository suggestIndexRepository; @Autowired private RedisTemplate redisTemplate; + @Autowired + private RedissonClient redissonClient; + + private static final String SUGGEST_INDEX = "suggestinfo"; @Override public void upperAlbum(Long albumId) { //1.创建专辑索引对象 AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex(); + //2.远程调用专辑服务查询专辑信息,封装专辑以及专辑标签列表 CompletableFuture albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> { AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData(); @@ -137,14 +146,18 @@ public class SearchServiceImpl implements SearchService { BaseCategoryViewCompletableFuture, userInfoCompletableFuture, statCompletableFuture - ).orTimeout(5, TimeUnit.SECONDS) + ).orTimeout(1, TimeUnit.SECONDS) .join(); //7.保存专辑到索引库 albumInfoIndexRepository.save(albumInfoIndex); + //8.将专辑标题作为提示词文档存入提示词索引库 this.saveSuggestIndex(albumInfoIndex); + //9.将专辑ID存入布隆过滤器,用于解决缓存穿透问题 + RBloomFilter bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER); + bloomFilter.add(albumId); } @@ -483,16 +496,18 @@ public class SearchServiceImpl implements SearchService { @Override public void updateLatelyAlbumRanking(Integer top) { try { - //1.从Redis检索获取分类下五种排序维度专辑TOPN(n=top)记录 + //1.从Redis检索获取分类下五种排序维度专辑TOPN记录 //1.1 远程调用专辑服务获取所有1级分类列表 List baseCategory1List = albumFeignClient.findAllCategory1().getData(); Assert.notNull(baseCategory1List, "1级分类列表为空"); + + //1.2 遍历1级分类 for (BaseCategory1 baseCategory1 : baseCategory1List) { - //从内容中找出id 设定一个key Long category1Id = baseCategory1.getId(); + //基于基于1级分类ID构建Redis中hash结构的Key String key = RedisConstant.RANKING_KEY_PREFIX + category1Id; - //绑定大key + //基于Key创建绑定hash操作对象 BoundHashOperations> hashOps = redisTemplate.boundHashOps(key); //1.3 遍历5种排序维度 String[] rankingDimensionArray @@ -525,31 +540,67 @@ public class SearchServiceImpl implements SearchService { } } } catch (IOException e) { - log.error("更新Redis小时排行数据异常", e); + log.error("更新Redis小时榜数据异常", e); throw new RuntimeException(e); } } @Override public List getRankingList(Long category1Id, String dimension) { - //基于1级分类ID构建Redis中hash结构的Key - String key =RedisConstant.RANKING_KEY_PREFIX+category1Id; - BoundHashOperations> hashOps = redisTemplate.boundHashOps(key); + //基于基于1级分类ID构建Redis中hash结构的Key + String key = RedisConstant.RANKING_KEY_PREFIX + category1Id; + BoundHashOperations> hashOps = redisTemplate.boundHashOps(key); //构建Hash排行榜中field 当前排序方式 String field = dimension; Boolean flag = hashOps.hasKey(field); - if(flag){ + if (flag) { //List list = (List) redisTemplate.opsForHash().get(key, field); List list = hashOps.get(field); if (CollUtil.isNotEmpty(list)) { List albumInfoIndexVoList = list.stream() - .map(albumInfoIndex -> BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class)) + .map(albumInfoIndex -> BeanUtil.copyProperties(albumInfoIndex,AlbumInfoIndexVo.class)) .collect(Collectors.toList()); return albumInfoIndexVoList; } } return null; + } + + /** + * #增量更新 + * POST /albuminfo/_update/1 + * { + * "script": { + * "source": "ctx._source.playStatNum += params.increment", + * "lang": "painless", + * "params": { + * "increment": 1 + * } + * } + * } + */ + + @Override + public void updateAlbumStat(TrackStatMqVo mqVo) { + try { + Long albumId = mqVo.getAlbumId(); + String incrementField = ""; + if (SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())) { + incrementField = "playStatNum"; + + } else if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())) { + incrementField = "commentStatNum"; + } + String finalIncrementField = incrementField; + elasticsearchClient.update( + u -> u.index(INDEX_NAME).id(albumId.toString()) + .script(s -> s.inline(i -> i.source("ctx._source." + finalIncrementField + " += params.increment").lang("painless").params(Map.of("increment", JsonData.of(mqVo.getCount()))))) + , AlbumInfoIndex.class + ); + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/service/service-search/src/test/java/com/atguigu/tingshu/search/test01.java b/service/service-search/src/test/java/com/atguigu/tingshu/search/test01.java index 46c18db..31f7548 100644 --- a/service/service-search/src/test/java/com/atguigu/tingshu/search/test01.java +++ b/service/service-search/src/test/java/com/atguigu/tingshu/search/test01.java @@ -1,7 +1,10 @@ package com.atguigu.tingshu.search; +import com.atguigu.tingshu.common.constant.RedisConstant; import com.atguigu.tingshu.search.service.SearchService; import org.junit.jupiter.api.Test; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -10,6 +13,8 @@ import org.springframework.boot.test.context.SpringBootTest; public class test01 { @Autowired private SearchService searchService; + @Autowired + private RedissonClient redissonClient; @Test public void test01() { @@ -19,8 +24,17 @@ public class test01 { } catch (Exception e) { e.printStackTrace(); } - - + } + } + @Test + public void test02() { + RBloomFilter bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER); + for (long i = 1; i <= 1629; i++) { + try { + bloomFilter.add(i); + } catch (Exception e) { + e.printStackTrace(); + } } } } diff --git a/service/service-search/target/classes/com/atguigu/tingshu/search/receiver/SearchReciever.class b/service/service-search/target/classes/com/atguigu/tingshu/search/receiver/SearchReciever.class index 25754f4e6226b12c3a4cdd4de758085cc30f625a..e73db86c2127deff468e7e421748712545f363a1 100644 GIT binary patch literal 4770 zcmcgv>wgs08GcT($t;_RV8CiX6k0SFLMB|iC1?o&Bs2j`LMTvcXE!ICVP|KSnb{=N zTD4cIy-2lIpp{bE+KW|gjeu6a^+W#-K>Mkm`=#x3W_FWF*aZ0X_bdFeyXVY%-gDmP zea`c~^Y?#W{TRTL_-hJPsMe6sQG;56C5O#%)38k^YwSrMmYG1HcC+PJ!BYa&%`L-= zki;SlDIImt1vX^dykQ2}qLnQgf#qcVv7+Hi)60w*p3F#VTzbY}I3AE0OL9m*wwo1b z=yScS;oGB|4jX-LHY>fZB<>Pe_35eO*I#@0`muA@F28c)?Wt>*U;pgm<2R;`-FW%^ ze|~iG`4{T21b1s_(6JQDc;dNk1QM1r>IyV9_sv8z7{}p0Ke8$XSX$FnhvitI;eH(}@qj?bojMRm6$=IFb=&D;USMfFiawVy zv<#=P3ad4IUdI=(M&O|kf`STg)HCyP!u4{7nLko6GOj0$e(C#WmccB`%AhpZ>$%(_ zu%s`rvAHG6#jQNdEb2<(VKixI*3p7i0pqq#Ftc9ZE}mtZv=u>)4w>Ww*|n<xTZT1kAY3yk9~$%A z2{UcW6gJ@z4Ug)0439I%wwq78IkHZD3Do(HG;>EobA?8XBhxOrT4J*UMJKTGa{FXP z76Qw4NZiiZ#C^3gahGGW&PzvDq-=Mhl;R}5D6ry-z@_uOV3KUuqE!;Gl*>I=+Hu1y+Wz76Z#>6i%k- zd5X3}B+NeQsDReryL;czc82g^l!5t7sU*+gYZ^=)X=Di6?L<81u!`L>=pF4&`#gof zfq3pp3K-)-X=c|g4i?Y$R@b$q=`cMKqZ+b0#$XBDJwvXNg@B%pY)km_#vfE-nbTo| zO1HRJAoMcSJw;r~N^?c|t;})iq6RWMrU277RB`8s4iBVn?TECcBH^t1P_70sFv^iB zRVwNjSCUj0iq3#kr7?v`9M$l39na$%yf9&UV)bl?nF(C)sK6rLy{N=dtK$jiu}$Bn zw9H-ECD2$BnPJPfC}G`>;}R0ZKY_<9-kvnG;2WdSrUpf_w7}>t2JVE#dqua29(RPH zFYcod>@Mch(i>6=8J6RWX%Cy8rN*VnL@;J?zfBdhuyC2@Ui;&rhLtPyGmoHmRSfE; zD#k3cR;-be0{6snH6zr#V4(o^j<7d~S%e4R!%Vq;tX%Lkw9J?5X47pRjXFo(W^(;z zp#mAS`};mLEum4U#H)mNTOIEP*HHKh@2So+-+r z+#0P_ZserGK{W-B+=&}rsBjrw)Qm?eT3>4W+C$U{+eneyD@r8-Ps!|tJJ}#SC%rG7 z<+VL*h?w`Bl+*IVfEr>{EY(aR zG-|8XXKCzNS#q z_>>=q=ChwAH?t9oUI60KojvIWM+?&Eaq|V&VQnHt zOBj6MbI(F_ZT!WxIG4hCT+nb)wI!F>D9zLr%Ncib(uiV=I+bb3eJid#CBw++PSY8& zrN5@nb#ui+{Dq*DmRK@oEga%;E0`aO*;gncB^N!WZ4X+3Wb-hkDji=PIzwHHc#6nJ1^QhX3npKzYZ~RrIUV9+$0)N-? z`Dt<_71xzQO{e*w79fH_`55^O-!07tl!02J1+X%*Xg7Gw=*i0~X{y)Ju6$j%~ zWiWn$UlLI=L+oP)H3BqAW;lu&Peov?ox+x@*ug|3NRnMs*xi_D7+Qwm3;6OCe6<#x zHH|e>7&(E~5{V91kv1zkYn6FpVhV-*>)NI{6(kTF&0<&;bG?mFIXs2ScCpN~Ce-|g z)3q9s8oD*C($J>iw;Fq1p}}eV7wY+9RwCk~sORzn#Qq?$f0o32j#Nz3wWRaI^fQWH zWHAT}CR<+@`!T^S)wLNQse5@N-^OWzu^jtw62GFS4dl!j{F)RF2v(WGlhYKQ#990% zr0`@&Ty@JQLo(l*!tczb@bBYU`$IWvXX#SWSJ60$KZdtm-RITew!or>R rgjX^WghrnK1J0#5dl^^p5!v`Bj;kmIW!?ER$M5kkNt^fi_m}?y35;9) delta 594 zcmchUJ5K^p5QV>e;I6npVqy@DF%&k&L`wotuH{uHe)A!y}O7tP#A_LO~|64W=xnnQ`*Q zt#huYns3I7>Wo{3I&n-?;cN8JfhCbjlx`GK61T}Y>tS=u|H5?110EwSjA6+XIRk@t zC=IV9BfBdAy)4LX`2-eOl1!nGtS)`i&P6yA*%Zh`i{%VPbTZC!xWDcejY^LpVJ)+Q zE_vd!+>LUqdXfq9ta-0Uu+E0CUGHOZw=JtsLa`|dTC&*@TDit9do}hc+Z^~n#Rm?B zTcg63)Q6&)bU?HbRo8Hmtf-`&tH@u!uaSTCTmA00>if;aD)0lh)u*;R8f)M(6+l diff --git a/service/service-search/target/classes/com/atguigu/tingshu/search/service/SearchService.class b/service/service-search/target/classes/com/atguigu/tingshu/search/service/SearchService.class index 1285a22e9a4855ffb27911df5bfe6c5a52bd1cff..db39428eb931c1a5a6b4be8db8ce99a80b7e4cd1 100644 GIT binary patch delta 124 zcmaFKeVj+&)W2Q(7#J9A8MN3Lm>3xhCT3xhCTQT82dIMydKxpU9G_nhDT&bep2rZ*ywO%II#XvWt; z`MX@_%qF%ZbGgn;G@VRjx+Tnub!I!%Kf7wYE2(by$EEN?p0+w~%eaHPr5IIk5BCc{ zT@K0lNQMiK6BC=P@8bY0YF3*=RbR%O!H9_^3^Ig4WLx zsoY!jt;W_BB7yCF% z*pCA!L7z74-QJ`7&c_V?{cs?NnW#emOZY9r8mvN+|1@Ig!dk&v&D(&i!P2dRI7Frc zDICTTSV-82qv+=i0kq;6Eh}k@ah#ToG(S$zvg0H<4Q<@|9eT0XXkms?EWiqpK~U`1ni%)K z^ISS{t#5PtXOM3 zZ*HL0h19yJi26UR+i+D-*`$IxTGm{oHAJmpYOSHxND(z`Y7LuO!=~0*q8255oEt`G ztuL>P|Lv@DR&hww{-)WB!(89u&?`K`M|^;Xcp|KEJi`lNP2nY`jJ-G9dryCvsz2Zp HzM$e4Y4^KL delta 938 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 PKT-FenO~S+QOW-UiR>Sh literal 48945 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 RoHIfz^!dtAO{kWN{vU)W8`A&) diff --git a/service/service-search/target/test-classes/com/atguigu/tingshu/search/test01.class b/service/service-search/target/test-classes/com/atguigu/tingshu/search/test01.class index 703bf48c366f0f6b94b09f7435342e88d2e43483..f680baede3d5577b1d425a0370fe0d6ce6e51343 100644 GIT binary patch literal 1763 zcmb7ETT|Oc6#iBgh{y^gBqrgK*jxe_nk?WpO);eynoAisnTaWx=}aHiVlhi>$s?^u z@<000hd%S1nNHjEq0gQEiT;mHdO4CWF_a-wYrN8)J(ur0=j{3Y?_Yie@F})!7)V(# z9rU4};nJZv61*kO&+{B9pv(XaoT3$T&4;5fL5L54}!Z*ZY& zk*Y;pE3XzFL_Cxta1Xeap%8iB%kavT$$g_mrouS(-^@u3)dZtVvJ$w|AuF4!nt}3kMGN=&P9R(v=W^1)6sCz%Aoo-l`rcMu zNsrP$S@we(??}05D*p6@O3}hChUK%!kgVZ*-1WUs3s1KUx7xb_%rOj$n9FLl?)#0^ zeO1>oNF$F03yTguz-@-?d%_dsn$(-Ig9nK{L!mb@6-{Jo#rzpGdW@(x97`6K9ju_h zaO1yGd$g$+#oiu6rZ-TlUyJ##8AhK*o>mRHt3p++%aZ5$TEtZd8SW)O!ng#r{XjJ2 z3qN?yt5SF&-xsd-gI8R14R|Ti{tHFvFEW^0{vP%1P+58Manz{FU`LR9%3|4fMSWKU zD&Dt+ran*lnx`hru;B! zDk@^qCp&cUy1rK!(n*;+Rm#4nA`BxZX}xN8s1?pPUF*T$uF5*$uq%EPxRT-%$8?ME zo%pIS+&#M}zOR$%5qGebFEy_uW!ofq!VQyja}1hC20b)~!0=%_o#_2hBBd*JN|vHM zO*&fdk_M+Yqj%rJPhhVTlaFbQk3kOa(dx7m@CjCF$55nGBQgFCCOyEMy^*~N>)Y>< zUHAbPvOiJzU z!7L_{@Hx^Qr!Z?YtWy+t2J84Nfi%~Kv_U=wVZMc7KC>`x{z(8kSt!vN!)6!qZ{#T+ z;ROPk8=ifGc>`s;lxxFZ%q7_G7 a{)Isc-293d_Mc{G;2|j)c+}(j!!O6XPXZm~ac%Y`8%a?$NLyVc`L|gDY=A z2;p@wY~Gvqk@ug!c;1fIyVVcy#%mqT%;MLkruueY6N4ot!H_9F5%pnwzZlf!iyyhoQV#~9~@zH&OJtk8X%>V!Z diff --git a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserInfoApiController.java b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserInfoApiController.java index 55f18b4..2c285ab 100644 --- a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserInfoApiController.java +++ b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserInfoApiController.java @@ -19,38 +19,72 @@ import java.util.Map; @SuppressWarnings({"all"}) public class UserInfoApiController { - @Autowired - private UserInfoService userInfoService; - /** - * 根据用户ID查询用户信息 - * @param userId - * @return - */ - @Operation(summary = "根据用户ID查询用户信息") - @GetMapping("/userInfo/getUserInfoVo/{userId}") - public Result getUserInfoVo(@PathVariable Long userId){ - UserInfoVo userInfoVo = userInfoService.getUserInfo(userId); - return Result.ok(userInfoVo); -} - - /** - * 获取每一页声音列表(提交声音ID集合)对应购买状态 - * - * @param userId 用户ID - * @param albumId 专辑ID - * @param needCheckPayStateTrackIdList 需要检查购买状态的声音ID集合 - * @return {声音ID:1(已购),声音ID:0(未购)} - */ - @GuiGuLogin //对内微服务访问接口,上游系统调用是否只要携带token 下游系统就可以获取用户ID - @Operation(summary = "获取每一页声音列表(提交声音ID集合)对应购买状态") - @PostMapping("/userInfo/userIsPaidTrack/{albumId}") - public Result> userIsPaidTrack( - @PathVariable("albumId") Long albumId, - @RequestBody List needCheckPayStateTrackIdList - ) { - Long userId = AuthContextHolder.getUserId(); - Map map = userInfoService.userIsPaidTrack(userId, albumId, needCheckPayStateTrackIdList); - return Result.ok(map); - } + @Autowired + private UserInfoService userInfoService; + + /** + * 根据用户ID查询用户信息 + * + * @param userId + * @return + */ + @Operation(summary = "根据用户ID查询用户信息") + @GetMapping("/userInfo/getUserInfoVo/{userId}") + public Result getUserInfoVo(@PathVariable Long userId) { + UserInfoVo userInfoVo = userInfoService.getUserInfo(userId); + return Result.ok(userInfoVo); + } + + /** + * 获取每一页声音列表(提交声音ID集合)对应购买状态 + * + * @param userId 用户ID + * @param albumId 专辑ID + * @param needCheckPayStateTrackIdList 需要检查购买状态的声音ID集合 + * @return {声音ID:1(已购),声音ID:0(未购)} + */ + @GuiGuLogin //对内微服务访问接口,上游系统调用是否只要携带token 下游系统就可以获取用户ID + @Operation(summary = "获取每一页声音列表(提交声音ID集合)对应购买状态") + @PostMapping("/userInfo/userIsPaidTrack/{albumId}") + public Result> userIsPaidTrack( + @PathVariable("albumId") Long albumId, + @RequestBody List needCheckPayStateTrackIdList + ) { + Long userId = AuthContextHolder.getUserId(); + Map map = userInfoService.userIsPaidTrack(userId, albumId, needCheckPayStateTrackIdList); + return Result.ok(map); + } + + /** + * 判断当前用户是否已购买指定专辑 + * + * @param albumId + * @return + */ + @GuiGuLogin + @Operation(summary = "判断当前用户是否已购买指定专辑") + @GetMapping("/userInfo/isPaidAlbum/{albumId}") + public Result isPaidAlbum(@PathVariable Long albumId) { + Long userId = AuthContextHolder.getUserId(); + Boolean flag = userInfoService.isPaidAlbum(userId, albumId); + return Result.ok(flag); + } + + /** + * 查询当前用户指定专辑下已购声音ID列表 + * + * @param albumId + * @return + */ + @GuiGuLogin + @Operation(summary = "查询当前用户指定专辑下已购声音ID列表") + @GetMapping("/userInfo/findUserPaidTrackList/{albumId}") + public Result> findUserPaidTrackList(@PathVariable Long albumId) { + Long userId = AuthContextHolder.getUserId(); + List list = userInfoService.findUserPaidTrackList(userId, albumId); + return Result.ok(list); + + + } } diff --git a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserListenProcessApiController.java b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserListenProcessApiController.java index 2be5141..1f1b192 100644 --- a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserListenProcessApiController.java +++ b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/UserListenProcessApiController.java @@ -26,9 +26,16 @@ public class UserListenProcessApiController { @Operation(summary = "获取声音播放进度") @GetMapping("/userListenProcess/getTrackBreakSecond/{trackId}") public Result getTrackBreakSecond (Long trackId){ + //1.尝试获取用户ID Long userId = AuthContextHolder.getUserId(); - BigDecimal second =userListenProcessService.getTrackBreakSecond(userId, trackId); - return Result.ok(second); + //2.查询用户对于指定声音播放进度 + if (userId != null) { + //3.返回 + BigDecimal breakSecond = userListenProcessService.getTrackBreakSecond(userId, trackId); + return Result.ok(breakSecond); + } + //3.返回 + return Result.ok(BigDecimal.valueOf(0)); } /** diff --git a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/VipServiceConfigApiController.java b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/VipServiceConfigApiController.java index 8b0cdbf..086d99c 100644 --- a/service/service-user/src/main/java/com/atguigu/tingshu/user/api/VipServiceConfigApiController.java +++ b/service/service-user/src/main/java/com/atguigu/tingshu/user/api/VipServiceConfigApiController.java @@ -1,11 +1,19 @@ package com.atguigu.tingshu.user.api; +import com.atguigu.tingshu.common.cache.GuiGuCache; +import com.atguigu.tingshu.common.result.Result; +import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.user.service.VipServiceConfigService; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @Tag(name = "VIP服务配置管理接口") @RestController @RequestMapping("api/user") @@ -15,5 +23,23 @@ public class VipServiceConfigApiController { @Autowired private VipServiceConfigService vipServiceConfigService; + @Operation(summary = "获取平台套餐列表") + @GetMapping("/vipServiceConfig/findAll") + @GuiGuCache(prefix = "user:vipconfig:list") + public Result> findAll(){ + List list = vipServiceConfigService.list(); + return Result.ok(list); + } + + @Operation(summary = "根据id获取套餐内容") + @GuiGuCache(prefix = "user:vipconfig:") + @GetMapping("/vipServiceConfig/getVipServiceConfig/{id}") + + public Result getVipServiceConfig(@PathVariable Long id){ + VipServiceConfig content = vipServiceConfigService.getById(id); + return Result.ok(content); + + } + } diff --git a/service/service-user/src/main/java/com/atguigu/tingshu/user/service/UserInfoService.java b/service/service-user/src/main/java/com/atguigu/tingshu/user/service/UserInfoService.java index e8881e8..de32c4c 100644 --- a/service/service-user/src/main/java/com/atguigu/tingshu/user/service/UserInfoService.java +++ b/service/service-user/src/main/java/com/atguigu/tingshu/user/service/UserInfoService.java @@ -17,4 +17,7 @@ public interface UserInfoService extends IService { Map userIsPaidTrack(Long userId, Long albumId, List needCheckPayStateTrackIdList); + Boolean isPaidAlbum(Long userId, Long albumId); + + List findUserPaidTrackList(Long userId, Long albumId); } diff --git a/service/service-user/src/main/java/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.java b/service/service-user/src/main/java/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.java index 1584a1c..472650c 100644 --- a/service/service-user/src/main/java/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.java +++ b/service/service-user/src/main/java/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.java @@ -46,124 +46,161 @@ public class UserInfoServiceImpl extends ServiceImpl i private RedisTemplate redisTemplate; @Autowired private RabbitService rabbitService; - @Autowired - private UserPaidAlbumMapper userPaidAlbumMapper; - @Autowired - private UserPaidTrackMapper userPaidTrackMapper; - - @Override - public Map wxLogin(String code) { - try { - //1.根据小程序提交的临时票据code调用微信接口服务,获取当前微信用户唯一标识openid - WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code); - String wxOpenid = sessionInfo.getOpenid(); - //2.根据openid查询数据库用户表,判断微信账户是否关联用户 - UserInfo userInfo = userInfoMapper.selectOne( - new LambdaQueryWrapper() - .eq(UserInfo::getWxOpenId, wxOpenid) - );//3.如果不存在用户记录,说明该微信是首次登录 - if (userInfo == null) { - //3.1 新增用户记录 - userInfo = new UserInfo(); - userInfo.setWxOpenId(wxOpenid); - userInfo.setNickname("听友" + IdUtil.nanoId()); - userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-07-21/1ce194e3-4212-4ff8-948b-babd9a7b8a02.jpg"); - userInfoMapper.insert(userInfo); - //3.2 TODO 隐式初始化账户记录,用于后续订单消费,采用MQ - //3.2.1 创建初始化账户MQ消息对象 注意:如果使用的是VO对象,该对象必须得实现序列化接口 Serializable - Map mqData = new HashMap<>(); - mqData.put("userId", userInfo.getId()); - mqData.put("amount", BigDecimal.valueOf(100)); - mqData.put("orderNo", "ZS"+ DateUtil.today().replaceAll("-", "")+IdUtil.getSnowflakeNextId()); - mqData.put("title", "首次注册,赠送体验金活动"); - - //3.2.2 发送消息到RabbitMQ交换机 todo 使用RabbitService - rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, mqData); - - } - //4.无论是否首次登录,都需要生成令牌,返回给小程序 - //4.1 生成随机令牌 - String token = IdUtil.randomUUID(); - //4.2 将用户令牌作为Key,用户基本信息UserInfoVo作为Value,保存到Redis中 - String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token; - UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class); - redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS); - //4.3 返回用户令牌 - return Map.of("token", token); - } catch (WxErrorException e) { - log.error("微信登录失败:{}", e.getMessage()); - throw new RuntimeException(e); - } - - } + @Autowired + private UserPaidAlbumMapper userPaidAlbumMapper; + @Autowired + private UserPaidTrackMapper userPaidTrackMapper; @Override - @GuiGuCache(prefix = "user:info:") - public UserInfoVo getUserInfo(Long userId) { - UserInfo userInfo = userInfoMapper.selectById(userId); - return BeanUtil.copyProperties(userInfo, UserInfoVo.class); + public Map wxLogin(String code) { + try { + //1.根据小程序提交的临时票据code调用微信接口服务,获取当前微信用户唯一标识openid + WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code); + String wxOpenid = sessionInfo.getOpenid(); + //2.根据openid查询数据库用户表,判断微信账户是否关联用户 + UserInfo userInfo = userInfoMapper.selectOne( + new LambdaQueryWrapper() + .eq(UserInfo::getWxOpenId, wxOpenid) + );//3.如果不存在用户记录,说明该微信是首次登录 + if (userInfo == null) { + //3.1 新增用户记录 + userInfo = new UserInfo(); + userInfo.setWxOpenId(wxOpenid); + userInfo.setNickname("听友" + IdUtil.nanoId()); + userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-07-21/1ce194e3-4212-4ff8-948b-babd9a7b8a02.jpg"); + userInfoMapper.insert(userInfo); + //3.2 TODO 隐式初始化账户记录,用于后续订单消费,采用MQ + //3.2.1 创建初始化账户MQ消息对象 注意:如果使用的是VO对象,该对象必须得实现序列化接口 Serializable + Map mqData = new HashMap<>(); + mqData.put("userId", userInfo.getId()); + mqData.put("amount", BigDecimal.valueOf(100)); + mqData.put("orderNo", "ZS" + DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId()); + mqData.put("title", "首次注册,赠送体验金活动"); + + //3.2.2 发送消息到RabbitMQ交换机 todo 使用RabbitService + rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, mqData); + + } + //4.无论是否首次登录,都需要生成令牌,返回给小程序 + //4.1 生成随机令牌 + String token = IdUtil.randomUUID(); + //4.2 将用户令牌作为Key,用户基本信息UserInfoVo作为Value,保存到Redis中 + String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token; + UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class); + redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS); + //4.3 返回用户令牌 + return Map.of("token", token); + } catch (WxErrorException e) { + log.error("微信登录失败:{}", e.getMessage()); + throw new RuntimeException(e); + } + + } + + @Override + @GuiGuCache(prefix = "user:info:") + public UserInfoVo getUserInfo(Long userId) { + UserInfo userInfo = userInfoMapper.selectById(userId); + return BeanUtil.copyProperties(userInfo, UserInfoVo.class); } @Override public void updateUser(UserInfoVo userInfoVo, Long userId) { - UserInfo userInfo = new UserInfo(); - userInfo.setId(userId); - userInfo.setAvatarUrl(userInfoVo.getAvatarUrl()); - userInfo.setNickname(userInfoVo.getNickname()); - userInfoMapper.updateById(userInfo); + UserInfo userInfo = new UserInfo(); + userInfo.setId(userId); + //更换头像 + userInfo.setAvatarUrl(userInfoVo.getAvatarUrl()); + //更换昵称 + userInfo.setNickname(userInfoVo.getNickname()); + userInfoMapper.updateById(userInfo); } - @Override - public Map userIsPaidTrack(Long userId, Long albumId, List needCheckPayStateTrackIdList) { - Map payStatusMap = new HashMap<>(); - //1.根据用户ID+专辑ID查询已购专辑记录,如果存在专辑购买记录,将提交声音ID购买状态全部设置为1,返回 - Long count = userPaidAlbumMapper.selectCount( - new LambdaQueryWrapper() - .eq(UserPaidAlbum::getUserId, userId) - .eq(UserPaidAlbum::getAlbumId, albumId) - ); - if (count > 0) { - for (Long trackId : needCheckPayStateTrackIdList) { - payStatusMap.put(trackId, 1); - } - return payStatusMap; - } + @Override + public Map userIsPaidTrack(Long userId, Long albumId, List needCheckPayStateTrackIdList) { + Map payStatusMap = new HashMap<>(); + //1.根据用户ID+专辑ID查询已购专辑记录,如果存在专辑购买记录,将提交声音ID购买状态全部设置为1,返回 + Long count = userPaidAlbumMapper.selectCount( + new LambdaQueryWrapper() + .eq(UserPaidAlbum::getUserId, userId) + .eq(UserPaidAlbum::getAlbumId, albumId) + ); + if (count > 0) { + for (Long trackId : needCheckPayStateTrackIdList) { + payStatusMap.put(trackId, 1); + } + return payStatusMap; + } - //2.如果未购买专辑,根据用户ID+专辑ID查询已购声音记录 - List userPaidTrackList = userPaidTrackMapper.selectList( - new LambdaQueryWrapper() - .eq(UserPaidTrack::getUserId, userId) - .eq(UserPaidTrack::getAlbumId, albumId) - .select(UserPaidTrack::getTrackId) - ); + //2.如果未购买专辑,根据用户ID+专辑ID查询已购声音记录 + //查询出来的是购买的声音的id + List userPaidTrackList = userPaidTrackMapper.selectList( + new LambdaQueryWrapper() + .eq(UserPaidTrack::getUserId, userId) + .eq(UserPaidTrack::getAlbumId, albumId) + .select(UserPaidTrack::getTrackId) + ); - //2.1 如果不存在购买声音记录,将提交声音ID购买状态全部设置为0,返回 - if (CollUtil.isEmpty(userPaidTrackList)) { - for (Long trackID : needCheckPayStateTrackIdList) { - payStatusMap.put(trackID, 0); - } - return payStatusMap; - } + //2.1 如果不存在购买声音记录,将提交声音ID购买状态全部设置为0,返回 + if (CollUtil.isEmpty(userPaidTrackList)) { + for (Long trackID : needCheckPayStateTrackIdList) { + payStatusMap.put(trackID, 0); + } + return payStatusMap; + } - //2.2 如果存在购买声音记录,将提交声音ID已购设置1,未购买设置为0返回 - //2.2.1 获取已购声音ID集合 - List userPaidTrackIdList = - userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList()); - //2.2.2 判断哪些是已购设置为1,哪些是未购买设置为0 - for (Long trackId : needCheckPayStateTrackIdList) { - if(userPaidTrackIdList.contains(trackId)){ - //已购买 - payStatusMap.put(trackId, 1); - }else{ - //未购买 - payStatusMap.put(trackId, 0); - } - } - return payStatusMap; - } + //2.2 如果存在购买声音记录,将提交声音ID已购设置1,未购买设置为0返回 + //2.2.1 获取已购声音ID集合 + //这个stream流就是将userPaidTrackList这个中的id单独取出来然后做成一个List + List userPaidTrackIdList = + userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList()); + //2.2.2 判断哪些是已购设置为1,哪些是未购买设置为0 + for (Long trackId : needCheckPayStateTrackIdList) { + if (userPaidTrackIdList.contains(trackId)) { + //已购买 + payStatusMap.put(trackId, 1); + } else { + //未购买 + payStatusMap.put(trackId, 0); + } + } + return payStatusMap; + } + @Override + public Boolean isPaidAlbum(Long userId, Long albumId) { + //根据用户的id去查询购买的专辑 返回一个数值,如果有的话说明买过了 + Long count = userPaidAlbumMapper.selectCount( + new LambdaQueryWrapper() + .eq(UserPaidAlbum::getUserId, userId) + .eq(UserPaidAlbum::getAlbumId, albumId) + ); + return count > 0; + } - } + /** + * 查询指定用户指定专辑下已购声音ID列表 + * + * @param userId + * @param albumId + * @return + */ + @Override + public List findUserPaidTrackList(Long userId, Long albumId) { + List userPaidTracks = userPaidTrackMapper.selectList( + new LambdaQueryWrapper() + .eq(UserPaidTrack::getUserId, userId) + .eq(UserPaidTrack::getAlbumId, albumId) + .select(UserPaidTrack::getTrackId) + ); + if (CollUtil.isNotEmpty(userPaidTracks)) { + List userPaidTrackIdList = userPaidTracks.stream() + .map(u -> u.getTrackId()) + .collect(Collectors.toList()); + return userPaidTrackIdList; + } + return null; + } +} diff --git a/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserInfoApiController.class b/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserInfoApiController.class index 88dd5390aec9abc7d96603bc96af7d6572a6a44a..d1ef641f254705215ce9df4fea0d479bb5cf4f3c 100644 GIT binary patch delta 1277 zcma)5OHUI~6#njXTBIdJ!BT_-MGz=Z#s~FPL3t`z5CufV2LlvR9ovLbT^Q4a3v0wg zGe(UY;xjBnP)H~yvRC*10;bl)-(Wmpzt_kw7N_3Q6XD*zfW z^1*sXD7GON}$)Fb;xo0Q@3bq2ROXK}PBa$sEY1_R?Br}wISLzZ+$b1eG~G?HLpK3$*`B#DcVpcJn2^?j#!uyzL-nIF-0C{ z$j{J(;k0TO8zLLqH+mRcTJ&}N$*lHS)$Tp`@pvX4U5Y=O(;mLnmOdsHXIGc!<5PFF z_s>^jYGUOP!*(f596JJILCGLjx+Qbf9iz7yL`CO4RAt~o6Hv>=IyE=Ts(!OLBD}LZ zKK&*!_i|llL#x%;{UinL)%?%tg^pG&`ZV!khQaxlLB^38xmbIH;fWDnyI=7!6!GbD z=1r=y>6fF1)@T7kEuCgM&HBxy2D1w)>rydbD7t3ii>>rM}B9jc}|L z0{!oV4P`oh2TXL|oWgUf9&XCBQPzGOAoM|sn;E%*L-Z4H7@UxUEDr&Xgy@Mil5unp zEQDidqF@#yM>81=rD)*<$X3193Y|l)-qQ_{I^C)Qcb->P2ML)-0pUgl<<1`STPIz^9#5+Dgzinru6y z2sx{hnQ5u^lvEL0bb$gv%{YTz>X8~g7m*x(Unc(jh6SAccLB~c>6mqWOM MAqvQJNj|{gKf&$z6#xJL delta 480 zcmX|-IZgvn5Jcaf@fa~VcURBL@OL5n`hEWZ8r-(RC$Am*pF#UI^v&Ifs#y%9dVRft%PPud zl^MN3?G4is@Te+UF$^%KnCl1_XF{2w#-s*Rnm(+p9^Ep>_9{SwQ zIvHZ}>bAaPiJ+MJD6-*v*jj^4Ez9Ua>&;hBmMykb*-=kHTt~Gl)ZzBTS>L^$#@K)S z@BLxlnIF=W0~Nam9CDN%aO@7qd+q>Tm1u(DL}HIq7thiW=VC4rF1ZqMosPKq0}Bi& AF8}}l diff --git a/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserListenProcessApiController.class b/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserListenProcessApiController.class index 1d292a0c64bf24109d2415f27f4e49d0020db1d9..f715cdf55686d03d54bbc84faabc767375578e51 100644 GIT binary patch delta 823 zcmZ8fT~ks~6kX@u3zrbJi5qdJ4-rcdvD=54eH&>Jfm&FZeI5zPL~O!*<+16R|Iz5s zOw>b(U6+-Cqo!Q zs`*1pi|&qvw2+$F9jkB-Ut|pcxPcv7MJt#&8%FgluTR}=yLfgvRZa>tJQLI z#yF@oKltbL`Y=WMA#jqX!PDnv4l{?D;OR?#hS+4F;S|3L42n9BF!QPJu#%t9nHu_nu#TzW7VM3EE%dZdw>OB=-N_M>1IO4GLjZAd^ub2I z%ar7;#ripn63w|goGLnxF>)I?iVKW1Tx2}}@f)^@OMC&YAP54k@~7b%)Xs-FW*q3j zb@rxfxPb`4xXEY$w>Z*iYz2?hPv{UzGTS_*2Uvo;>nNGV5GIfEBj#$0?rDo6 zgWDXYwEw3q+@>wVR;5X`j}xAq$DLhWIppiS@`O!cnlMwn%M1g-|H57ND8nnr~VP%t)eN)@d8a1RA&i~Md{HW6K0Y~15Yu=-e>GK1OdRf$2tMEQ5T;k8K1Lls)`qQ$z-|+QKhAVKE5WK(&=tA z$7|ghL_?P5?bz-qNb}4f$e9-BsC^Tj+SskK7-ld_KAnE}%I1g)LR`TzF@K|OhL=i} zYM&&0p@26x(%xdR#w%aj5|#-wmG3J|1iyEcJxXYWYb&dwG&H9SrJsEgcIrRj5f3v* zL0Pn5;g0BNL$prwlvKfY13B1`AFxJZ^_TwDI!F0tZro?)<{dM)2+gxuV*3`lL!#X( O-r+qT_E`A475)Ki&Q6~I diff --git a/service/service-user/target/classes/com/atguigu/tingshu/user/api/VipServiceConfigApiController.class b/service/service-user/target/classes/com/atguigu/tingshu/user/api/VipServiceConfigApiController.class index 8a8748f973298b2a5d18e4d756380c86b1fd5caa..d35fffc2160b41fb530b606a7fa625f86da927d9 100644 GIT binary patch literal 2518 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