From ed248ad8f29e1a01a79c3d50354b5acb90c8b1ed Mon Sep 17 00:00:00 2001 From: lenghui Date: Sat, 21 Dec 2024 18:05:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=81=A5=E5=A3=AE=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../java/com/lh/controller/VoteController.java | 3 +- src/main/java/com/lh/mapper/VoterMapper.java | 2 +- src/main/java/com/lh/service/VoteService.java | 2 +- src/main/java/com/lh/service/VoteServiceImpl.java | 123 +++++++++-------- .../java/com/lh/service/VoteServiceImpl_copy.java | 151 +++++++++++++++++++++ src/main/resources/com/lh/mapper/VoterMapper.xml | 6 +- .../java/com/lh/VoteSystemApplicationTests.java | 2 +- 8 files changed, 229 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/lh/service/VoteServiceImpl_copy.java diff --git a/pom.xml b/pom.xml index 9a594a3..3201389 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ mysql mysql-connector-java + runtime org.projectlombok @@ -72,7 +73,6 @@ javax.servlet javax.servlet-api - 9.0.41 diff --git a/src/main/java/com/lh/controller/VoteController.java b/src/main/java/com/lh/controller/VoteController.java index 305446f..0c7d8f7 100644 --- a/src/main/java/com/lh/controller/VoteController.java +++ b/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)); } } diff --git a/src/main/java/com/lh/mapper/VoterMapper.java b/src/main/java/com/lh/mapper/VoterMapper.java index 09fd863..923106d 100644 --- a/src/main/java/com/lh/mapper/VoterMapper.java +++ b/src/main/java/com/lh/mapper/VoterMapper.java @@ -9,7 +9,7 @@ import java.util.List; @Mapper public interface VoterMapper { // 查询用户今日投票信息 - //List countVotesToday(@Param("jwcode") String jwcode); + List countVotesToday(@Param("jwcode") String jwcode); //插入投票记录 void insertVote(@Param("jwcode") String jwcode, @Param("candidateJwcode") String candidateJwcode,@Param("name") String namne); //根据候选人的jwcode查询投票记录 diff --git a/src/main/java/com/lh/service/VoteService.java b/src/main/java/com/lh/service/VoteService.java index d4a19a6..18c915e 100644 --- a/src/main/java/com/lh/service/VoteService.java +++ b/src/main/java/com/lh/service/VoteService.java @@ -12,5 +12,5 @@ public interface VoteService { //获取所有候选人 List getCandidates(String VoterJwcode); //获取某个候选人的被投票记录 - List getVotesByCandidate(String candidateJwcode); + List getVotesByCandidate(String candidateJwcode) throws MyException; } diff --git a/src/main/java/com/lh/service/VoteServiceImpl.java b/src/main/java/com/lh/service/VoteServiceImpl.java index 960ce0d..bf56593 100644 --- a/src/main/java/com/lh/service/VoteServiceImpl.java +++ b/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 candidates = candidatesMapper.getCandidates(); // 从数据库加载所有教师信息 - for (Candidate candidate : candidates) { - candidateCacheRepository.saveCandidate(candidate); // 将数据保存到 Redis + public void initCandidatesCache() throws MyException { + try { + List 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 getCandidates(String voterJwcode) { - // 从 Redis 中获取按投票数排序的候选人 jwCode 列表 - Set jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes(); - //将jwCodes转为字符串类型 - List jwCodeList = new ArrayList<>(); - for (Object jwCode : jwCodes) { - jwCodeList.add(jwCode.toString()); - } - List candidateList = new ArrayList<>(); - for (String jwCode : jwCodeList) { - Candidate candidate = candidateCacheRepository.getCandidate(jwCode); - candidateList.add(candidate); - } + try { + Set jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes(); + List jwCodeList = new ArrayList<>(); + for (Object jwCode : jwCodes) { + jwCodeList.add(jwCode.toString()); + } - // 插入投票记录,为 List 插入是否投过票的状态 - for (Candidate candidate : candidateList) { - //从redis查询该用户投票状态 - String voteStatusKey = "vote_status:" + voterJwcode + ":"; - //获取stringRedisTemplate的所有键值 - Set 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 getVotesByCandidate(String candidateJwcode) { - return voterMapper.getVotesByCandidate(candidateJwcode); + public List getVotesByCandidate(String candidateJwcode) throws MyException { + try { + return voterMapper.getVotesByCandidate(candidateJwcode); + } catch (Exception e) { + throw new MyException("获取候选人投票记录失败"); + } + } + + private void markVotedStatus(String voterJwcode, List candidateList) { + String voteStatusKeyPrefix = "vote_status:" + voterJwcode + ":"; + Set votedKeys = stringRedisTemplate.keys(voteStatusKeyPrefix + "*"); + + if (votedKeys != null) { + for (Candidate candidate : candidateList) { + String fullKey = voteStatusKeyPrefix + candidate.getJwCode(); + if (votedKeys.contains(fullKey)) { + candidate.setVoted(true); + } + } + } } } diff --git a/src/main/java/com/lh/service/VoteServiceImpl_copy.java b/src/main/java/com/lh/service/VoteServiceImpl_copy.java new file mode 100644 index 0000000..5f9cb0a --- /dev/null +++ b/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 redisTemplate; +// @Autowired +// private CandidateCacheRepository candidateCacheRepository; +// +// +// +// // 初始化时加载所有候选人数据并缓存到 Redis +// @PostConstruct +// public void initCandidatesCache() { +// try { +// List 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 getCandidates(String voterJwcode) { +// // 从 Redis 中获取按投票数排序的候选人 jwCode 列表 +// Set jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes(); +// //将jwCodes转为字符串类型 +// List jwCodeList = new ArrayList<>(); +// for (Object jwCode : jwCodes) { +// jwCodeList.add(jwCode.toString()); +// } +// +// List 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 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 getVotesByCandidate(String candidateJwcode) { +// return voterMapper.getVotesByCandidate(candidateJwcode); +// } +//} diff --git a/src/main/resources/com/lh/mapper/VoterMapper.xml b/src/main/resources/com/lh/mapper/VoterMapper.xml index 53a13de..a2a999a 100644 --- a/src/main/resources/com/lh/mapper/VoterMapper.xml +++ b/src/main/resources/com/lh/mapper/VoterMapper.xml @@ -5,9 +5,9 @@ INSERT INTO voters(jwcode, candidate_jwcode, name) VALUES(#{jwcode}, #{candidateJwcode}, #{name}) - - - + diff --git a/src/test/java/com/lh/VoteSystemApplicationTests.java b/src/test/java/com/lh/VoteSystemApplicationTests.java index c87f29d..f6319c6 100644 --- a/src/test/java/com/lh/VoteSystemApplicationTests.java +++ b/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() {