微信公众平台开发—24点游戏
因为申请的是个人未认证的订阅号,开启开发者模式以后没有菜单,只能使用接收消息—回复消息的方式来开发
一、实现思路
1、24点题库
第一步是建一张题库表,基础字段是:"num1"、"num2"、"num3"、"num4"、"答案"五个,如果未来需要"最难题排行"功能,需要再加入"答题数、答对数、命中率"三个字段
我的题库来自http://www.cnblogs.com/grenet/archive/2013/02/28/2936965.html
我把内容复制到excel里,按逗号切割了一下,然后又复制到数据库表里,因为想操作方便点,就去掉了带有数字"10"的题目
2、出题
先计算上一题结果,因为有404道题,从404中取一个随机数,作为题目id查询题目返回
3、计算答案
(1)先要把可能接收到的全角括号、长短横杠、"X"替换成能识别的"+-*/"
(2)然后再判断答案里的4个数字是否和题目里的一致,如果题目是"1 2 3 5"回答是"4+4+4+4"就不能通过
(3)借助ScriptEngineManager类和ScriptEngine类将表达式eval成结果,判断是否是"24"
(4)完成判断以后根据回复时间给出用户得分,用时越少得分越多,然后更新用户的"积分、答题数、答对数、命中率"和题目的"答题数、答对数、命中率"
4、排行榜
按用户表的积分大小倒序输出排名即可,需要注意的是如果前10名没有当前用户,要再输出一行当前用户的排名
5、答题状态
出题后用户会切换成答题状态,回复的内容除了关键字都会当做答案来计算,回复"24"、"答案"、"公告"和正确答案后会再切换成正常状态
二、核心源码
package org.xs.dntown.wx.game24.modules.web; import java.text.NumberFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Random; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.xs.dntown.wx.common.utils.MessageUtil; import org.xs.dntown.wx.common.web.BaseController; import org.xs.dntown.wx.core.modules.entity.UserInfo; import org.xs.dntown.wx.core.modules.entity.enums.ModuleEnum; import org.xs.dntown.wx.core.modules.entity.req.BaseMsgReq; import org.xs.dntown.wx.core.modules.service.LogService; import org.xs.dntown.wx.core.modules.service.UserService; import org.xs.dntown.wx.game24.modules.entity.Game24UserInfo; import org.xs.dntown.wx.game24.modules.entity.QuestionInfo; import org.xs.dntown.wx.game24.modules.entity.enums.StepEnum; import org.xs.dntown.wx.game24.modules.service.Game24UserService; import org.xs.dntown.wx.game24.modules.service.QuestionService; /** * 核心Controller */ @Controller(value = "game24CoreController") @RequestMapping("game24") public class CoreController extends BaseController { @Autowired private Game24UserService game24UserService; @Autowired private QuestionService questionService; @Autowired private UserService userService; @Autowired private LogService logService; /** * 文本消息 */ @ResponseBody @RequestMapping(value = "msg", produces = { "text/plain;charset=utf-8" }) public String textMsg(HttpServletRequest request) throws Exception { String result = ""; String openId = ""; String userName = ""; String content = ""; try { //得到接收消息实体 BaseMsgReq msgReq = (BaseMsgReq)request.getAttribute("msgReq"); openId = msgReq.getFromUserName(); content = msgReq.getContent(); //得到用户信息实体 UserInfo userInfo = (UserInfo)request.getAttribute("userInfo"); userName = userInfo.getUserName(); log.info("收到消息:" + msgReq.getContent()); //更新模块 userService.setModule(msgReq.getFromUserName(), ModuleEnum.game24.getValue()); //得到game24用户信息实体 Game24UserInfo gUserInfo = game24UserService.getByOpenId(userInfo.getOpenId()); if(gUserInfo == null) { log.info("新增game24用户"); //如果没有就新增 gUserInfo = new Game24UserInfo(); gUserInfo.setOpenId(userInfo.getOpenId()); gUserInfo.setModuleStep(StepEnum.normal.getValue()); gUserInfo.setStepTime(new Date()); gUserInfo.setIsAnswered(false); game24UserService.add(gUserInfo); } //刷新模块(1小时) long timeDiff = new Date().getTime() - userInfo.getModuleTime().getTime(); long hoursDiff = timeDiff / (1000 * 60 * 60); if(hoursDiff >= 1) { game24UserService.setStep(userInfo.getOpenId(), StepEnum.normal.getValue()); gUserInfo.setModuleStep(StepEnum.normal.getValue()); } log.info("开始转入步骤"); //出题 if(msgReq.getContent().equals("24")) { //计算上一道题 if(!StringUtils.isEmpty(gUserInfo.getQuestionId()) && gUserInfo.getModuleStep().equals(StepEnum.answer.getValue())) { //处理答题结果 finishAnswer(gUserInfo, msgReq); } log.info("开始下一题"); //下一题 Random r = new Random(); String questionId = String.valueOf(r.nextInt(403) + 1); gUserInfo.setQuestionId(questionId); gUserInfo.setModuleStep(StepEnum.answer.getValue()); gUserInfo.setStepTime(new Date()); gUserInfo.setIsAnswered(false); //更新模块 game24UserService.update(gUserInfo); //返回题目 QuestionInfo questionInfo = questionService.getById(questionId); result = "请听题:" + questionInfo.getNum1() + "、" + questionInfo.getNum2() + "、" + questionInfo.getNum3() + "、" + questionInfo.getNum4(); logService.addInfoLog(openId, userName, content, result, ModuleEnum.game24.getValue(), StepEnum.answer.getValue()); result = MessageUtil.msgReqToXml(msgReq, result); log.info(result); return result; } //答案 else if(msgReq.getContent().equals("答案")) { //返回答案 if(!StringUtils.isEmpty(gUserInfo.getQuestionId())) { log.info("返回答案"); //完成答题 finishAnswer(gUserInfo, msgReq); //设置步骤 gUserInfo.setModuleStep(StepEnum.normal.getValue()); gUserInfo.setStepTime(new Date()); game24UserService.update(gUserInfo); //答案 String answer = questionService.getAnswer(gUserInfo.getQuestionId()); logService.addInfoLog(openId, userName, content, answer, ModuleEnum.game24.getValue(), StepEnum.answer.getValue()); result = MessageUtil.msgReqToXml(msgReq, answer); log.info(result); return result; } } //排行榜 else if(msgReq.getContent().equals("排行") || msgReq.getContent().equals("排行榜")) { log.info("返回排行榜"); result += "【排行榜】\n"; boolean flag = false; List<Game24UserInfo> list = game24UserService.getTopScope(1000); if(list != null && list.size() > 0) { for (int i = 0; i < list.size(); i++) { if(list.get(i).getOpenId().equals(gUserInfo.getOpenId())) { flag = true; } //TODO 优化 UserInfo sUserInfo = userService.getByOpenId(list.get(i).getOpenId()); if(sUserInfo != null) { result += "No." + (i + 1) + " 『" + sUserInfo.getUserName() + "』(" + list.get(i).getScore() + "分)\n"; } } } if(!flag) { result += "No." + (Integer.parseInt(game24UserService.getRankScope(gUserInfo.getScore()))) + " 『" + userInfo.getUserName() + "』(" + gUserInfo.getScore() + "分)"; } else { result = result.substring(0, result.length() - 1); } logService.addInfoLog(openId, userName, content, result, ModuleEnum.game24.getValue(), StepEnum.normal.getValue()); result = MessageUtil.msgReqToXml(msgReq, result); log.info(result); return result; } //如果是新进来就刷新状态 if(msgReq.getContent().equals("24点")) { //完成答题 if(!StringUtils.isEmpty(gUserInfo.getQuestionId())) { finishAnswer(gUserInfo, msgReq); } //设置步骤 gUserInfo.setModuleStep(StepEnum.normal.getValue()); gUserInfo.setStepTime(new Date()); game24UserService.update(gUserInfo); } //如果是答题状态 if(gUserInfo.getModuleStep().equals(StepEnum.answer.getValue())) { //返回处理答题结果 result = finishAnswer(gUserInfo, msgReq); logService.addInfoLog(openId, userName, content, result, ModuleEnum.game24.getValue(), StepEnum.answer.getValue()); result = MessageUtil.msgReqToXml(msgReq, result); log.info(result); return result; } //返回game24公告 result += "『24点游戏』\n"; result += "【规则】\n"; result += "利用 + - * / ( ) 列出综合算式\n结果是24即可\n"; result += "【说明】\n"; result += "1、输入“24”:开始答题\n"; result += "2、输入“答案”:查看答案\n"; result += "3、输入“排行”:查看排行\n"; result += "4、输入“公告”:返回首页"; logService.addInfoLog(openId, userName, content, result, ModuleEnum.game24.getValue(), StepEnum.normal.getValue()); result = MessageUtil.msgReqToXml(msgReq, result); } catch (Exception e) { log.debug("执行失败"); logService.addErrorLog(openId, userName, content, "", ModuleEnum.game24.getValue(), StepEnum.normal.getValue(), e); e.printStackTrace(); } //返回消息 log.info(result); return result; } /** * 处理答题结果 */ private String finishAnswer(Game24UserInfo gUserInfo, BaseMsgReq msgReq) { log.info("处理答题结果"); String result = ""; //题目 QuestionInfo questionInfo = questionService.getById(gUserInfo.getQuestionId()); //计算 String answer = msgReq.getContent(); answer = answer.replace("(", "(").replace(")", ")").replace("÷", "/").replace("/", "/").replace("×", "*").replace("-", "-").replace("—", "-").replace(" ", ""); //计算表达式结果 ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine se = manager.getEngineByName("js"); Double res = (double) 0; try { //校对答案 boolean flag = true; String nums = answer.replace("(", "").replace(")", "").replace("*", "").replace("/", "").replace("-", "").replace("+", ""); if(nums.length() != 4) { flag = false; } else { char[] strs = nums.toCharArray(); Arrays.sort(strs); if(!String.valueOf(strs).equals(String.valueOf(questionInfo.getNum1()) + String.valueOf(questionInfo.getNum2()) + String.valueOf(questionInfo.getNum3()) + String.valueOf(questionInfo.getNum4()))) { flag = false; } } if(flag) { res =(Double) se.eval(answer); } else { res = 0.0; } } catch (ScriptException e) { log.debug("运算失败"); //e.printStackTrace(); } //回答正确 if(res == 24) { log.info("回答正确"); //计算用时 long timeDiff = new Date().getTime() - gUserInfo.getStepTime().getTime(); long secondsDiff = timeDiff / (1000); int score = 0; //30秒以内加5分 if(secondsDiff < 30) { score = 5; } //60秒以内加3分 else if(secondsDiff < 60) { score = 3; } //加1分 else { score = 1; } result = "答对了!用时" + secondsDiff + "s,积分 +" + score; //更新用户积分、次数、答对次数、命中率 gUserInfo.setScore(gUserInfo.getScore() + score); gUserInfo.setRightTimes(gUserInfo.getRightTimes() + 1); gUserInfo.setTimes(gUserInfo.getTimes() + 1); NumberFormat numberFormat = NumberFormat.getInstance(); numberFormat.setMaximumFractionDigits(2); gUserInfo.setHitRate(Float.valueOf(numberFormat.format((float) gUserInfo.getRightTimes() / (float) gUserInfo.getTimes() * 100))); gUserInfo.setModuleStep(StepEnum.normal.getValue()); gUserInfo.setStepTime(new Date()); gUserInfo.setIsAnswered(false); //更新试题次数、答对次数、命中率 questionInfo.setRightTimes(questionInfo.getRightTimes() + 1); questionInfo.setTimes(questionInfo.getTimes() + 1); numberFormat.setMaximumFractionDigits(2); questionInfo.setHitRate(Float.valueOf(numberFormat.format((float) questionInfo.getRightTimes() / (float) questionInfo.getTimes() * 100))); } //回答错误 else { log.info("回答错误"); //如果第一次回答 if(!gUserInfo.getIsAnswered()) { //更新用户积分、次数、答对次数、命中率 gUserInfo.setTimes(gUserInfo.getTimes() + 1); NumberFormat numberFormat = NumberFormat.getInstance(); numberFormat.setMaximumFractionDigits(2); gUserInfo.setHitRate(Float.valueOf(numberFormat.format((float) gUserInfo.getRightTimes() / (float) gUserInfo.getTimes() * 100))); gUserInfo.setIsAnswered(true); //更新试题次数、答对次数、命中率 questionInfo.setTimes(questionInfo.getTimes() + 1); numberFormat.setMaximumFractionDigits(2); questionInfo.setHitRate(Float.valueOf(numberFormat.format((float) questionInfo.getRightTimes() / (float) questionInfo.getTimes() * 100))); } //gUserInfo.setModuleStep(StepEnum.answer.getValue()); //gUserInfo.setStepTime(new Date()); result = "错误"; } //更新game24用户 game24UserService.update(gUserInfo); //更新题目 questionService.update(questionInfo); return result; } }
三、演示地址
四、源码地址
https://github.com/ctxsdhy/dntown