基于别踩白块游戏实现的后台排行榜功能
基于别踩白块游戏实现的后台排行榜功能
班级博客 | https://edu.cnblogs.com/campus/zjcsxy/SE2020 |
---|---|
作业要求 | https://edu.cnblogs.com/campus/zjcsxy/SE2020/homework/11334 |
作业目标 | 编写一个小程序,可以全新编写,也可以学习别人的小程序进行修改;熟悉git代码管理流程,将源代码上传到到github;在博客园班级中写一篇相应的博文 |
作业源代码 | https://github.com/zlxzgtc/wxapp |
学号 | 31801122 张乐晓 |
院系专业 | 浙大城市学院计算机系 |
前言
我认为我们这门课程最终要实现的目标是团队协作完成一个完整的项目,所以我想通过这次微信小程序的个人作业来熟悉一下前端的界面展示,以及小程序和后端的对接。因此我直接从网上拉了一份“别踩白块”的小游戏代码,通过为这个小程序添加一个排行榜的功能来快速熟悉小程序的开发过程。
小程序部分
页面展示
- 原来的程序中排行榜所对应的页面是一个记录登录日志的log页面,我将其修改为了与程序风格一致的排行榜页面。
页面布局
排行榜页面主要分为以下三个部分:
- 第一部分是用户头像、昵称、得分和排名
得分和排名在用户登陆后通过查询后台数据库得到。 - 第二部分是展示10行数据的表格
ranklist中存放了后台返回的json对象的列表,在循环中,可以直接用index来取下标,item来取元素。
排名列直接通过当前页面和下标计算得到:10*(pagenum-1)+index+1
。 - 第三部分是翻页的按钮
pagenum==1
时左边按钮为首页,pagenum==页面总数
时,右边按钮为尾页。
<!--pages/ranks/ranks.wxml-->
<view class="userinfo">
<button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
<block wx:else>
<image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
<block>
<text class="userrank">我的分数:</text>
<text class="userrank">{{userscore}}</text>
<text class="userrank">我的排名:</text>
<text class="userrank">{{userrank}}</text>
</block>
</view>
<view class="table">
<view class="tr bg-w">
<view class="th">排名</view>
<view class="th">昵称</view>
<view class="th ">成绩</view>
</view>
<block wx:for="{{ranklist}}" wx:key="key">
<view class="tr bg-w" wx:if="{{index % 2 == 0}}">
<view class="td">{{10*(pagenum-1)+index+1}}</view>
<view class="td">{{item.username}}</view>
<view class="td">{{item.score}}</view>
</view>
<view class="tr bg-g" wx:else>
<view class="td">{{10*(pagenum-1)+index+1}}</view>
<view class="td">{{item.username}}</view>
<view class="td">{{item.score}}</view>
</view>
</block>
</view>
<view class="pages">
<button wx:if="{{pagenum==1}}">首页</button>
<button wx:else bindtap="pre">上一页</button>
<text>{{pagenum}}/{{allpages}}</text>
<button wx:if="{{pagenum==allpages}}">尾页</button>
<button wx:else bindtap="next">下一页</button>
</view>
页面逻辑
1、获取用户信息,并向后台请求查询用户的排名和排行榜数据
wx.request()
:向后台发送GET请求pre()、next()
:更改当前的页码,并重新加载页面getUserInfo()
: 获取用户个人信息
// pages/ranks/ranks.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo'),
ranklist: [],
userrank: 0,
userscore: 0,
pagenum: 1,
allpages: 0
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse) {
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
var that = this
//请求排行榜数据和页面总数
wx.request({
url: 'http://127.0.0.1:5000/login',
method: 'GET',
data: {
pages: this.data.pagenum
},
success: function (res) {
// success
console.log('submit success');
that.setData({
ranklist: res.data.ranks,
allpages: res.data.allpages
})
}
})
//请求当前用户排名和用户最好成绩
wx.request({
url: 'http://127.0.0.1:5000/userRank',
method: 'GET',
data: {
username: this.data.userInfo.nickName
},
success: function (res) {
console.log('submit success');
that.setData({
userrank: res.data.rank,
userscore: res.data.score,
})
}
})
},
//上一页
pre: function () {
this.setData({
pagenum: this.data.pagenum - 1
})
this.onLoad();
},
//下一页
next: function () {
this.setData({
pagenum: this.data.pagenum + 1
})
this.onLoad();
},
//获得用户个人信息
getUserInfo: function (e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
2、用户当前得分高于历史最好成绩时,向后台请求更新排行榜中的数据
if (score > app.globalData.endlessScore) {
app.globalData.endlessScore = score
wx.setStorageSync('endlessScore', score)
if (app.globalData.userInfo)
wx.request({
url: 'http://127.0.0.1:5000/updateScore',
method: 'GET',
data: {
username: app.globalData.userInfo.nickName,
highscore:score
},
success: function(res) {
console.log('submit success');
}
})
}
本地后端
后端逻辑
使用python的flask框架,连接本地数据库对数据进行查询和修改。均使用默认的GET方法向后台请求数据。
(在微信开发者工具中需要勾选不检验合法域名)
/login
:接收用户传来的页码k
,返回当前页面的10条数据limit (k-1)*10, k*10
/userRank
:接收用户传来的username
,mysql无法直接获得列号,所以我通过设置变量@rank=@rank+1
来获取用户的排名 (这里要注意一次只能执行一条sql语句,所以要将SET语句和SELECT语句分两次来执行)/updateScore
:接收用户昵称和最高分,更新数据表中对应用户的成绩
后端代码
from flask import Flask
from flask import request
import pymysql
app = Flask(__name__)
# 分页查询ranks中的昵称和分数
@app.route('/login')
def login():
# 连接数据库
connect = pymysql.Connect(
host='localhost',
port=3306,
user='xxxxxx',
passwd='xxxxxx',
db='xxxxxx',
charset='utf8'
)
cursor = connect.cursor()
# name = request.args['username']
k = request.args['pages']
sql = "SELECT username,score FROM `ranks` order by score desc limit %d,%d" % ((int(k) - 1) * 10, int(k) * 10)
cursor.execute(sql)
list1 = []
for row in cursor.fetchall():
list1.append({"username": row[0], "score": row[1]})
sql = "SELECT count(*) FROM `ranks` "
cursor.execute(sql)
res1 = cursor.fetchall()[0]
return {"ranks": list1, "allpages": int(int(res1[0] + 9) / 10)}
# 获得用户的排名、得分和排行榜的总页数
@app.route('/userRank')
def userRank():
# 连接数据库
connect = pymysql.Connect(
host='localhost',
port=3306,
user='xxxxxx',
passwd='xxxxxx',
db='xxxxxx',
charset='utf8'
)
cursor = connect.cursor()
name = request.args['username']
sql = "SET @rank:=0"
cursor.execute(sql)
sql = "SELECT userrank,score from ( " \
"SELECT username,score,@rank:=@rank+1 userrank FROM ranks order by score desc ) p " \
"where username= '{}'".format(name)
cursor.execute(sql)
res = cursor.fetchall()[0]
sql = "SELECT count(*) FROM `ranks` "
cursor.execute(sql)
res1 = cursor.fetchall()[0]
return {"rank": res[0], "score": res[1], "allpages": int(int(res1[0] + 9) / 10)}
# 更新用户的最高分
@app.route('/updateScore')
def highScore():
# 连接数据库
connect = pymysql.Connect(
host='localhost',
port=3306,
user='xxxxxx',
passwd='xxxxxx',
db='xxxxxx',
charset='utf8'
)
cursor = connect.cursor()
name = request.args['username']
score = request.args['highscore']
sql = "UPDATE ranks SET score={} where username='{}'".format(score, name)
cursor.execute(sql)
connect.commit()
return "ok"
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000)
收获总结
在这次的个人作业中,我编写了一个小程序的页面,并通过wx.request()
来向后台请求数据,实现了一个用户成绩排行榜的功能。基本熟悉了小程序的开发过程,完成了本次作业的目标。但是由于在小程序中使用服务器需要对域名申请备案(需要4-15天),所以在这次作业中我还没有尝试将后台搭建在个人服务器中。
参考程序地址
别踩白块小程序:https://github.com/zhange1/can-not-click-white
10.26更新(将项目部署到服务器上)
完成了服务器端的配置,使小程序能够通过合法域名校验
服务器后端:python + uwsgi + nginx
创建python虚拟环境
为了避免之后部署其他应用导致环境的混乱和与第三方库的一些冲突问题,为当前的项目添加一个虚拟环境,并安装需要的库
[root@iZbp1iv8c9cefydjjokdg2Z flask]#python3 -m virtualenv env
[root@iZbp1iv8c9cefydjjokdg2Z flask]#source env/bin/activate
[root@iZbp1iv8c9cefydjjokdg2Z flask]#pip3 install flask pymysql uwsgi
使用uwsgi来连接服务器和应用
在项目目录下创建一个配置文件
[uwsgi]
#uwsgi启动时所使用的地址与端口,端口可以使用其他端口
socket=127.0.0.1:5000
# 指向网站的项目根目录
chdir=/www/wwwroot/app.morii.top/flask
#python启动程序文件
wsgi-file=app.py
callable=app
同时也要修改nginx的配置信息,在server{}中添加以下内容,注意uwsgi_pass项需要与uwsgi中保持一致
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:5000;
# 虚拟环境所在目录
uwsgi_param UWSGI_PYHOME /www/wwwroot/app.morii.top/flask/env;
# 项目根目录
uwsgi_param UWSGI_CHDIR /www/wwwroot/app.morii.top/flask;
uwsgi_param UWSGI_SCRIPT app:app;
}
修改后需要重启nginx服务
#service nginx restart
启动应用
添加-d
让uwsgi在后台运行
#uwsgi -d --ini uwsgi.ini
但是启动后发现报错了
查了一下,发现是数据库的权限问题,于是登录数据库修改权限
[root@iZbp1iv8c9cefydjjokdg2Z flask]#mysql -u root -p
mysql> grant all privileges on *.* to app_morii_top@'localhost' identified by '密码';
mysql> flush privileges;
修改完成后,在浏览器中测试了一下,终于可以返回正确的数据了!
修改微信公众平台中的设置
在开发->开发设置中修改request合法域名
详细中刷新一下项目配置,这下取消勾选不合法检验程序也能够正常获取数据啦!
git信息
commit1提交的是本地后台版本(一开始在写程序的时候没有注意使用git来进行版本管理)