项目采用前后端分离模式,使用maven进行项目构建。
项目包结构为com.magicbeans.sheer。项目为模块分为sheer-api,sheer-common,sheer-parent,sheer-service,sheer-admin(后台模板),sheer-api为项目入口类。项目运行最低需要java8。项目层次为三层架构,controller层,service层,mapper层,其中mapper采用mybatis-plus为基础的持久层框架。项目特点采用redis进行分布式存储,编码使用stream流,函数式接口,Lambada表达式。
个人赛要素:赛程*(List [Query]),房间(key-value)。(homeNo答题房间)
团队赛要素:赛程(List [Query]),房间(key-value)。团战房间(HouseUserVO 团队房间)(homeNo答题房间)
sheer-api入口类
*.sheer.aop
ApiRequestRespect(类):用于计算每个请求的访问时间
.sheer.config(包结构):
public class InitCityConfig:(初始化配置类)
// 实现CommandLineRunner类,springboot定时任务类
public class InitCityConfig extends AbstractInitConfig implements CommandLineRunner
commonService.cacheCity();
//缓存城市,从数据库中查询城市,写入缓存
public class InitNewProvinceRankDataConfig:(初始化机构数据)
//移除缓存中所有的机构
redisService.remove(StatusConstant.COUNT_PROVINCE_NEW);
//查询所有的用户
List<User> users = userService.findList("isValid", 1);
//查询当前用户的关卡数,分数,星数之和,添加排行榜缓存
int star = starRecordService.getAllStar(user.id);
//通过查询用户的邀请码获取当前用户的的所属机构,把当前机构加入到缓存队列中
redisService.zSetIncrementScore(StatusConstant.COUNT_PROVINCE_NEW, o.id, 0));
public class InitNewProvinceRankDataConfig:(初始化省人数)
// 是否初始化机构人数
redisTemplate.delete(StatusConstant.COUNT_ORGANIZATION_PERSON);
List<Organization> list = organizationService.findList("1", "1");
redisTemplate.opsForValue().set(StatusConstant.COUNT_ORGANIZATION_PERSON, objectMapper.writeValueAsString(list));
public class InitNewProvinceUserConfig:(初始化新的数据)
commonService.cacheNews();
//移除缓存中的机构人数
redisTemplate.delete(StatusConstant.COUNT_ORGANIZATION_PERSON);
//查询机构列表
List<Organization> list = organizationService.findList("1", "1");
//写入缓存
redisTemplate.opsForValue().set(StatusConstant.COUNT_ORGANIZATION_PERSON, objectMapper.writeValueAsString(list));
public class InitNewsConfig:(初始化新闻列表)
// 查询全部新闻
List<News> list = newsService.findAllByOrder();
// 根据新闻类型分别缓存
if (Objects.nonNull(news)) {
redisTemplate.opsForValue().set(StatusConstant.NEWS_CACHE, JSON.toJSONString(news));
}
if (Objects.nonNull(branch)) {
redisTemplate.opsForValue().set(StatusConstant.BRANCH_CACHE, JSON.toJSONString(branch));
}
public class InitNoticeConfig:(初始化消息)
commonService.cacheNotice();
// 查询所有公告
List<Notice> list = noticeService.findList("isValid", StatusConstant.YES);
// 缓存公告
redisTemplate.opsForValue().set(StatusConstant.NOTICE_CACHE, noticeStr);
public class InitOrganizationConfig:(初始化机构)
//缓存机构
commonService.cacheOrganization();
//查询机构,建立邀请码与机构对应的map数据
List<Organization> list = organizationService.findList("1", "1");
//移除缓存中不存在的机构,更新机构
redisTemplate.opsForHash().delete(StatusConstant.ORGANIZATION_CACHE, code);
redisTemplate.opsForHash().put(StatusConstant.ORGANIZATION_CACHE, code, JSON.toJSONString(org));
public class InitScheduleConfig:(初始化赛程及分组)
// 查询所有赛程,将赛程列表加入缓存
List<Schedule> schedules = scheduleService.findAll();
cacheUpdateUtil.addScheduleListToCache(schedules);
// 查询团队赛程,根据团队id查询赛程,,更新赛程信息,将团队赛程加入缓存
List<Team> teamList = teamService.findAll();
cacheUpdateUtil.addTeamScheduleListToCache(teamList);
public class InitSubjectConfig:(初始化题库)
//缓存题库到redis
commonService.cacheSubjectToRedis();
// 查询题库
List<Subject> subjects = subjectService.findList("type", 0);
// 查询所有答案
List<SubjectItem> subjectItems = subjectItemService.findList("1", "1");
//题库和答案进行映射 >>> 将题目与关卡进行整合 >>> 将题目存入内存中
public class InitTeamSubjectConfig:(团队题库启动加载)
//缓存题库
commonService.cacheTeamSubjectToRedis();
//查询题库 >>> 查询所有答案 >>> 将答案和题目相匹配 写入缓存
*.sheer.controller(包结构)
public class TeamController:战队赛程
public ResponseData mySchedule:查询我的赛程,通过前后文获取用户信息,在通过用户信息获取赛程信息,并返回赛程信息。
User currentUser = LoginHelper.getCurrentUser(redisService);
UserScheduleInfo usi = scheduleService.findMySchedule(currentUser);
// scheduleService.findMySchedule(service层接口)
// 通过查询缓存中的用户信息获取当前用户赛程,并加入赛程列表
public ResponseData historySchedule:查询历史赛程
page = scheduleService.findHistorySchedulePage(page, currentUser);
//通过查询缓存中的历史数据,通过排序分页后返回
KeepAliveController:心跳检测类
public ResponseData validate: 接受token,检测用户是否登录
//从redis中获取用户信息
User user = (User) redisService.get(token);
public ResponseData houseOnline:接受token,检测房间是否存在
//设置
redisService.set(PollStatusConstants.HOUSE_HEART_DUNCE +
user.getToken(), "1", PollStatusConstants.REDIS_POLL_USER_KEEP_ALLIVE_SECONDS, TimeUnit.SECONDS);
//在redis中设置房间缓存key-value
template.opsForValue().set(key, value, time, timeUnit);
MatchController:匹配请求类
public ResponseData start:@param(token,scheduleId) 开始匹配,通过查询redis中的赛程信息,比赛信息。将比赛赛程队列加入到比赛房间缓存。
//比赛匹配 (使用MatchServiceImpl类)
matchService.start(token, scheduleId, u, schedule);
//用户是否已匹配
redisService.get(PollStatusConstants.CANCEL_MATCH_ORDER + token) != null
//获取当前赛程的房间队列集合,当队列为空时,创建新的空列队,并加入到缓存
Object o = redisService.get(PollStatusConstants.TEAM_WAR_ROOMS_PREFIX + scheduleId);
//再次获取赛程情况,确认用户自己是否在队列中
redisService.set(PollStatusConstants.TEAM_WAR_ROOMS_PREFIX + schedule.getId(), queues, seconds, TimeUnit.SECONDS);
redisService.get(PollStatusConstants.MATCHED_SUCCESS_INFO_PREFIX + u.getId()) == null
//用户加入队列,赛程队列加入到房间缓存
queueList.get(0).offer(u.getToken());
redisService.set(PollStatusConstants.TEAM_WAR_ROOMS_PREFIX + schedule.getId(), queueList);
//从对方战队取出用户进行匹配,更新房间缓存
matchUser = (User) redisService.get((frontQueue.poll()));
//匹配成功后的处理(当前用户,对象用户,赛程队列,赛程)
executeMatchSuccess(u, matchUser, queueList, schedule);
//创建一个对战房间,缓存房间题目,换成匹配对象(双方当用户,对战房间,过期时间)
redisService.addList(PollStatusConstants.TEAM_HOUSE_SUBJECT_PREFIX + homeNo, subject);
redisService.set(PollStatusConstants.MATCHED_SUCCESS_INFO_PREFIX + matchUser.getId(), msi, PollStatusConstants.MATCH_SUCCESS_WAIT_TIME, TimeUnit.SECONDS);
//在队列中添加对战双方对象
redisService.addList(homeNo, currentUser);
redisService.addList(homeNo, matchUser);
//设置第一道题目开始时间
teamService.setHomeSubjectStartTime(msi.getHomeNo(), msi.getSubjectList().get(0).getId());
//创建比赛结果实体类返回前端,设置对战房间失效时间
public ResponseData cacel:@param(token,scheduleId) 取消匹配,
//取消匹配(用户,赛程)
matchService.cacelMatch(u, schedule);
//取出赛程队列,判断用户方向,根据用户方向判断用户是否在队列存在
List<Queue> queueList = (List<Queue>) redisService.get(PollStatusConstants.TEAM_WAR_ROOMS_PREFIX + schedule.getId());
Queue queue = userIsA(u, schedule) ? queueList.get(0) : queueList.get(1);
//判断用户是否已经匹配成功,匹配成功则无法取消匹配
MatchSuccessInfo msi = (MatchSuccessInfo) redisService.get(PollStatusConstants.MATCHED_SUCCESS_INFO_PREFIX + u.getId());
public ResponseData getCurrentGameInfo:@param(token,scheduleId) 团战等待界面信息。查询redis中用户是否登录,比赛开始结束情况。
//等待团战信息(用户,赛程)
matchService.currentGameInfo(u, schedule);
//创建两个房间列表
List<HouseUserVO> myTeamUserList = new ArrayList<>();
List<HouseUserVO> frontTeamUserList = new ArrayList<>();
//从缓存中取出团战队伍列表,遍历添加到房间列表中
List<Object> teamUserListA = redisService.getList(PollStatusConstants.ST + schedule.getId() + "_" + schedule.getFirstTeamId());
List<Object> teamUserListB = redisService.getList(PollStatusConstants.ST + schedule.getId() + "_" + schedule.getSecondTeamId());
for (Object o : teamUserListA) {
HouseUserVO o1 = (HouseUserVO) o;
if (userIsA(u, schedule)) {
myTeamUserList.add(o1);
} else {
frontTeamUserList.add(o1);
}
}
for (Object o : teamUserListB) {
HouseUserVO o1 = (HouseUserVO) o;
if (userIsA(u, schedule)) {
frontTeamUserList.add(o1);
} else {
myTeamUserList.add(o1);
}
}
//创建对战页面玩家信息实体,过滤重复用户
filterDuplicateUser(myTeamUserList);
//将自身加入房间放入缓存
updateTeamRoomOnlineUser(u, schedule.getFirstTeamId(), schedule);
public ResponseData getLastTime:@param(scheduleId) 获取比赛的剩余时间,通过比赛id查询当前比赛的剩余时间,返回比赛剩余时间
long lastSeconds = PollStatusConstants.TEAM_TIME_MINUTES * 60 - ((new Date().getTime() - schedule.getStartTime().getTime()) / 1000);
public ResponseData lossMatch:@param(token,scheduleId)未匹配对手记录。用户和赛程查询比赛房间情况,查询所有团队赛程房间情况
//从缓存中获取用户,获取房间列表
User u = (User) redisService.get(token);
List<Object> houseUserVOS = redisService.getList(PollStatusConstants.ST + scheduleId + "_" + u.getTeamId());
//从缓存中获取房间列表
for (Object o : houseUserVOS) {
HouseUserVO huv = (HouseUserVO) o;
if (huv.getId().equals(u.getId())) {
redisService.removeList(PollStatusConstants.ST + scheduleId + "_" + u.getTeamId(), huv);
huv.setTimeOuts(huv.getTimeOuts() + 1);
redisService.addList(PollStatusConstants.ST + scheduleId + "_" + u.getTeamId(), huv);
this.redisService.set(PollStatusConstants.TEAM_MATCH_TIMEOUTS_PREFIX + token, PollStatusConstants.TEAM_MATCH_TIMEOUTS_PREFIX + token, 35L, TimeUnit.SECONDS);
break;
}
}
TeamWarController:团队战
public ResponseData answer:答题,获取房间列表,查询房间是否存在当前用户。获取题目和答案,判断是否事最后一题,最后一题分数加倍。更具题目计算答案,并把已完成的答题情况写入缓存。最后创建一个新的队伍答题实例,返回前端。
//检查房间的用户数据,判断用户是否存在。获取当前房间的题目和答案列表,获取答题数量,计算答题分数。把答题得分加入到缓存,设置答题题目答案集缓存过期时间
TeamUserAnswer teamUserAnswer = this.teamService.answer(scheduleId, teamId, userId, subjectId, answerId, homeNo, userTime);
public ResponseData answerTimeout:答题超时,返回一个新的答题实例。
//超时提交
TeamUserAnswer teamUserAnswer = this.teamService.answerTimeout(scheduleId, teamId, userId, subjectId, homeNo);
public ResponseData isNext:进入下一题,判断双方事否完成答题
//能否进入下一题
//获取房间的用户数据,获取对象答题情况
Map map = this.teamService.subjectNext(scheduleId, teamId, homeNo, userId, subjectId, nextSubjectId);
public ResponseData userScore:本轮结果
//获取本轮答题结果
// 查询用户是否都完成了答题,完成答题后进行数据持久化
List<TeamUserGameVO> list = this.teamService.userScore(scheduleId, teamId, homeNo, userId);
public ResponseData score:团战结果
//团战结果
//从缓存中存取,没有缓存则从数据库查询,数据库未查询到,统计分数
TeamWarScoreVO teamWarScoreVO = this.teamService.teamScore(scheduleId, teamId);
public ResponseData scheduleHistoryScore:历史赛程详情
//先查缓存,缓存无则查询数据库
TeamWarScoreVO teamWarScoreVO = this.teamService.scheduleHistoryScore(scheduleId, teamId);
CommandLineRunner
是springboot的在项目启动后的回调接口,只要实现接口,即可时间功能运行。类似与开机自启动。同ApplicationRunner一样的效果。
public interface CommandLineRunner {
void run(String... var1) throws Exception;
}
redisTemplate
spring针对redis进行封装,提供了一个redisTemplate的类,主要可以操作。在使用redis操作中必须进行序列化操作/反序列化操作。
@Autowired
private RedisTemplate redisTemplate;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。