Browse Source

增强健壮性

lh_vote_java
lenghui 6 months ago
parent
commit
ed248ad8f2
  1. 2
      pom.xml
  2. 3
      src/main/java/com/lh/controller/VoteController.java
  3. 2
      src/main/java/com/lh/mapper/VoterMapper.java
  4. 2
      src/main/java/com/lh/service/VoteService.java
  5. 123
      src/main/java/com/lh/service/VoteServiceImpl.java
  6. 151
      src/main/java/com/lh/service/VoteServiceImpl_copy.java
  7. 6
      src/main/resources/com/lh/mapper/VoterMapper.xml
  8. 2
      src/test/java/com/lh/VoteSystemApplicationTests.java

2
pom.xml

@ -33,6 +33,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@ -72,7 +73,6 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>9.0.41</version>
</dependency>
</dependencies>
<dependencyManagement>

3
src/main/java/com/lh/controller/VoteController.java

@ -1,6 +1,7 @@
package com.lh.controller;
import com.lh.bean.RespBean;
import com.lh.exception.MyException;
import com.lh.service.VoteService;
import com.lh.until.Utils;
import org.springframework.beans.factory.annotation.Autowired;
@ -43,7 +44,7 @@ public class VoteController {
//获取某个候选人的被投票记录
@GetMapping("/getVotesByCandidate/{candidateJwcode}")
public RespBean getVotesByCandidate(@PathVariable("candidateJwcode") String candidateJwcode) {
public RespBean getVotesByCandidate(@PathVariable("candidateJwcode") String candidateJwcode) throws MyException {
return RespBean.ok("获取成功",voteService.getVotesByCandidate(candidateJwcode));
}
}

2
src/main/java/com/lh/mapper/VoterMapper.java

@ -9,7 +9,7 @@ import java.util.List;
@Mapper
public interface VoterMapper {
// 查询用户今日投票信息
//List<Voter> countVotesToday(@Param("jwcode") String jwcode);
List<Voter> countVotesToday(@Param("jwcode") String jwcode);
//插入投票记录
void insertVote(@Param("jwcode") String jwcode, @Param("candidateJwcode") String candidateJwcode,@Param("name") String namne);
//根据候选人的jwcode查询投票记录

2
src/main/java/com/lh/service/VoteService.java

@ -12,5 +12,5 @@ public interface VoteService {
//获取所有候选人
List<Candidate> getCandidates(String VoterJwcode);
//获取某个候选人的被投票记录
List<Voter> getVotesByCandidate(String candidateJwcode);
List<Voter> getVotesByCandidate(String candidateJwcode) throws MyException;
}

123
src/main/java/com/lh/service/VoteServiceImpl.java

@ -13,6 +13,8 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import java.sql.Timestamp;
@ -25,6 +27,9 @@ import java.util.concurrent.TimeUnit;
@Service
public class VoteServiceImpl implements VoteService {
private static final Logger logger = LoggerFactory.getLogger(VoteServiceImpl.class);
@Autowired
private VoterMapper voterMapper;
@Autowired
@ -42,103 +47,113 @@ public class VoteServiceImpl implements VoteService {
// 初始化时加载所有候选人数据并缓存到 Redis
@PostConstruct
public void initCandidatesCache() {
List<Candidate> candidates = candidatesMapper.getCandidates(); // 从数据库加载所有教师信息
for (Candidate candidate : candidates) {
candidateCacheRepository.saveCandidate(candidate); // 将数据保存到 Redis
public void initCandidatesCache() throws MyException {
try {
List<Candidate> candidates = candidatesMapper.getCandidates();
for (Candidate candidate : candidates) {
candidateCacheRepository.saveCandidate(candidate);
}
logger.info("Candidate cache initialized successfully.");
} catch (Exception e) {
logger.error("Failed to initialize candidate cache.", e);
throw new MyException("初始化候选人缓存失败");
}
}
//投票
// 投票
@Override
@Transactional(rollbackFor = Exception.class)
public Integer insertVote(String voterJwcode, String candidateJwcode, String voterName) throws MyException {
// 1. 检查 Redis 中用户今天的投票次数
String redisKey = "vote_count:" + voterJwcode + ":" + LocalDateTime.now().toLocalDate();
String currentVoteCount = stringRedisTemplate.opsForValue().get(redisKey);
String currentVoteCountStr = stringRedisTemplate.opsForValue().get(redisKey);
int voteCountToday = currentVoteCountStr == null ? 0 : Integer.parseInt(currentVoteCountStr);
int voteCountToday = currentVoteCount == null ? 0 : Integer.parseInt(currentVoteCount);
if (voteCountToday >= dailyVoteLimit) {
throw new MyException("今日投票次数已达上限");
}
// 2. 检查该用户是否已经为该候选人投票
String voteStatusKey = "vote_status:" + voterJwcode + ":" + candidateJwcode;
Boolean hasVoted = stringRedisTemplate.hasKey(voteStatusKey);
if (hasVoted != null && hasVoted) {
throw new MyException("您已经为该候选人投票,不能重复投票");
}
// 3. 增加候选人的投票数Hash 表和 ZSet 同步更新
String candidateKey = "candidate:" + candidateJwcode;
// 4.使用 Redis Hash 增加候选人投票数
// 使用 Redis Hash 增加候选人投票数
redisTemplate.opsForHash().increment(candidateKey, "votes", 1);
// 5.使用 Redis ZSet 增加候选人投票数
// 使用 Redis ZSet 增加候选人投票数
redisTemplate.opsForZSet().incrementScore("candidate:votes", candidateJwcode, 1);
// 6. 更新 Redis 中的投票次数
// 更新 Redis 中的投票次数
redisTemplate.opsForValue().increment(redisKey, 1);
// 7.设置 Redis 键的过期时间为当天的23:59:59
// 设置 Redis 键的过期时间为当天的23:59:59
LocalDateTime now = LocalDateTime.now();
LocalDateTime endOfDay = now.toLocalDate().atTime(23, 59, 59);
long secondsUntilEndOfDay = Duration.between(now, endOfDay).getSeconds();
// 更新投票次数的过期时间
redisTemplate.expire(redisKey, secondsUntilEndOfDay, TimeUnit.SECONDS);
//8.更新投票重复键
// 更新投票重复键
stringRedisTemplate.opsForValue().set(voteStatusKey, "true", secondsUntilEndOfDay, TimeUnit.SECONDS);
//9.控制台打印剩余长时间过期
System.out.println("Redis键" + redisKey + "将在" + secondsUntilEndOfDay + "秒后过期。");
System.out.println("Redis键" + voteStatusKey + "将在" + secondsUntilEndOfDay + "秒后过期。");
//10.将投票请求发送到 Kafka 消息队列
voteProducer.sendVoteMessage(new VoteMessage(voterJwcode,
candidateJwcode, voterName,
Timestamp.valueOf(LocalDateTime.now()).toString()));
// 将投票请求发送到 Kafka 消息队列
voteProducer.sendVoteMessage(new VoteMessage(voterJwcode, candidateJwcode, voterName, Timestamp.valueOf(LocalDateTime.now()).toString()));
return dailyVoteLimit - voteCountToday - 1;
}
//获取所有候选人
//先查redis中有没有数据没有就从数据库中查
// 获取所有候选人
@Override
public List<Candidate> getCandidates(String voterJwcode) {
// Redis 中获取按投票数排序的候选人 jwCode 列表
Set<Object> jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes();
//将jwCodes转为字符串类型
List<String> jwCodeList = new ArrayList<>();
for (Object jwCode : jwCodes) {
jwCodeList.add(jwCode.toString());
}
List<Candidate> candidateList = new ArrayList<>();
for (String jwCode : jwCodeList) {
Candidate candidate = candidateCacheRepository.getCandidate(jwCode);
candidateList.add(candidate);
}
try {
Set<Object> jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes();
List<String> jwCodeList = new ArrayList<>();
for (Object jwCode : jwCodes) {
jwCodeList.add(jwCode.toString());
}
// 插入投票记录 List 插入是否投过票的状态
for (Candidate candidate : candidateList) {
//从redis查询该用户投票状态
String voteStatusKey = "vote_status:" + voterJwcode + ":";
//获取stringRedisTemplate的所有键值
Set<String> redisCandidateJwCode = stringRedisTemplate.keys(voteStatusKey + "*");
//提取最后5位
if (redisCandidateJwCode != null) {
for (String redisKey : redisCandidateJwCode) {
String candidateJwCode = redisKey.substring(redisKey.length() - 5);
if (candidate.getJwCode().equals(candidateJwCode)) {
candidate.setVoted(true);
}
for (String jwCode : jwCodeList) {
Candidate candidate = candidateCacheRepository.getCandidate(jwCode);
if (candidate != null) {
candidateList.add(candidate);
}
}
}catch(Exception e){
//如果缓存中获取候选人失败则从数据库中获取候选人列表
candidateList = candidatesMapper.getCandidates();
}
// 插入投票记录 List 插入当日是否投过票的状态
markVotedStatus(voterJwcode, candidateList);
return candidateList;
}
//获取候选人被投票记录
// 获取候选人被投票记录
@Override
public List<Voter> getVotesByCandidate(String candidateJwcode) {
return voterMapper.getVotesByCandidate(candidateJwcode);
public List<Voter> getVotesByCandidate(String candidateJwcode) throws MyException {
try {
return voterMapper.getVotesByCandidate(candidateJwcode);
} catch (Exception e) {
throw new MyException("获取候选人投票记录失败");
}
}
private void markVotedStatus(String voterJwcode, List<Candidate> candidateList) {
String voteStatusKeyPrefix = "vote_status:" + voterJwcode + ":";
Set<String> votedKeys = stringRedisTemplate.keys(voteStatusKeyPrefix + "*");
if (votedKeys != null) {
for (Candidate candidate : candidateList) {
String fullKey = voteStatusKeyPrefix + candidate.getJwCode();
if (votedKeys.contains(fullKey)) {
candidate.setVoted(true);
}
}
}
}
}

151
src/main/java/com/lh/service/VoteServiceImpl_copy.java

@ -0,0 +1,151 @@
//package com.lh.service;
//
//import com.lh.bean.Candidate;
//import com.lh.bean.VoteMessage;
//import com.lh.bean.Voter;
//import com.lh.config.CandidateCacheRepository;
//import com.lh.exception.MyException;
//import com.lh.mapper.CandidatesMapper;
//import com.lh.mapper.VoterMapper;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import javax.annotation.PostConstruct;
//import java.sql.Timestamp;
//import java.time.Duration;
//import java.time.LocalDateTime;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Set;
//import java.util.concurrent.TimeUnit;
//
//@Service
//public class VoteServiceImpl_copy implements VoteService {
// @Autowired
// private VoterMapper voterMapper;
// @Autowired
// private CandidatesMapper candidatesMapper;
// @Value("${vote.limit.daily}")
// private int dailyVoteLimit; // 每日投票次数限制
// @Autowired
// private StringRedisTemplate stringRedisTemplate;
// @Autowired
// private VoteProducer voteProducer;
// @Autowired
// private RedisTemplate<String, Object> redisTemplate;
// @Autowired
// private CandidateCacheRepository candidateCacheRepository;
//
//
//
// // 初始化时加载所有候选人数据并缓存到 Redis
// @PostConstruct
// public void initCandidatesCache() {
// try {
// List<Candidate> candidates = candidatesMapper.getCandidates();
// for (Candidate candidate : candidates) {
// candidateCacheRepository.saveCandidate(candidate);
// }
// System.out.println("Candidate cache initialized successfully.");
// } catch (Exception e) {
// System.out.println("Failed to initialize candidate cache.");
// }
// }
//
// //投票
// @Override
// @Transactional(rollbackFor = Exception.class)
// public Integer insertVote(String voterJwcode, String candidateJwcode, String voterName) throws MyException {
// // 1. 检查 Redis 中用户今天的投票次数
// String redisKey = "vote_count:" + voterJwcode + ":" + LocalDateTime.now().toLocalDate();
// String currentVoteCount = stringRedisTemplate.opsForValue().get(redisKey);
//
// int voteCountToday = currentVoteCount == null ? 0 : Integer.parseInt(currentVoteCount);
// if (voteCountToday >= dailyVoteLimit) {
// throw new MyException("今日投票次数已达上限");
// }
//
// // 2. 检查该用户是否已经为该候选人投票
// String voteStatusKey = "vote_status:" + voterJwcode + ":" + candidateJwcode;
// Boolean hasVoted = stringRedisTemplate.hasKey(voteStatusKey);
// if (hasVoted != null && hasVoted) {
// throw new MyException("您已经为该候选人投票,不能重复投票");
// }
//
//
// // 3. 增加候选人的投票数Hash 表和 ZSet 同步更新
// String candidateKey = "candidate:" + candidateJwcode;
//
// // 4.使用 Redis Hash 增加候选人投票数
// redisTemplate.opsForHash().increment(candidateKey, "votes", 1);
//
// // 5.使用 Redis ZSet 增加候选人投票数
// redisTemplate.opsForZSet().incrementScore("candidate:votes", candidateJwcode, 1);
//
// // 6. 更新 Redis 中的投票次数
// redisTemplate.opsForValue().increment(redisKey, 1);
// // 7.设置 Redis 键的过期时间为当天的23:59:59
// LocalDateTime now = LocalDateTime.now();
// LocalDateTime endOfDay = now.toLocalDate().atTime(23, 59, 59);
// long secondsUntilEndOfDay = Duration.between(now, endOfDay).getSeconds();
// redisTemplate.expire(redisKey, secondsUntilEndOfDay, TimeUnit.SECONDS);
// //8.更新投票重复键
// stringRedisTemplate.opsForValue().set(voteStatusKey, "true", secondsUntilEndOfDay, TimeUnit.SECONDS);
// //9.控制台打印剩余长时间过期
// System.out.println("Redis键" + redisKey + "将在" + secondsUntilEndOfDay + "秒后过期。");
// System.out.println("Redis键" + voteStatusKey + "将在" + secondsUntilEndOfDay + "秒后过期。");
//
// //10.将投票请求发送到 Kafka 消息队列
// voteProducer.sendVoteMessage(new VoteMessage(voterJwcode,
// candidateJwcode, voterName,
// Timestamp.valueOf(LocalDateTime.now()).toString()));
// return dailyVoteLimit - voteCountToday - 1;
// }
//
// //获取所有候选人
// //先查redis中有没有数据没有就从数据库中查
// @Override
// public List<Candidate> getCandidates(String voterJwcode) {
// // Redis 中获取按投票数排序的候选人 jwCode 列表
// Set<Object> jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes();
// //将jwCodes转为字符串类型
// List<String> jwCodeList = new ArrayList<>();
// for (Object jwCode : jwCodes) {
// jwCodeList.add(jwCode.toString());
// }
//
// List<Candidate> candidateList = new ArrayList<>();
// for (String jwCode : jwCodeList) {
// Candidate candidate = candidateCacheRepository.getCandidate(jwCode);
// candidateList.add(candidate);
// }
//
// // 插入投票记录 List 插入是否投过票的状态
// for (Candidate candidate : candidateList) {
// //从redis查询该用户投票状态
// String voteStatusKey = "vote_status:" + voterJwcode + ":";
// //获取stringRedisTemplate的所有键值
// Set<String> redisCandidateJwCode = stringRedisTemplate.keys(voteStatusKey + "*");
// //提取最后5位
// if (redisCandidateJwCode != null) {
// for (String redisKey : redisCandidateJwCode) {
// String candidateJwCode = redisKey.substring(redisKey.length() - 8);
// if (candidate.getJwCode().equals(candidateJwCode)) {
// candidate.setVoted(true);
// }
// }
// }
// }
//
// return candidateList;
// }
// //获取候选人被投票记录
// @Override
// public List<Voter> getVotesByCandidate(String candidateJwcode) {
// return voterMapper.getVotesByCandidate(candidateJwcode);
// }
//}

6
src/main/resources/com/lh/mapper/VoterMapper.xml

@ -5,9 +5,9 @@
INSERT INTO voters(jwcode, candidate_jwcode, name)
VALUES(#{jwcode}, #{candidateJwcode}, #{name})
</insert>
<!--<select id="countVotesToday" resultType="com.lh.bean.Voter">-->
<!-- SELECT * FROM voters WHERE jwcode = #{jwcode} AND DATE(vote_time) = CURDATE()-->
<!--</select>-->
<select id="countVotesToday" resultType="com.lh.bean.Voter">
SELECT * FROM voters WHERE jwcode = #{jwcode} AND DATE(vote_time) = CURDATE()
</select>
<select id="getVotesByCandidate" resultType="com.lh.bean.Voter">
SELECT * FROM voters WHERE candidate_jwcode = #{candidateJwcode}
</select>

2
src/test/java/com/lh/VoteSystemApplicationTests.java

@ -20,7 +20,7 @@ class VoteSystemApplicationTests {
}
@Test
void contextLoads1() {
voterMapper.getVotesByCandidate("20010").forEach(System.out::println);
voterMapper.getVotesByCandidate("90044602").forEach(System.out::println);
}
@Test
void contextLoads2() {

Loading…
Cancel
Save