소스 검색

高并发投票

孙永军 1 년 전
부모
커밋
bca7d59a53

+ 2 - 0
src/main/java/com/smcic/api/operate/entity/KeysConst.java

@@ -10,4 +10,6 @@ public class KeysConst {
     public static final String KAFKA_QUEUE = "operate_vote_queue";
 
     public static final String KAFKA_LOCK = "OPERATE_KAFKA_LOCK";
+
+    public static final String ENROLL_LIST = "OPERATE_ENROLL_LIST_";
 }

+ 66 - 6
src/main/java/com/smcic/api/operate/service/impl/EnrollInfoServiceImpl.java

@@ -2,17 +2,27 @@ package com.smcic.api.operate.service.impl;
 
 import cn.hutool.core.date.DateTime;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.smcic.api.operate.entity.EnrollInfo;
+import com.smcic.api.operate.entity.KeysConst;
 import com.smcic.api.operate.mapper.EnrollInfoMapper;
 import com.smcic.api.operate.service.IEnrollInfoService;
 import com.smcic.api.operate.service.OSSService;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.http.util.TextUtils;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import java.time.Duration;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -23,6 +33,7 @@ import java.util.List;
  * @since 2022-12-10
  */
 @Service
+@Slf4j
 public class EnrollInfoServiceImpl extends ServiceImpl<EnrollInfoMapper, EnrollInfo> implements IEnrollInfoService {
     @Resource
     private OSSService ossService;
@@ -30,6 +41,15 @@ public class EnrollInfoServiceImpl extends ServiceImpl<EnrollInfoMapper, EnrollI
     @Resource
     private VoteInfoServiceImpl voteInfoService;
 
+    @Resource
+    private RedisTemplate<String, String> redisTemplate;
+
+    @Resource
+    private ObjectMapper objectMapper;
+
+    @Resource
+    private OperateServiceImpl operateService;
+
     public void store(String phone, String name, String university, String isOnLine, String type, Integer operateId, String introduction, String workTitle, MultipartFile file){
 
         String url = null;
@@ -50,25 +70,65 @@ public class EnrollInfoServiceImpl extends ServiceImpl<EnrollInfoMapper, EnrollI
             one.setUpdateTime(enrollInfo.getUpdateTime());
             one.setType(enrollInfo.getType());
             updateById(one);
+            enrollInfo = one;
         }else{
             save(enrollInfo);
         }
+
+        pushEnrollCache(enrollInfo);
+    }
+
+    public void pushEnrollCache(EnrollInfo enrollInfo){
+        try {
+            redisTemplate.opsForValue().set(KeysConst.TARGET + enrollInfo.getOperateId() + "_" + enrollInfo.getPhone(),
+                    objectMapper.writeValueAsString(enrollInfo),
+                    Duration.ofSeconds(operateService.getTTl(enrollInfo.getOperateId())));
+            redisTemplate.opsForList().rightPush(KeysConst.ENROLL_LIST + enrollInfo.getOperateId(), objectMapper.writeValueAsString(enrollInfo));
+            redisTemplate.expire(KeysConst.ENROLL_LIST + enrollInfo.getOperateId(),
+                    Duration.ofSeconds(operateService.getTTl(enrollInfo.getOperateId())));
+        } catch (JsonProcessingException e) {
+            log.error("报名信息入缓存失败", e);
+        }
+    }
+
+    // 报名信息db -> redis
+    public void pushAllCache(Integer operateId){
+        redisTemplate.delete(KeysConst.ENROLL_LIST + operateId);
+        lambdaQuery().eq(EnrollInfo::getOperateId, operateId).list().forEach(this::pushEnrollCache);
     }
 
     public List<EnrollInfo> list(String type, Integer operate, String phone){
-        voteInfoService.lsVerify(phone);
+        return getEnrollInfos(type, operate, phone, Comparator.comparing(EnrollInfo::getSort));
 
-        return lambdaQuery().eq(!TextUtils.isEmpty(type), EnrollInfo::getType, type).eq(EnrollInfo::getOperateId, operate)
+        /*return lambdaQuery().eq(!TextUtils.isEmpty(type), EnrollInfo::getType, type).eq(EnrollInfo::getOperateId, operate)
                 .eq(EnrollInfo::getStatus, 1)
-                .orderByAsc(EnrollInfo::getSort).list();
+                .orderByAsc(EnrollInfo::getSort).list();*/
     }
 
     public List<EnrollInfo> rank(String type, Integer operate, String phone){
-        voteInfoService.lsVerify(phone);
+        return getEnrollInfos(type, operate, phone, Comparator.comparing(EnrollInfo::getVotes));
 
-        return lambdaQuery().eq(!TextUtils.isEmpty(type), EnrollInfo::getType, type).eq(EnrollInfo::getOperateId, operate)
+        /*return lambdaQuery().eq(!TextUtils.isEmpty(type), EnrollInfo::getType, type).eq(EnrollInfo::getOperateId, operate)
                 .eq(EnrollInfo::getStatus, 1)
-                .orderByDesc(EnrollInfo::getVotes).list();
+                .orderByDesc(EnrollInfo::getVotes).list();*/
+    }
+
+    private List<EnrollInfo> getEnrollInfos(String type, Integer operate, String phone, Comparator<EnrollInfo> comparing) {
+        voteInfoService.lsVerify(phone);
+        List<String> range = redisTemplate.opsForList().range(KeysConst.ENROLL_LIST + operate, 0, -1);
+        if (null == range || range.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        return range.stream().map(data -> {
+                    try {
+                        return objectMapper.readValue(data, EnrollInfo.class);
+                    } catch (JsonProcessingException e) {
+                        log.error("报名信息反序列化失败", e);
+                    }
+                    return null;
+                }).filter(data -> null != data && data.getStatus() == 1 && (!TextUtils.isEmpty(type) && type.equals(data.getType())))
+                .sorted(comparing).collect(Collectors.toList());
     }
 
 }

+ 8 - 0
src/main/java/com/smcic/api/operate/service/impl/OperateServiceImpl.java

@@ -8,6 +8,7 @@ import com.smcic.core.advice.APIException;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 
 /**
  * <p>
@@ -33,4 +34,11 @@ public class OperateServiceImpl extends ServiceImpl<OperateMapper, Operate> impl
         }
 
     }
+
+    public Long getTTl(Integer operateId){
+        LocalDateTime now = LocalDateTime.now();
+        Operate ope = getById(operateId);
+        if(null == ope.getEndTime()) return 3600 * 24 * 365L;
+        return ope.getEndTime().isAfter(now) ? ope.getEndTime().toEpochSecond(ZoneOffset.of("+8")) - now.toEpochSecond(ZoneOffset.of("+8")) : 1L;
+    }
 }

+ 7 - 2
src/main/java/com/smcic/api/operate/service/impl/VoteInfoServiceImpl.java

@@ -54,9 +54,10 @@ public class VoteInfoServiceImpl extends ServiceImpl<VoteInfoMapper, VoteInfo> i
     @Resource
     private ObjectMapper objectMapper;
 
+    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
 
     public void vote(String target, String source, String client, Integer operateId){
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
+
         String dt = formatter.format(LocalDate.now());
         operateService.verify(operateId);
         long max = 2L;
@@ -148,7 +149,11 @@ public class VoteInfoServiceImpl extends ServiceImpl<VoteInfoMapper, VoteInfo> i
     }
 
     public Long votedTimes(String phone){
-        return lambdaQuery().eq(VoteInfo::getSource, phone).eq(VoteInfo::getDt, LocalDate.now()).count();
+        String source = redisTemplate.opsForValue().get(KeysConst.SOURCE + formatter.format(LocalDate.now()) + phone);
+        if(TextUtils.isEmpty(source))
+            return 0L;
+        return Long.valueOf(source);
+        //return lambdaQuery().eq(VoteInfo::getSource, phone).eq(VoteInfo::getDt, LocalDate.now()).count();
     }
 
 }