|
@@ -0,0 +1,123 @@
|
|
|
|
+package com.tzld.piaoquan.recommend.server.service.filter.strategy;
|
|
|
|
+
|
|
|
|
+import com.tzld.piaoquan.recommend.server.common.ThreadPoolFactory;
|
|
|
|
+import com.tzld.piaoquan.recommend.server.repository.VideoAppTypeStatus;
|
|
|
|
+import com.tzld.piaoquan.recommend.server.repository.VideoAppTypeStatusRepository;
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterStrategy;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
+import org.apache.commons.lang3.RandomUtils;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.apache.commons.lang3.math.NumberUtils;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
+import org.springframework.dao.DataAccessException;
|
|
|
|
+import org.springframework.data.redis.core.RedisOperations;
|
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
+import org.springframework.data.redis.core.SessionCallback;
|
|
|
|
+import org.springframework.data.redis.core.ValueOperations;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+
|
|
|
|
+import java.util.*;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @author dyp
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@Slf4j
|
|
|
|
+public class AppletVideoStatusStrategy implements FilterStrategy {
|
|
|
|
+ @Autowired
|
|
|
|
+ @Qualifier("videoRedisTemplate")
|
|
|
|
+ private RedisTemplate<String, String> videoRedisTemplate;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private VideoAppTypeStatusRepository videoAppTypeStatusRepository;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private final String videoAppTypeStatusKeyFormat = "video:active:status:%s:%s";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<Long> filter(FilterParam param) {
|
|
|
|
+ if (param == null
|
|
|
|
+ || CollectionUtils.isEmpty(param.getVideoIds())) {
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
+ }
|
|
|
|
+ return filterActiveStatusVideoId(param.getVideoIds(), param.getAppType());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 过滤上下架状态
|
|
|
|
+ * <p>
|
|
|
|
+ * 紧急需求上线
|
|
|
|
+ * 性能待优化
|
|
|
|
+ *
|
|
|
|
+ * @param idList videoId列表
|
|
|
|
+ * @param appType appType
|
|
|
|
+ */
|
|
|
|
+ private List<Long> filterActiveStatusVideoId(List<Long> idList, Integer appType) {
|
|
|
|
+ if (CollectionUtils.isEmpty(idList)) {
|
|
|
|
+ return idList;
|
|
|
|
+ }
|
|
|
|
+ List<String> keys = idList.stream()
|
|
|
|
+ .map(id -> String.format(videoAppTypeStatusKeyFormat, appType, id))
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
+ List<String> activeStatusList = videoRedisTemplate.opsForValue().multiGet(keys);
|
|
|
|
+ if (activeStatusList == null) {
|
|
|
|
+ activeStatusList = new ArrayList<>();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<Long> cacheMissVideoIds = new ArrayList<>();
|
|
|
|
+ Map<String, String> updateRedisStatus = new HashMap<>();
|
|
|
|
+ Map<Long, Integer> activeStatusMap = new HashMap<>();
|
|
|
|
+ for (int i = 0; i < idList.size(); i++) {
|
|
|
|
+ String value = activeStatusList.get(i);
|
|
|
|
+ if (StringUtils.isBlank(value)) {
|
|
|
|
+ cacheMissVideoIds.add(idList.get(i));
|
|
|
|
+ } else {
|
|
|
|
+ activeStatusMap.put(idList.get(i), NumberUtils.toInt(value));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (CollectionUtils.isNotEmpty(cacheMissVideoIds)) {
|
|
|
|
+ List<VideoAppTypeStatus> videoAppTypeStatusList = videoAppTypeStatusRepository.findAllByVideoIdInAndAppType(cacheMissVideoIds, appType);
|
|
|
|
+ Set<Long> cacheMissVideoIdSet = new HashSet<>(cacheMissVideoIds);
|
|
|
|
+ if (CollectionUtils.isNotEmpty(videoAppTypeStatusList)) {
|
|
|
|
+ // 数据库有数据
|
|
|
|
+ for (VideoAppTypeStatus videoAppTypeStatus : videoAppTypeStatusList) {
|
|
|
|
+ activeStatusMap.put(videoAppTypeStatus.getVideoId(), videoAppTypeStatus.getVideoStatus());
|
|
|
|
+ updateRedisStatus.put(String.format(videoAppTypeStatusKeyFormat, appType, videoAppTypeStatus.getVideoId()), "1");
|
|
|
|
+ cacheMissVideoIdSet.remove(videoAppTypeStatus.getVideoId());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 数据库无数据的videoId
|
|
|
|
+ if (CollectionUtils.isNotEmpty(cacheMissVideoIdSet)) {
|
|
|
|
+ for (Long videoId : cacheMissVideoIdSet) {
|
|
|
|
+ // 设置默认值
|
|
|
|
+ updateRedisStatus.put(String.format(videoAppTypeStatusKeyFormat, appType, videoId), "1");
|
|
|
|
+ activeStatusMap.put(videoId, 1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 异步更新缓存
|
|
|
|
+ ThreadPoolFactory.defaultPool().execute(() -> videoRedisTemplate.executePipelined(new SessionCallback<String>() {
|
|
|
|
+ @Override
|
|
|
|
+ public <A, B> String execute(RedisOperations<A, B> redisOperations) throws DataAccessException {
|
|
|
|
+ ValueOperations<String, String> operations = (ValueOperations<String, String>) redisOperations.opsForValue();
|
|
|
|
+ updateRedisStatus.forEach((key, value) -> operations.set(key, value, RandomUtils.nextInt(10, 15),
|
|
|
|
+ TimeUnit.DAYS));
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ }));
|
|
|
|
+
|
|
|
|
+ return idList.stream()
|
|
|
|
+ .filter(id -> activeStatusMap.containsKey(id) && activeStatusMap.get(id) == 1)
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|