You've already forked FrameTour-BE
- 新增视频查看权限相关数据结构和接口 - 实现用户视频查看记录的创建和更新逻辑 - 添加视频查看权限的检查和记录功能 -优化分布式环境下的并发控制
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package com.ycwl.basic.repository;
|
||||
|
||||
import com.ycwl.basic.model.pc.video.entity.UserVideoViewEntity;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 用户视频查看记录Repository
|
||||
* 仅使用Redis存储查看记录
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class UserVideoViewRepository {
|
||||
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private static final String USER_VIDEO_VIEW_CACHE_KEY = "user_video_view:%s:%s";
|
||||
private static final String VIEW_RECORD_LOCK_KEY = "view_record_lock:%s:%s";
|
||||
private static final int CACHE_EXPIRE_HOURS = 72; // 3天过期
|
||||
private static final int LOCK_EXPIRE_SECONDS = 30;
|
||||
|
||||
/**
|
||||
* 获取用户视频查看记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
* @return 查看记录
|
||||
*/
|
||||
public UserVideoViewEntity getViewRecord(Long userId, Long videoId) {
|
||||
String cacheKey = String.format(USER_VIDEO_VIEW_CACHE_KEY, userId, videoId);
|
||||
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
String cached = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (cached != null) {
|
||||
return JacksonUtil.parseObject(cached, UserVideoViewEntity.class);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查看记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
* @param scenicId 景区ID
|
||||
* @return 查看记录
|
||||
*/
|
||||
public UserVideoViewEntity createViewRecord(Long userId, Long videoId, Long scenicId) {
|
||||
UserVideoViewEntity entity = new UserVideoViewEntity();
|
||||
entity.setUserId(userId);
|
||||
entity.setVideoId(videoId);
|
||||
entity.setScenicId(scenicId);
|
||||
entity.setViewCount(1);
|
||||
entity.setLastViewTime(new Date());
|
||||
entity.setCreateTime(new Date());
|
||||
entity.setUpdateTime(new Date());
|
||||
|
||||
String cacheKey = String.format(USER_VIDEO_VIEW_CACHE_KEY, userId, videoId);
|
||||
redisTemplate.opsForValue().set(cacheKey, JacksonUtil.toJSONString(entity),
|
||||
CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||
|
||||
log.debug("创建用户视频查看记录到Redis: userId={}, videoId={}, scenicId={}", userId, videoId, scenicId);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新查看次数
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
* @param viewCount 新的查看次数
|
||||
* @return 是否更新成功
|
||||
*/
|
||||
public boolean updateViewCount(Long userId, Long videoId, Integer viewCount) {
|
||||
UserVideoViewEntity entity = getViewRecord(userId, videoId);
|
||||
if (entity != null) {
|
||||
entity.setViewCount(viewCount);
|
||||
entity.setLastViewTime(new Date());
|
||||
entity.setUpdateTime(new Date());
|
||||
|
||||
String cacheKey = String.format(USER_VIDEO_VIEW_CACHE_KEY, userId, videoId);
|
||||
redisTemplate.opsForValue().set(cacheKey, JacksonUtil.toJSONString(entity),
|
||||
CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||
|
||||
log.debug("更新用户视频查看次数到Redis: userId={}, videoId={}, viewCount={}", userId, videoId, viewCount);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加查看次数(原子操作)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
* @return 是否更新成功
|
||||
*/
|
||||
public boolean incrementViewCount(Long userId, Long videoId) {
|
||||
UserVideoViewEntity entity = getViewRecord(userId, videoId);
|
||||
if (entity != null) {
|
||||
entity.setViewCount(entity.getViewCount() + 1);
|
||||
entity.setLastViewTime(new Date());
|
||||
entity.setUpdateTime(new Date());
|
||||
|
||||
String cacheKey = String.format(USER_VIDEO_VIEW_CACHE_KEY, userId, videoId);
|
||||
redisTemplate.opsForValue().set(cacheKey, JacksonUtil.toJSONString(entity),
|
||||
CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||
|
||||
log.debug("增加用户视频查看次数到Redis: userId={}, videoId={}, newCount={}",
|
||||
userId, videoId, entity.getViewCount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Redis分布式锁执行查看记录操作
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
* @param operation 操作函数
|
||||
* @return 操作结果
|
||||
*/
|
||||
public <T> T executeWithLock(Long userId, Long videoId, java.util.function.Supplier<T> operation) {
|
||||
String lockKey = String.format(VIEW_RECORD_LOCK_KEY, userId, videoId);
|
||||
|
||||
try {
|
||||
// 尝试获取锁
|
||||
Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked",
|
||||
LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
|
||||
if (Boolean.TRUE.equals(lockAcquired)) {
|
||||
try {
|
||||
return operation.get();
|
||||
} finally {
|
||||
// 释放锁
|
||||
redisTemplate.delete(lockKey);
|
||||
}
|
||||
} else {
|
||||
log.warn("获取查看记录锁失败: userId={}, videoId={}", userId, videoId);
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("执行查看记录操作失败: userId={}, videoId={}", userId, videoId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param videoId 视频ID
|
||||
*/
|
||||
public void clearCache(Long userId, Long videoId) {
|
||||
String cacheKey = String.format(USER_VIDEO_VIEW_CACHE_KEY, userId, videoId);
|
||||
redisTemplate.delete(cacheKey);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user