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 73c2c21..de2c655 100644 Binary files a/common/service-util/target/classes/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.class and b/common/service-util/target/classes/com/atguigu/tingshu/common/cache/GuiGuCacheAspect.class differ 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 6f36dc4..223435c 100644 Binary files a/common/service-util/target/classes/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.class and b/common/service-util/target/classes/com/atguigu/tingshu/common/config/pool/ThreadPoolConfig.class differ 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 fb595b3..6a985bc 100644 Binary files a/service-client/service-album-client/target/classes/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.class and b/service-client/service-album-client/target/classes/com/atguigu/tingshu/album/impl/AlbumDegradeFeignClient.class differ 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 37d703f..31de6ac 100644 Binary files a/service-client/service-user-client/target/classes/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.class and b/service-client/service-user-client/target/classes/com/atguigu/tingshu/user/client/impl/UserDegradeFeignClient.class differ 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 1f84c33..ce62a14 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/ServiceAlbumApplication.class and b/service/service-album/target/classes/com/atguigu/tingshu/ServiceAlbumApplication.class differ diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/api/AlbumInfoApiController.class b/service/service-album/target/classes/com/atguigu/tingshu/album/api/AlbumInfoApiController.class index a282306..e4c2f1d 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/api/AlbumInfoApiController.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/api/AlbumInfoApiController.class differ diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/api/TrackInfoApiController.class b/service/service-album/target/classes/com/atguigu/tingshu/album/api/TrackInfoApiController.class index 4fa11a4..db39dab 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/api/TrackInfoApiController.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/api/TrackInfoApiController.class differ 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 f72b6a7..f5c873e 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/service/AlbumInfoService.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/service/AlbumInfoService.class differ 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 8b349ab..4aae1f2 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/AlbumInfoServiceImpl.class differ 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 7c48afa..e742b58 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/service/impl/TrackInfoServiceImpl.class differ diff --git a/service/service-album/target/classes/com/atguigu/tingshu/album/task/ReviewTask.class b/service/service-album/target/classes/com/atguigu/tingshu/album/task/ReviewTask.class index 439687e..cf1d1b2 100644 Binary files a/service/service-album/target/classes/com/atguigu/tingshu/album/task/ReviewTask.class and b/service/service-album/target/classes/com/atguigu/tingshu/album/task/ReviewTask.class differ 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 57eeac0..b3a1e5e 100644 Binary files a/service/service-order/target/classes/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.class and b/service/service-order/target/classes/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.class differ diff --git a/service/service-search/pom.xml b/service/service-search/pom.xml index 6e59f97..e3c9a6a 100644 --- a/service/service-search/pom.xml +++ b/service/service-search/pom.xml @@ -28,14 +28,6 @@ spring-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 25754f4..e73db86 100644 Binary files a/service/service-search/target/classes/com/atguigu/tingshu/search/receiver/SearchReciever.class and b/service/service-search/target/classes/com/atguigu/tingshu/search/receiver/SearchReciever.class differ 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 1285a22..db39428 100644 Binary files a/service/service-search/target/classes/com/atguigu/tingshu/search/service/SearchService.class and b/service/service-search/target/classes/com/atguigu/tingshu/search/service/SearchService.class differ diff --git a/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.class b/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.class index 4713d1c..6323181 100644 Binary files a/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.class and b/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/ItemServiceImpl.class differ diff --git a/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.class b/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.class index 5b2302a..5e3dca0 100644 Binary files a/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.class and b/service/service-search/target/classes/com/atguigu/tingshu/search/service/impl/SearchServiceImpl.class differ 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 703bf48..f680bae 100644 Binary files a/service/service-search/target/test-classes/com/atguigu/tingshu/search/test01.class and b/service/service-search/target/test-classes/com/atguigu/tingshu/search/test01.class differ 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 88dd539..d1ef641 100644 Binary files a/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserInfoApiController.class and b/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserInfoApiController.class differ 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 1d292a0..f715cdf 100644 Binary files a/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserListenProcessApiController.class and b/service/service-user/target/classes/com/atguigu/tingshu/user/api/UserListenProcessApiController.class differ 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 8a8748f..d35fffc 100644 Binary files a/service/service-user/target/classes/com/atguigu/tingshu/user/api/VipServiceConfigApiController.class and b/service/service-user/target/classes/com/atguigu/tingshu/user/api/VipServiceConfigApiController.class differ diff --git a/service/service-user/target/classes/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.class b/service/service-user/target/classes/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.class index 61b0d71..ae93408 100644 Binary files a/service/service-user/target/classes/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.class and b/service/service-user/target/classes/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.class differ