|
@@ -717,22 +717,23 @@ limit 0,10;
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 查询当前用户指定声音播放进度
|
|
|
+ * 获取声音播放进度
|
|
|
*
|
|
|
- * @param trackId 声音ID
|
|
|
- * @return 前端必须返回具体数值,返回null导致前端无法触发更新播放进度定时任务
|
|
|
+ * @param trackId
|
|
|
+ * @return
|
|
|
*/
|
|
|
@GuiGuLogin(required = false)
|
|
|
+@Operation(summary = "获取声音播放进度")
|
|
|
@GetMapping("/userListenProcess/getTrackBreakSecond/{trackId}")
|
|
|
public Result<BigDecimal> getTrackBreakSecond(@PathVariable Long trackId) {
|
|
|
//1.获取当前用户ID
|
|
|
Long userId = AuthContextHolder.getUserId();
|
|
|
+ //2.如果用户登录才查询上次播放进度
|
|
|
if (userId != null) {
|
|
|
- //2.根据用户ID+声音ID查询播放进度
|
|
|
- BigDecimal bigDecimal = userListenProcessService.getTrackBreakSecond(userId, trackId);
|
|
|
- return Result.ok(bigDecimal);
|
|
|
+ BigDecimal breakSecond = userInfoService.getTrackBreakSecond(userId, trackId);
|
|
|
+ return Result.ok(breakSecond);
|
|
|
}
|
|
|
- return Result.ok(new BigDecimal("0.00"));
|
|
|
+ return Result.ok(BigDecimal.valueOf(0));
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -740,7 +741,7 @@ public Result<BigDecimal> getTrackBreakSecond(@PathVariable Long trackId) {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 根据用户ID及声音ID查询播放进度
|
|
|
+ * 根据用户ID+声音ID查询上次播放秒数
|
|
|
* @param userId
|
|
|
* @param trackId
|
|
|
* @return
|
|
@@ -784,8 +785,8 @@ public class UserListenProcessServiceImpl implements UserListenProcessService {
|
|
|
@Autowired
|
|
|
private MongoTemplate mongoTemplate;
|
|
|
|
|
|
- /**
|
|
|
- * 根据用户ID及声音ID查询播放进度
|
|
|
+ /**
|
|
|
+ * 从MongoDB查询,根据用户ID+声音ID查询上次播放秒数
|
|
|
*
|
|
|
* @param userId
|
|
|
* @param trackId
|
|
@@ -793,16 +794,17 @@ public class UserListenProcessServiceImpl implements UserListenProcessService {
|
|
|
*/
|
|
|
@Override
|
|
|
public BigDecimal getTrackBreakSecond(Long userId, Long trackId) {
|
|
|
- //1.创建查询对象:封装查询条件
|
|
|
+ //1.根据用户ID确定操作集合名称 形式为:固定前缀_用户ID=userListenProcess_userId
|
|
|
+ String collectionName = this.getCollectionName(userId);
|
|
|
+ //2.查询条件:用户ID+声音ID
|
|
|
Query query = new Query();
|
|
|
query.addCriteria(Criteria.where("userId").is(userId).and("trackId").is(trackId));
|
|
|
- //2.执行查询:注意每个用户都有自己播放进度集合
|
|
|
- String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
|
|
|
+ //3.执行查询MongoDB
|
|
|
UserListenProcess listenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
|
|
|
if (listenProcess != null) {
|
|
|
return listenProcess.getBreakSecond();
|
|
|
}
|
|
|
- return new BigDecimal("0.00");
|
|
|
+ return BigDecimal.valueOf(0);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -825,34 +827,33 @@ public class UserListenProcessServiceImpl implements UserListenProcessService {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 保存更新声音播放进度
|
|
|
+ * 更新声音播放进度
|
|
|
* @param userListenProcessVo
|
|
|
* @return
|
|
|
*/
|
|
|
-@Operation(summary = "保存更新声音播放进度")
|
|
|
@GuiGuLogin(required = false)
|
|
|
+@Operation(summary = "更新声音播放进度")
|
|
|
@PostMapping("/userListenProcess/updateListenProcess")
|
|
|
public Result updateListenProcess(@RequestBody UserListenProcessVo userListenProcessVo) {
|
|
|
//1.获取当前用户ID
|
|
|
Long userId = AuthContextHolder.getUserId();
|
|
|
+ //2.如果用户登录才查询上次播放进度
|
|
|
if (userId != null) {
|
|
|
- //2.新增或修改播放进度
|
|
|
- userListenProcessService.updateListenProcess(userId, userListenProcessVo);
|
|
|
+ userInfoService.updateUserListenProcess(userId, userListenProcessVo);
|
|
|
}
|
|
|
return Result.ok();
|
|
|
}
|
|
|
-
|
|
|
```
|
|
|
|
|
|
**UserListenProcessService**接口:
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 保存更新声音播放进度
|
|
|
+ * 更新声音播放进度
|
|
|
* @param userListenProcessVo
|
|
|
* @return
|
|
|
*/
|
|
|
-void updateListenProcess(Long userId, UserListenProcessVo userListenProcessVo);
|
|
|
+void updateUserListenProcess(Long userId, UserListenProcessVo userListenProcessVo);
|
|
|
```
|
|
|
|
|
|
**UserListenProcessServiceImpl**实现类:
|
|
@@ -865,55 +866,62 @@ private RedisTemplate redisTemplate;
|
|
|
private RabbitService rabbitService;
|
|
|
|
|
|
/**
|
|
|
- * 保存更新声音播放进度
|
|
|
+ * 更新声音播放进度
|
|
|
*
|
|
|
* @param userListenProcessVo
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
-public void updateListenProcess(Long userId, UserListenProcessVo userListenProcessVo) {
|
|
|
- //1.查询当前用户某个声音播放进度
|
|
|
- //1.1 构建查询条件
|
|
|
- Query query = new Query();
|
|
|
- query.addCriteria(Criteria.where("userId").is(userId).and("trackId").is(userListenProcessVo.getTrackId()));
|
|
|
- //1.2 执行查询:注意每个用户都有自己播放进度集合
|
|
|
- String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
|
|
|
- UserListenProcess listenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
|
|
|
- //2.如果不存在构建声音播放进度保存到MongoDB中
|
|
|
- BigDecimal breakSecond = userListenProcessVo.getBreakSecond().setScale(0, RoundingMode.HALF_UP);
|
|
|
- if (listenProcess == null) {
|
|
|
- listenProcess = new UserListenProcess();
|
|
|
- listenProcess.setUserId(userId);
|
|
|
- listenProcess.setTrackId(userListenProcessVo.getTrackId());
|
|
|
- listenProcess.setAlbumId(userListenProcessVo.getAlbumId());
|
|
|
- listenProcess.setBreakSecond(breakSecond);
|
|
|
- listenProcess.setCreateTime(new Date());
|
|
|
- listenProcess.setUpdateTime(new Date());
|
|
|
+public void updateUserListenProcess(Long userId, UserListenProcessVo userListenProcessVo) {
|
|
|
+ //1.获取用户播放进度集合名称
|
|
|
+ String collectionName = this.getCollectionName(userId);
|
|
|
+
|
|
|
+ //2.根据用户ID+声音ID查询用户播放进度
|
|
|
+ Query query = new Query(
|
|
|
+ Criteria.where("userId").is(userId)
|
|
|
+ .and("trackId").is(userListenProcessVo.getTrackId())
|
|
|
+ );
|
|
|
+ UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
|
|
|
+
|
|
|
+ //3.如果播放进度不存在,构建播放进度,保存到MongoDB
|
|
|
+ //设置播放秒数小数位,舍入模式
|
|
|
+ userListenProcessVo.setBreakSecond(userListenProcessVo.getBreakSecond().setScale(0, RoundingMode.DOWN));
|
|
|
+ if (userListenProcess == null) {
|
|
|
+ userListenProcess = new UserListenProcess();
|
|
|
+ userListenProcess.setUserId(userId);
|
|
|
+ userListenProcess.setAlbumId(userListenProcessVo.getAlbumId());
|
|
|
+ userListenProcess.setTrackId(userListenProcessVo.getTrackId());
|
|
|
+ userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond());
|
|
|
+ userListenProcess.setCreateTime(new Date());
|
|
|
+ userListenProcess.setUpdateTime(new Date());
|
|
|
+ //mongoTemplate.save(userListenProcess, collectionName);
|
|
|
} else {
|
|
|
- //3.如果已存在播放进度,更新播放进度时间及更新时间 包含文档注解ID
|
|
|
- listenProcess.setBreakSecond(breakSecond);
|
|
|
- listenProcess.setUpdateTime(new Date());
|
|
|
+ //4.如果播放进度存在,更新播放秒+更新时间
|
|
|
+ userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond());
|
|
|
+ userListenProcess.setUpdateTime(new Date());
|
|
|
+ //mongoTemplate.save(userListenProcess, collectionName);
|
|
|
}
|
|
|
- mongoTemplate.save(listenProcess, collectionName);
|
|
|
-
|
|
|
- //4.采用MQ异步更新数据库及索引库中统计信息(播放量)
|
|
|
-
|
|
|
- //4.1 确保某个用户当日内只能更新一次播放统计量-生产者幂等性(由于播放进度更新会定时调用)
|
|
|
- //4.1.1 构建生产者幂等性Key 形式:前缀:userId_albumId_trackId
|
|
|
- String key = RedisConstant.USER_TRACK_REPEAT_STAT_PREFIX + userId + "_" + userListenProcessVo.getAlbumId() + "_" + userListenProcessVo.getTrackId();
|
|
|
- //4.1.2 计算过期时间 要求:当前日内一次统计有效
|
|
|
- long ttl = DateUtil.endOfDay(new Date()).getTime() - System.currentTimeMillis();
|
|
|
- Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "", ttl, TimeUnit.MILLISECONDS);
|
|
|
- //4.2 发送更新声音统计VO消息到RabbitMQ
|
|
|
+ mongoTemplate.save(userListenProcess, collectionName);
|
|
|
+
|
|
|
+ //5.TODO 基于MQ可靠性消息,更新声音/专辑(播放量)在MySQL以及ES中统计数值
|
|
|
+ //5.1 当天内判断当前用户是否为第一次收听更新统计数值 核心:生产者生产消息幂等性
|
|
|
+ //5.1.1 关键点找出业务标识作为RedisKey:前缀:用户ID_专辑ID_声音ID
|
|
|
+ String redisKey
|
|
|
+ = RedisConstant.USER_TRACK_REPEAT_STAT_PREFIX + userId + "_" + userListenProcessVo.getAlbumId() + "_" + userListenProcessVo.getTrackId();
|
|
|
+ //5.1.2 采用Redis提供set k v ex nx 命令,设置RedisKey,设置过期时间,写入成功则发送MQ消息,更新统计数值;写入失败则忽略
|
|
|
+ Date date = new Date();
|
|
|
+ long ttl = DateUtil.endOfDay(date).getTime() - date.getTime();
|
|
|
+ Boolean flag = redisTemplate.opsForValue().setIfAbsent(redisKey, "1", ttl, TimeUnit.MILLISECONDS);
|
|
|
+ //5.2 发送MQ消息,广播消息(通知"专辑服务"更新DB,通知"搜索服务"更新ES)
|
|
|
if (flag) {
|
|
|
- //4.2.1 创建更新统“计消息VO对象
|
|
|
+ //5.2.1 构建MQ消息VO对象 包含:专辑ID、声音ID、统计类型、增量数值、businessNo-UUID(用于消费者端幂等性处理),TODO VO必须序列化接口
|
|
|
TrackStatMqVo mqVo = new TrackStatMqVo();
|
|
|
+ mqVo.setBusinessNo(IdUtil.fastUUID());
|
|
|
mqVo.setAlbumId(userListenProcessVo.getAlbumId());
|
|
|
mqVo.setTrackId(userListenProcessVo.getTrackId());
|
|
|
mqVo.setStatType(SystemConstant.TRACK_STAT_PLAY);
|
|
|
mqVo.setCount(1);
|
|
|
- mqVo.setBusinessNo(IdUtil.randomUUID());
|
|
|
- //4.2.2 发送MQ消息
|
|
|
+ //5.2.2 发送MQ消息
|
|
|
rabbitService.sendMessage(MqConst.EXCHANGE_TRACK, MqConst.ROUTING_TRACK_STAT_UPDATE, mqVo);
|
|
|
}
|
|
|
}
|
|
@@ -921,14 +929,14 @@ public void updateListenProcess(Long userId, UserListenProcessVo userListenProce
|
|
|
|
|
|
注意:要修改**TrackStatMqVo**实现序列化接口,否则会导致发送MQ消息失败
|
|
|
|
|
|
-
|
|
|
+ 
|
|
|
|
|
|
### 3.2.2 更新MySQL统计信息
|
|
|
|
|
|
在`service-album` 微服务中添加监听消息:
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.album;
|
|
|
+package com.atguigu.tingshu.album.receiver;
|
|
|
|
|
|
import com.atguigu.tingshu.album.service.TrackInfoService;
|
|
|
import com.atguigu.tingshu.common.constant.RedisConstant;
|
|
@@ -946,12 +954,11 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
-import java.io.IOException;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-14 14:23
|
|
|
+ * @create: 2025-06-09 14:30
|
|
|
*/
|
|
|
@Slf4j
|
|
|
@Component
|
|
@@ -963,13 +970,12 @@ public class AlbumReceiver {
|
|
|
@Autowired
|
|
|
private TrackInfoService trackInfoService;
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * 监听到更新声音统计消息,更新MySQL中统计数值
|
|
|
+ * 监听到更新声音统计数值MQ消息
|
|
|
*
|
|
|
* @param mqVo
|
|
|
- * @param message
|
|
|
* @param channel
|
|
|
+ * @param message
|
|
|
*/
|
|
|
@SneakyThrows
|
|
|
@RabbitListener(bindings = @QueueBinding(
|
|
@@ -977,27 +983,26 @@ public class AlbumReceiver {
|
|
|
value = @Queue(value = MqConst.QUEUE_TRACK_STAT_UPDATE, durable = "true"),
|
|
|
key = MqConst.ROUTING_TRACK_STAT_UPDATE
|
|
|
))
|
|
|
- public void updateStat(TrackStatMqVo mqVo, Message message, Channel channel) {
|
|
|
- String key = "";
|
|
|
- try {
|
|
|
- if (mqVo != null) {
|
|
|
- log.info("监听到更新声音统计消息:{}", mqVo);
|
|
|
- //1. 处理消费者幂等性问题(一个消息被多次消费带来数据不一致问题)
|
|
|
- //1.1 基于MQ消息对象中唯一标识作为Redis中Key 采用set k v ex nx命令写入Redis
|
|
|
- key = RedisConstant.BUSINESS_PREFIX + mqVo.getBusinessNo();
|
|
|
- Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "", 10, TimeUnit.MINUTES);
|
|
|
- if (flag) {
|
|
|
- //2. 更新统计数值 业务处理可能抛出异常
|
|
|
- trackInfoService.updateStat(mqVo);
|
|
|
+ public void updateTrackStat(TrackStatMqVo mqVo, Channel channel, Message message) {
|
|
|
+ if (mqVo != null) {
|
|
|
+ log.info("更新声音统计:{}", mqVo);
|
|
|
+ //1.先对消费者进行幂等性处理
|
|
|
+ //1.1 找出业务消息的标识 作为setnx中key
|
|
|
+ String redisKey = RedisConstant.USER_TRACK_REPEAT_STAT_PREFIX + "db:" + mqVo.getBusinessNo();
|
|
|
+ //1.2 尝试存入Redis
|
|
|
+ Boolean flag = redisTemplate.opsForValue().setIfAbsent(redisKey, "1", 10, TimeUnit.MINUTES);
|
|
|
+ if (flag) {
|
|
|
+ try {
|
|
|
+ //2.更新声音/专辑统计数值
|
|
|
+ trackInfoService.updateTrackStat(mqVo);
|
|
|
+ } catch (Exception e) {
|
|
|
+ redisTemplate.delete(redisKey);
|
|
|
+ //如果异常将无法处理消息发送到死信(异常)交换机->死信队列->消费者处理进入存入消费者异常消息表->人工处理
|
|
|
+ channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
}
|
|
|
}
|
|
|
- channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[专辑服务]监听更新声音统计消息异常:{}", e);
|
|
|
- //3.捕获到业务处理异常后,将消息再次进行入队,RabbitMQ再次投递消息
|
|
|
- redisTemplate.delete(key);
|
|
|
- channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
|
|
|
}
|
|
|
+ channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1006,10 +1011,10 @@ public class AlbumReceiver {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 更新声音统计数值
|
|
|
+ * 更新DB中声音/专辑统计数值
|
|
|
* @param mqVo
|
|
|
*/
|
|
|
-void updateStat(TrackStatMqVo mqVo);
|
|
|
+void updateTrackStat(TrackStatMqVo mqVo);
|
|
|
```
|
|
|
|
|
|
在**TrackInfoServiceImpl** 中添加实现
|
|
@@ -1019,35 +1024,42 @@ void updateStat(TrackStatMqVo mqVo);
|
|
|
private AlbumStatMapper albumStatMapper;
|
|
|
|
|
|
/**
|
|
|
- * 更新声音统计数值
|
|
|
- * 注意:如果声音被播放,被评论 所属专辑也需要更新统计数值
|
|
|
+ * 更新DB中声音/专辑统计数值
|
|
|
*
|
|
|
* @param mqVo
|
|
|
*/
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
-public void updateStat(TrackStatMqVo mqVo) {
|
|
|
+public void updateTrackStat(TrackStatMqVo mqVo) {
|
|
|
//1.更新声音统计数值
|
|
|
- LambdaUpdateWrapper<TrackStat> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
- updateWrapper.eq(TrackStat::getTrackId, mqVo.getTrackId());
|
|
|
- updateWrapper.eq(TrackStat::getStatType, mqVo.getStatType());
|
|
|
- updateWrapper.setSql("stat_num=stat_num+" + mqVo.getCount());
|
|
|
- trackStatMapper.update(null, updateWrapper);
|
|
|
- //2.如果统计类型是:声音播放 所属专辑统计信息也一并修改
|
|
|
- if (SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())) {
|
|
|
- LambdaUpdateWrapper<AlbumStat> albumStatLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
|
|
|
- albumStatLambdaUpdateWrapper.eq(AlbumStat::getAlbumId, mqVo.getAlbumId());
|
|
|
- albumStatLambdaUpdateWrapper.eq(AlbumStat::getStatType, SystemConstant.ALBUM_STAT_PLAY);
|
|
|
- albumStatLambdaUpdateWrapper.setSql("stat_num = stat_num+" + mqVo.getCount());
|
|
|
- albumStatMapper.update(null, albumStatLambdaUpdateWrapper);
|
|
|
+ trackStatMapper.update(
|
|
|
+ null,
|
|
|
+ new LambdaUpdateWrapper<TrackStat>()
|
|
|
+ .eq(TrackStat::getTrackId, mqVo.getTrackId())
|
|
|
+ .eq(TrackStat::getStatType, mqVo.getStatType())
|
|
|
+ .setSql("stat_num = stat_num + " + mqVo.getCount())
|
|
|
+ );
|
|
|
+
|
|
|
+ //2.如果是"播放"or"评论"还需要更新专辑统计数值
|
|
|
+ if (TRACK_STAT_PLAY.equals(mqVo.getStatType())) {
|
|
|
+ //更新专辑统计表播放量
|
|
|
+ albumStatMapper.update(
|
|
|
+ null,
|
|
|
+ new LambdaUpdateWrapper<AlbumStat>()
|
|
|
+ .eq(AlbumStat::getAlbumId, mqVo.getAlbumId())
|
|
|
+ .eq(AlbumStat::getStatType, ALBUM_STAT_PLAY)
|
|
|
+ .setSql("stat_num = stat_num + " + mqVo.getCount())
|
|
|
+ );
|
|
|
}
|
|
|
- //3.如果统计类型是:声音评论 所属专辑统计信息也一并修改
|
|
|
- if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())) {
|
|
|
- LambdaUpdateWrapper<AlbumStat> albumStatLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
|
|
|
- albumStatLambdaUpdateWrapper.eq(AlbumStat::getAlbumId, mqVo.getAlbumId());
|
|
|
- albumStatLambdaUpdateWrapper.eq(AlbumStat::getStatType, SystemConstant.ALBUM_STAT_COMMENT);
|
|
|
- albumStatLambdaUpdateWrapper.setSql("stat_num = stat_num+" + mqVo.getCount());
|
|
|
- albumStatMapper.update(null, albumStatLambdaUpdateWrapper);
|
|
|
+ if (TRACK_STAT_COMMENT.equals(mqVo.getStatType())) {
|
|
|
+ //更新专辑统计表评论量
|
|
|
+ albumStatMapper.update(
|
|
|
+ null,
|
|
|
+ new LambdaUpdateWrapper<AlbumStat>()
|
|
|
+ .eq(AlbumStat::getAlbumId, mqVo.getAlbumId())
|
|
|
+ .eq(AlbumStat::getStatType, ALBUM_STAT_COMMENT)
|
|
|
+ .setSql("stat_num = stat_num + " + mqVo.getCount())
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1248,11 +1260,10 @@ public Result<List<BaseCategory1>> getAllCategory1() {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 更新不同分类不同排行TOP20数据
|
|
|
- *
|
|
|
+ * 更新Redis小时榜
|
|
|
* @return
|
|
|
*/
|
|
|
-@Operation(summary = "更新不同分类不同排行TOP20数据")
|
|
|
+@Operation(summary = "更新Redis小时榜")
|
|
|
@GetMapping("/albumInfo/updateLatelyAlbumRanking")
|
|
|
public Result updateLatelyAlbumRanking() {
|
|
|
searchService.updateLatelyAlbumRanking();
|
|
@@ -1277,57 +1288,50 @@ void updateLatelyAlbumRanking();
|
|
|
private RedisTemplate redisTemplate;
|
|
|
|
|
|
/**
|
|
|
- * 更新不同分类不同排行TOP20数据
|
|
|
+ * 更新Redis小时榜
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
public void updateLatelyAlbumRanking() {
|
|
|
try {
|
|
|
- //1.远程调用专辑服务获取所有1级分类获取一级分类ID列表
|
|
|
+ //1.检索ES得到响应的TOP20专辑列表
|
|
|
+ //1.1 远程调用专辑服务获取一级分类ID列表
|
|
|
List<BaseCategory1> baseCategory1List = albumFeignClient.getAllCategory1().getData();
|
|
|
- if (CollectionUtil.isNotEmpty(baseCategory1List)) {
|
|
|
- //2.遍历所有一级分类ID-根据分类ID+排序方式+限制返回文档数量
|
|
|
- List<Long> category1IdList = baseCategory1List.stream().map(BaseCategory1::getId).collect(Collectors.toList());
|
|
|
-
|
|
|
- for (Long category1Id : category1IdList) {
|
|
|
-
|
|
|
- //声明当前分类hash结构Key
|
|
|
- String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
|
|
|
- //处理某个一级分类排行数据,遍历5种不同排序字段
|
|
|
- String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
|
|
|
- for (String rankingDimension : rankingDimensionArray) {
|
|
|
- SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
|
|
|
- s -> s.index(INDE_NAME)
|
|
|
- .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
|
|
|
- .sort(sort -> sort.field(f -> f.field(rankingDimension).order(SortOrder.Desc)))
|
|
|
- .size(20)
|
|
|
- .source(s1 -> s1.filter(f -> f.includes("id", "albumTitle", "albumIntro", "coverUrl", "includeTrackCount", "playStatNum", "createTime", "payType"))),
|
|
|
- AlbumInfoIndex.class
|
|
|
- );
|
|
|
- //3.解析当前分类下当前排序方式命中TOP20专辑文档列表
|
|
|
- List<Hit<AlbumInfoIndex>> hitList = searchResponse.hits().hits();
|
|
|
- if (CollectionUtil.isNotEmpty(hitList)) {
|
|
|
- //3.1 遍历命中记录对象得到Hit对象中中_source(专辑对象)
|
|
|
- List<AlbumInfoIndexVo> top20List = hitList.stream()
|
|
|
- .map(hit -> {
|
|
|
- AlbumInfoIndex albumInfoIndex = hit.source();
|
|
|
- //3.2 处理高亮
|
|
|
- Map<String, List<String>> highlightMap = hit.highlight();
|
|
|
- if (CollectionUtil.isNotEmpty(highlightMap)) {
|
|
|
- String highlightText = highlightMap.get("albumTitle").get(0);
|
|
|
- albumInfoIndex.setAlbumTitle(highlightText);
|
|
|
- }
|
|
|
- return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
|
|
|
- }).collect(Collectors.toList());
|
|
|
- //4.将不同分类不同维度的TOP20数据放入Redis
|
|
|
- redisTemplate.opsForHash().put(key, rankingDimension, top20List);
|
|
|
- }
|
|
|
+ Assert.notNull(baseCategory1List, "一级分类列表为空!");
|
|
|
+ List<Long> category1IdList = baseCategory1List
|
|
|
+ .stream()
|
|
|
+ .map(BaseCategory1::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ //1.2 循环遍历1级分类ID-构建DSL检索 条件:一级分类ID(共计15个)+排序(五种排序方式)+长度(前20)
|
|
|
+ for (Long category1Id : category1IdList) {
|
|
|
+ //定义Redis中小时榜hash的Key
|
|
|
+ String redisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
|
|
|
+ //创建绑定hash对象方便操作hash结构
|
|
|
+ BoundHashOperations<String, String, List<AlbumInfoIndex>> hashOps = redisTemplate.boundHashOps(redisKey);
|
|
|
+ //处理某个一级分类排行数据,遍历5种不同排序字段
|
|
|
+ String[] rankingDimensionArray
|
|
|
+ = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
|
|
|
+ for (String rankingDimension : rankingDimensionArray) {
|
|
|
+ SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
|
|
|
+ s -> s.index(INDEX_NAME).size(20)
|
|
|
+ .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
|
|
|
+ .sort(s1 -> s1.field(f -> f.field(rankingDimension).order(SortOrder.Desc)))
|
|
|
+ , AlbumInfoIndex.class);
|
|
|
+ //2.解析ES检索结果,将专辑列表保存到Redis中Hash中
|
|
|
+ List<Hit<AlbumInfoIndex>> hitList = searchResponse.hits().hits();
|
|
|
+ if (CollUtil.isNotEmpty(hitList)) {
|
|
|
+ List<AlbumInfoIndex> top20List = hitList.stream().map(hit -> {
|
|
|
+ AlbumInfoIndex source = hit.source();
|
|
|
+ return source;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ hashOps.put(rankingDimension, top20List);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[搜索服务]更新排行榜Redis异常:{}", e);
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("更新Redis小时榜失败!");
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
@@ -1370,7 +1374,7 @@ public Result<List<AlbumInfoIndexVo>> getRankingList(@PathVariable Long category
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 获取不同分类下不同排行维度TOP20
|
|
|
+ * 查询Redis中不同分类下不同维度TOP20专辑列表
|
|
|
* @param category1Id
|
|
|
* @param dimension
|
|
|
* @return
|
|
@@ -1382,7 +1386,7 @@ List<AlbumInfoIndexVo> getRankingList(Long category1Id, String dimension);
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 获取不同分类下不同排行维度TOP20
|
|
|
+ * 查询Redis中不同分类下不同维度TOP20专辑列表
|
|
|
*
|
|
|
* @param category1Id
|
|
|
* @param dimension
|
|
@@ -1390,13 +1394,21 @@ List<AlbumInfoIndexVo> getRankingList(Long category1Id, String dimension);
|
|
|
*/
|
|
|
@Override
|
|
|
public List<AlbumInfoIndexVo> getRankingList(Long category1Id, String dimension) {
|
|
|
- //方式一:根据key+filed获取hash的VALUE
|
|
|
- String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
|
|
|
- //List<AlbumInfoIndexVo> list = (List<AlbumInfoIndexVo>) redisTemplate.opsForHash().get(key, dimension);
|
|
|
- //方式二:创建操作hash对象
|
|
|
- BoundHashOperations<String, String, List<AlbumInfoIndexVo>> boundHashOperations = redisTemplate.boundHashOps(key);
|
|
|
- List<AlbumInfoIndexVo> list = boundHashOperations.get(dimension);
|
|
|
- return list;
|
|
|
+ //定义Redis中小时榜hash的Key
|
|
|
+ String redisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
|
|
|
+ String field = dimension;
|
|
|
+ //创建绑定hash对象方便操作hash结构
|
|
|
+ BoundHashOperations<String, String, List<AlbumInfoIndex>> hashOps = redisTemplate.boundHashOps(redisKey);
|
|
|
+ Boolean flag = hashOps.hasKey(field);
|
|
|
+ if (flag) {
|
|
|
+ List<AlbumInfoIndex> list = hashOps.get(field);
|
|
|
+ List<AlbumInfoIndexVo> albumInfoIndexVoList = list
|
|
|
+ .stream()
|
|
|
+ .map(albumInfoIndex -> BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ return albumInfoIndexVoList;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
```
|
|
|
|