目录

1.显示背包的已解锁/未解锁格子数

2.显示背包中的道具物品

3.用户购买道具的时候,判断背包存储是否达到上限

4.道具也可以使用积分购买

5.在商城界面根据金额/积分显示不同商品

6.背包解锁

1.显示背包的已解锁/未解锁格子数

1.设计种植园的配置参数

在背包中显示道具,会涉及到用户的背包格子的显示以及解锁问题,所以我们需要在服务端准备一个参数信息, 用于保存种植园中用户的业务参数,例如:

格子的初始化数量, 每次解锁背包格子的价格等等.

参数信息的保存与之前项目配置的信息有所不同, 不同的地方在于, 参数信息仅仅是种植园额业务参数,会在项目运营的时候允许有所改动,而项目配置的变量参数则在项目上线以后基本不做改动.

所以我们可以才用数据库表的方式来保存种植园的参数信息.orchard/models.py代码:

class Setting(BaseModel):
"""参数信息"""
__tablename__ = "mf_orchard_setting"
title=db.Column(db.String(255), comment="提示文本")
value=db.Column(db.String(255), comment="数值")

2.在配置表中添加种植园公共配置参数

添加测试数据,sql语句:

INSERT INTO mofang.mf_orchard_setting
(id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES
(1, 'package_number_base', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '背包格子基础数量', '4'),
(2, 'package_number_max', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '背包格子上限数量', '32'),
(3, 'td_prop_max', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '单个格子存储道具数量上限', '10'),
(4, 'package_unlock_price_1', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格5-8', '10'),
(5, 'package_unlock_price_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格9-12', '50'),
(6, 'package_unlock_price_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格13-16', '100'),
(7, 'package_unlock_price_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格17-20', '200'),
(8, 'package_unlock_price_5', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格21-24', '500'),
(9, 'package_unlock_price_6', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格25-28', '1000'),
(10, 'package_unlock_price_7', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '解锁背包格子价格29-32', '5000');

3.后端:用户应该在登录时获取到后端发送过来的种植园公共参数和用户私人参数

服务端在用户登陆种植园得到时候需要返回种植园的公共参数以及用户的私有参数,orchard/socket.py, 代码:

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting
from status import APIStatus as status
from message import ErrorMessage as errmsg # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang")
def user_login(data):
# 分配房间
room = data["uid"]
join_room(room)
# 保存当前用户和sid的绑定关系
# 判断当前用户是否在mongo中有记录
query = {
"_id": data["uid"]
}
ret = mongo.db.user_info_list.find_one(query)
if ret:
mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
else:
mongo.db.user_info_list.insert_one({
"_id": data["uid"],
"sid": request.sid,
}) # A.返回种植园的相关配置参数
orchard_settings = {}
# 1.查询公共配置表得到种植园所有公共配置
setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
# 2.重新构造数据结构类型{配置名1:配置对应的值1,配置名2:配置对应的值2}
for item in setting_list:
orchard_settings[item.name] = item.value # B.返回当前用户相关的配置参数
user_settings = {}
# 1.从mongo中查找用户信息
dict = mongo.db.user_info_list.find_one({"sid":request.sid})
# 2.新用户如果在mongo查询不到格子数,则用户的默认格子数为4
if dict.get("package_number") is None:
user_settings["package_number"] = orchard_settings.get("package_number_base",4)
# 3.将用户格子数更新到mongo中
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
# 4.如果在mongo中能查询到当前用户的格子数,则将格子数取出来赋值给user_setting
else:
user_settings["package_number"] = dict.get("package_number")
# 5.将种植园公共配置和用户私有配置返回给前端
socketio.emit("login_response", {
"errno":status.CODE_OK,
"errmsg":errmsg.ok,
"orchard_settings":orchard_settings,
"user_settings":user_settings
}, namespace="/mofang", room=room)

4.前端:接收到种植园公共配置和用户私有配置并通过fsave保存在前端

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
this.checkout();
this.money = this.game.fget("money");
this.buy_prop();
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
});
},
// ***用户登录种植园后获取种植园公有配置和私有配置***
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings
});
});
},
go_index(){
this.game.goWin("root");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
go_my_package(){
// 我的背包
this.game.goFrame("package","package.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
buy_prop(){
api.addEventListener({
name: 'buy_prop'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("user_buy_prop",ret.value);
}
});
},
}
});
}
</script>
</body>
</html>

用户登录种植园后获取种植园公有配置和私有配置

5.前端:背包显示用户真实的解锁格子和未解锁格子

1.在背包页面获取种植园公有配置和用户私有配置

2.通过computed计算属性计算出用户解锁格子数和用户未解锁格子数

3.在HTML页面通过for循环显示解锁格子和未解锁格子

package.html ,代码:

<!DOCTYPE html>
<html>
<head>
<title>我的背包</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
</head>
<body>
<div class="app frame avatar add_friend package" id="app">
<div class="box">
<p class="title">我的背包</p>
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="prop_list">
<div class="item" v-for="number in unlock_td_number"></div>
<div class="item lock" v-for="number in lock_td_number"></div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
td: 36, // 背包格子总数量
user_id: "", // 当前登陆用户Id
orchard_settings:{}, // 种植园相关公共参数
user_settings:{}, // 用户相关私有参数
prev:{name:"",url:"",params:{}},
current:{name:"package",url:"package.html",params:{}},
}
},
computed:{// 计算属性
lock_td_number(){
// 未解锁的格子
return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number);
},
unlock_td_number(){
// 解锁的格子
return parseInt( this.user_settings.package_number);
}
},
created(){
this.user_id = this.game.get("id") || this.game.fget("id");
this.orchard_settings = JSON.parse(this.game.fget("orchard_settings"));
this.user_settings = JSON.parse(this.game.fget("user_settings"));
},
methods:{
close_frame(){
this.game.outFrame("package");
},
}
});
}
</script>
</body>
</html>

背包显示真实的解锁格子和未解锁格子

2.显示背包中的道具物品

接着往下就应用显示背包中的道具物品了

在显示道具的时候, 因为每个格子有存储上限,所以我们可以在参数配置中新增一项参数限制存储道具的数量, (在上面添加测试数据时已经设置了.)

INSERT INTO mofang.mf_orchard_setting
(id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES
(3, 'td_prop_max', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '单个格子存储道具数量上限', '10'),

orchard/socket.py ,代码:

1.后端获取用户当前道具物品并且按照格子规则进行分开

socket.py,代码

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting
from status import APIStatus as status
from message import ErrorMessage as errmsg @socketio.on("user_prop", namespace="/mofang")
def user_prop():
"""用户道具"""
# 1.查询当前用户的mongo对象
userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
# 2.获取当前用户的道具物品列表
prop_list = userinfo.get("prop_list")
prop_id_list = [] # 用户所拥有所有道具的id值
for prop_str,num in prop_list.items():
pid = int(prop_str[5:]) # 获取每个道具的id值
prop_id_list.append(pid) # prop_id_list用来存储用户所拥有所有道具的id值 data = []
# 3.查询用户所拥有的所有道具的所有信息(道具价格、道具图片等信息)
prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
# 4.查询种植园公共配置:每个格子可容纳的最大数量
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) # 5.循环用户拥有的每个道具
for prop_data in prop_list_data:
# 查询用户所拥有每个道具对应的数量
num = int( prop_list[("prop_%s" % prop_data.id)])
if td_prop_max > num: # 如果道具数量小于单个格子最大容纳值
data.append({ # 构造data数据结构:包括道具id/道具数量/道具图片
"num": num,
"image": prop_data.image,
"pid": prop_data.id
})
else: # 如果道具数量大于单个格子最大容纳值
padding_time = num // td_prop_max # 545//100=5
padding_last = num % td_prop_max # 545%100=45
# 容量已满的整格子
arr = [{
"num": td_prop_max,
"image": prop_data.image,
"pid": prop_data.id
}] * padding_time
# 余下的未满的单独再占一个格子
if padding_last != 0:
arr.append({
"num": padding_last,
"image": prop_data.image,
"pid": prop_data.id
})
data = data + arr room = request.sid
# 6.将背包中的道具物品/道具数量/所占格子等返回给前端
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
}, namespace="/mofang",
room=room)

2.前端获取用户道具

1.用户登录种植园后,前端向后端发送请求(user_prop),请求去获取用户背包的道具数据

connect(){
this.socket.emit("user_prop");
});
},

2.后端接收请求(user_prop),将用户道具数据响应(user_prop_response)给前端

@socketio.on("user_prop", namespace="/mofang")
def user_prop(): socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
}, namespace="/mofang",
room=room)

3.前端接收来自后端的响应数据(user_prop_response),并将用户道具数据存储在前端

user_package(){
// 用户背包道具列表
this.socket.on("user_prop_response",(message)=>{
this.game.fsave({
"user_package":message.data,
})
})
},

在客户端中获取道具, orchard.html, 代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
this.checkout();
this.money = this.game.fget("money");
this.buy_prop();
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
this.socket.emit("user_prop");
});
},
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings
});
});
},
user_package(){
// 用户背包道具列表
this.socket.on("user_prop_response",(message)=>{
this.game.fsave({
"user_package":message.data,
})
})
},
go_index(){
this.game.goWin("root");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
go_my_package(){
// 我的背包
this.game.goFrame("package","package.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
buy_prop(){
api.addEventListener({
name: 'buy_prop'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("user_buy_prop",ret.value);
}
});
}, }
});
}
</script>
</body>
</html>

客户端获取道具

3.在背包页面显示用户所拥有的道具

package.html中显示, 代码:

<!DOCTYPE html>
<html>
<head>
<title>我的背包</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
</head>
<body>
<div class="app frame avatar add_friend package" id="app">
<div class="box">
<p class="title">我的背包</p>
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="prop_list">
<div class="item" v-for="prop in user_package" @click="use_prop(prop.pid)">
<img :src="settings.static_url+prop.image" alt="">
<span>{{prop.num}}</span>
</div>
<div class="item" v-for="number in unlock_td_number"></div>
<div class="item lock" v-for="number in lock_td_number"></div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
td: 36, // 背包格子总数量
user_id: "", // 当前登陆用户Id
orchard_settings:{}, // 种植园相关公共参数
user_settings:{}, // 用户相关私有参数
user_package:[], // 用户背包信息
prev:{name:"",url:"",params:{}},
current:{name:"package",url:"package.html",params:{}},
}
},
computed:{// 计算属性
lock_td_number(){
// 未解锁的格子
return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number);
},
unlock_td_number(){
// 解锁的格子
return parseInt( this.user_settings.package_number - this.user_package.length);
}
},
created(){
this.user_id = this.game.get("id") || this.game.fget("id");
this.orchard_settings = JSON.parse(this.game.fget("orchard_settings"));
this.user_settings = JSON.parse(this.game.fget("user_settings"));
this.user_package = JSON.parse(this.game.fget("user_package"));
},
methods:{
use_prop(pid){
// 发起使用道具的通知
},
close_frame(){
this.game.outFrame("package");
},
}
});
}
</script>
</body>
</html>

背包页面显示用户所拥有的道具

4.如果道具已经被使用/新增道具购买,服务端响应数据到本地

在道具如果被使用或者新增购买, 则服务端响应新的数据到本地,socket.py代码

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting
from status import APIStatus as status
from message import ErrorMessage as errmsg # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang")
def user_login(data):
# 分配房间
room = data["uid"]
join_room(room)
# 保存当前用户和sid的绑定关系
# 判断当前用户是否在mongo中有记录
query = {
"_id": data["uid"]
}
ret = mongo.db.user_info_list.find_one(query)
if ret:
mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
else:
mongo.db.user_info_list.insert_one({
"_id": data["uid"],
"sid": request.sid,
}) # 返回种植园的相关配置参数
orchard_settings = {}
setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
"""
现在的格式:
[<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
需要返回的格式:
{
package_number_base:4,
package_number_max: 32,
...
}
"""
for item in setting_list:
orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数
user_settings = {}
# 从mongo中查找用户信息,判断用户是否激活了背包格子
dict = mongo.db.user_info_list.find_one({"sid":request.sid})
# 背包格子
if dict.get("package_number") is None:
user_settings["package_number"] = orchard_settings.get("package_number_base",4)
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
else:
user_settings["package_number"] = dict.get("package_number") socketio.emit("login_response", {
"errno":status.CODE_OK,
"errmsg":errmsg.ok,
"orchard_settings":orchard_settings,
"user_settings":user_settings
}, namespace="/mofang", room=room) @socketio.on("user_buy_prop", namespace="/mofang")
def user_buy_prop(data):
"""用户购买道具"""
room = request.sid
# 从mongo中获取当前用户信息
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return
# 从mysql中获取商品价格
prop = Goods.query.get(data["pid"])
if float(user.money) < float(prop.price) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
return
# 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
query = {"sid": request.sid}
if user_info.get("prop_list") is None:
"""此前没有购买任何道具"""
message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
mongo.db.user_info_list.update_one(query,message)
else:
"""此前有购买了道具"""
prop_list = user_info.get("prop_list") # 道具列表
if ("prop_%s" % prop.id) in prop_list:
"""如果再次同一款道具"""
prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
else:
"""此前没有购买过这种道具"""
prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
get_user_prop()
socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room) @socketio.on("user_prop", namespace="/mofang")
def user_prop():
get_user_prop() def get_user_prop():
"""用户道具"""
userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
prop_list = userinfo.get("prop_list")
prop_id_list = []
for prop_str,num in prop_list.items():
pid = int(prop_str[5:])
prop_id_list.append(pid) data = []
prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) for prop_data in prop_list_data:
num = int( prop_list[("prop_%s" % prop_data.id)])
if td_prop_max > num:
data.append({
"num": num,
"image": prop_data.image,
"pid": prop_data.id
})
else:
padding_time = num // td_prop_max
padding_last = num % td_prop_max
arr = [{
"num": td_prop_max,
"image": prop_data.image,
"pid": prop_data.id
}] * padding_time
if padding_last != 0:
arr.append({
"num": padding_last,
"image": prop_data.image,
"pid": prop_data.id
})
data = data + arr room = request.sid
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
}, namespace="/mofang",
room=room)

3.用户购买道具的时候,判断背包存储是否达到上限

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting
from status import APIStatus as status
from message import ErrorMessage as errmsg @socketio.on("user_buy_prop", namespace="/mofang")
def user_buy_prop(data):
"""用户购买道具"""
room = request.sid
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return # 判断背包物品存储是否达到上限
use_package_number = int(user_info.get("use_package_number",0)) # 当前已经使用的格子数量
package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量 setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) # ***计算购买道具以后需要额外占用的格子数量***
if ("prop_%s" % data["pid"]) in user_info.get("prop_list"): # 如果用户购买过当前道具
"""A.曾经购买过当前道具""" # 1.获取购买前的道具数量
prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]])
# 2.如果成功购买道具以后的数量
new_prop_num = prop_num+int(data["num"]) # 3.计算购买前道具所占用的格子数量
old_td_num = prop_num // td_prop_max
if prop_num % td_prop_max > 0:
old_td_num+=1
# 4.计算如果成功购买后道具所占用的格子数量
new_td_num = new_prop_num // td_prop_max
if new_prop_num % td_prop_max > 0:
new_td_num+=1
# 5.计算本次购买道具所需要占用的格子数量
td_num = new_td_num - old_td_num
else:
"""B.新增购买的道具"""
# 计算本次购买道具需要占用的格子数量
if int(data["num"]) > td_prop_max:
"""需要多个格子"""
td_num = int(data["num"]) // td_prop_max
if int(data["num"]) % td_prop_max > 0:
td_num+=1
else:
"""需要一个格子"""
td_num = 1 if use_package_number+td_num > package_number:
"""如果超出存储上限,提示错误:达到背包上限"""
socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
namespace="/mofang", room=room)
return # 从mysql中获取商品价格
prop = Goods.query.get(data["pid"])
if float(user.money) < float(prop.price) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
return
# 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
query = {"sid": request.sid}
if user_info.get("prop_list") is None:
"""此前没有购买任何道具"""
message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
mongo.db.user_info_list.update_one(query,message)
else:
"""此前有购买了道具"""
prop_list = user_info.get("prop_list") # 道具列表
if ("prop_%s" % prop.id) in prop_list:
"""如果再次同一款道具"""
prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
else:
"""此前没有购买过这种道具"""
prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 返回购买成功的信息
socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
# 返回最新的用户道具列表
user_prop() @socketio.on("user_prop", namespace="/mofang")
def user_prop():
"""用户道具"""
userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
prop_list = userinfo.get("prop_list")
prop_id_list = []
for prop_str,num in prop_list.items():
pid = int(prop_str[5:])
prop_id_list.append(pid) data = []
prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) for prop_data in prop_list_data:
num = int( prop_list[("prop_%s" % prop_data.id)])
if td_prop_max > num:
data.append({
"num": num,
"image": prop_data.image,
"pid": prop_data.id
})
else:
padding_time = num // td_prop_max
padding_last = num % td_prop_max
arr = [{
"num": td_prop_max,
"image": prop_data.image,
"pid": prop_data.id
}] * padding_time
if padding_last != 0:
arr.append({
"num": padding_last,
"image": prop_data.image,
"pid": prop_data.id
})
data = data + arr
# 保存当前用户已经使用的格子数量
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
room = request.sid
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
}, namespace="/mofang",
room=room)

用户购买道具的时候,判断背包存储是否达到上限

4.道具也可以使用积分购买

在购买道具的流程中, 增加积分(果子)商品.

orchard/marshmallow.py,代码:

from message import ErrorMessage as Message
from .models import Goods,db
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field
from marshmallow import post_dump
class GoodsInfoSchema(SQLAlchemyAutoSchema):
id = auto_field()
name = auto_field()
price = auto_field()
image = auto_field()
remark = auto_field()
credit = auto_field() class Meta:
model = Goods
fields = ["id","name","price","image","remark","credit"]
sql_session = db.session @post_dump()
def mobile_format(self, data, **kwargs):
data["price"] = "%.2f" % data["price"]
if data["image"] == None:
data["image"] = ""
return data

5.在商城界面根据金额/积分显示不同商品

在客户端中, 根据金额或者积分来显示不同商品.shop.html代码:

<!DOCTYPE html>
<html>
<head>
<title>商店</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
</head>
<body>
<div class="app frame avatar update_nickname add_friend shop" id="app">
<div class="box">
<p class="title">商店</p>
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="friends_list shop_list">
<div class="item" @click="buy_prop(goods.id)" v-for="goods in goods_list">
<div class="avatar shop_item">
<img :src="settings.static_url+goods.image" alt="">
</div>
<div class="info">
<p class="username">{{goods.name}}</p>
<p class="time">{{goods.remark}}</p>
</div>
<div class="status">
<span v-if="goods.price>0">{{goods.price}}</span>
<span v-else>{{goods.credit}}个果子</span>
</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
user_id: "", // 当前登陆用户Id
goods_list:[], // 商品列表
page: 1,
limit: 10,
is_send_ajax:false,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"shop.html",params:{}},
}
},
created(){
this.user_id = this.game.get("id") || this.game.fget("id");
this.get_goods_list();
},
methods:{
close_frame(){
this.game.outFrame("orchard_shop");
},
get_goods_list(){
if(this.is_send_ajax){
return ;
}
// 通过请求获取当前用户的好友列表
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.is_send_ajax = true;
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Orchard.goods.list",
"params": {
"page": this.page,
"limit": this.limit,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
if(this.page+1 == response.data.result.pages){
this.is_send_ajax = true;
}else{
this.is_send_ajax = false;
this.page+=1;
}
if(this.page>1){
api.refreshHeaderLoadDone();
}
this.goods_list = response.data.result.goods_list.concat(this.goods_list);
}else if(parseInt(response.data.result.errno) == 1008){
this.friends = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
buy_prop(prop_id){
// 购买商品道具
// 让用户选择购买的数量
api.prompt({
text: 1,
title:"请输入购买数量",
type: "number",
buttons: ['确定', '取消']
}, (ret, err)=>{
if(ret.buttonIndex == 1){
// 通过通知告知socket进行商品购买
api.sendEvent({
name: 'buy_prop',
extra: {
"pid": prop_id,
"num": ret.text
}
});
}
});
}
}
});
}
</script>
</body>
</html>

在商城页面根据金额/积分显示不同商品

在购买商品成功以后, 计算金额和积分的扣除, socket.py代码:

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting,db
from status import APIStatus as status
from message import ErrorMessage as errmsg
# 建立socket通信
# @socketio.on("connect", namespace="/mofang")
# def user_connect():
# """用户连接"""
# print("用户%s连接过来了!" % request.sid)
# # 主动响应数据给客户端
# socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang")
def user_login(data):
# 分配房间
room = data["uid"]
join_room(room)
# 保存当前用户和sid的绑定关系
# 判断当前用户是否在mongo中有记录
query = {
"_id": data["uid"]
}
ret = mongo.db.user_info_list.find_one(query)
if ret:
mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
else:
mongo.db.user_info_list.insert_one({
"_id": data["uid"],
"sid": request.sid,
}) # 返回种植园的相关配置参数
orchard_settings = {}
setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
"""
现在的格式:
[<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
需要返回的格式:
{
package_number_base:4,
package_number_max: 32,
...
}
"""
for item in setting_list:
orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数
user_settings = {}
# 从mongo中查找用户信息,判断用户是否激活了背包格子
dict = mongo.db.user_info_list.find_one({"sid":request.sid})
# 背包格子
if dict.get("package_number") is None:
user_settings["package_number"] = orchard_settings.get("package_number_base",4)
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
else:
user_settings["package_number"] = dict.get("package_number") socketio.emit("login_response", {
"errno":status.CODE_OK,
"errmsg":errmsg.ok,
"orchard_settings":orchard_settings,
"user_settings":user_settings
}, namespace="/mofang", room=room) @socketio.on("user_buy_prop", namespace="/mofang")
def user_buy_prop(data):
"""用户购买道具"""
room = request.sid
# 从mongo中获取当前用户信息
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return # 判断背包物品存储是否达到上限
use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
# 本次购买道具需要使用的格子数量
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) # 计算购买道具以后需要额外占用的格子数量
if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
"""曾经购买过当前道具"""
prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
old_td_num = prop_num // td_prop_max
if prop_num % td_prop_max > 0:
old_td_num+=1
new_td_num = new_prop_num // td_prop_max
if new_prop_num % td_prop_max > 0:
new_td_num+=1
td_num = new_td_num - old_td_num
else:
"""新增购买的道具"""
# 计算本次购买道具需要占用的格子数量 if int(data["num"]) > td_prop_max:
"""需要多个格子"""
td_num = int(data["num"]) // td_prop_max
if int(data["num"]) % td_prop_max > 0:
td_num+=1
else:
"""需要一个格子"""
td_num = 1 if use_package_number+td_num > package_number:
"""超出存储上限"""
socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
namespace="/mofang", room=room)
return # 从mysql中获取商品价格
prop = Goods.query.get(data["pid"])
if user.money > 0: # 当前商品需要通过RMB购买
if float(user.money) < float(prop.price) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
return
else:
"""当前通过果子进行购买"""
if int(user.credit) < int(prop.credit) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
namespace="/mofang", room=room)
return # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
query = {"sid": request.sid}
if user_info.get("prop_list") is None:
"""此前没有购买任何道具"""
message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
mongo.db.user_info_list.update_one(query,message)
else:
"""此前有购买了道具"""
prop_list = user_info.get("prop_list") # 道具列表
if ("prop_%s" % prop.id) in prop_list:
"""如果再次同一款道具"""
prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
else:
"""此前没有购买过这种道具"""
prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 扣除余额或果子
if prop.price > 0:
user.money = float(user.money) - float(prop.price) * int(data["num"])
else:
user.credit = int(user.credit) - int(prop.credit) * int(data["num"]) db.session.commit() # 返回购买成功的信息
socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
# 返回最新的用户道具列表
user_prop() @socketio.on("user_prop", namespace="/mofang")
def user_prop():
"""用户道具"""
userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
prop_list = userinfo.get("prop_list",{})
prop_id_list = []
for prop_str,num in prop_list.items():
pid = int(prop_str[5:])
prop_id_list.append(pid) data = []
prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) for prop_data in prop_list_data:
num = int( prop_list[("prop_%s" % prop_data.id)])
if td_prop_max > num:
data.append({
"num": num,
"image": prop_data.image,
"pid": prop_data.id
})
else:
padding_time = num // td_prop_max
padding_last = num % td_prop_max
arr = [{
"num": td_prop_max,
"image": prop_data.image,
"pid": prop_data.id
}] * padding_time
if padding_last != 0:
arr.append({
"num": padding_last,
"image": prop_data.image,
"pid": prop_data.id
})
data = data + arr
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
room = request.sid
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
}, namespace="/mofang",
room=room)

购买商品成功后,计算金额和积分的扣除

6.背包解锁

orchard/socket.py, 代码:

@socketio.on("unlock_package", namespace="/mofang")
def unlock_package():
"""解锁背包"""
# 从mongo获取当前用户解锁的格子数量
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return package_number = int(user_info.get("package_number"))
num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格
setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
if setting is None:
unlock_price = 0
else:
unlock_price = int(setting.value) # 判断是否有足够的积分或者价格
room = request.sid
if user.money < unlock_price:
socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
namespace="/mofang", room=room)
return # 解锁成功
user.money = float(user.money) - float(unlock_price)
db.session.commit() # mongo中调整数量
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}}) # 返回解锁的结果
socketio.emit("unlock_package_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok},
namespace="/mofang", room=room)

orchard.html ,代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
this.socket.emit("user_prop");
});
},
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings
});
});
},
user_package(){
// 用户背包道具列表
this.socket.on("user_prop_response",(message)=>{
this.game.fsave({
"user_package":message.data,
})
})
},
go_index(){
this.game.goWin("root");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
go_my_package(){
// 我的背包
this.game.goFrame("package","package.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
buy_prop(){
api.addEventListener({
name: 'buy_prop'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("user_buy_prop",ret.value);
}
});
this.socket.on("user_buy_prop_response",(message)=>{
alert(message.errmsg);
})
},
unlock_package_number(){
api.addEventListener({
name: 'unlock_package_number'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("unlock_package");
}
}); this.socket.on("unlock_package_response",(message)=>{
if(parseInt(message.errno) === 1000){
api.sendEvent({
name: 'unlock_package_success',
extra: {
}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
}); } })
}
}
});
}
</script>
</body>
</html>

背包解锁:orchard.html

package.html ,代码:

<!DOCTYPE html>
<html>
<head>
<title>我的背包</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
</head>
<body>
<div class="app frame avatar add_friend package" id="app">
<div class="box">
<p class="title">我的背包</p>
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="prop_list">
<div class="item" v-for="prop in user_package" @click="use_prop(prop.pid)">
<img :src="settings.static_url+prop.image" alt="">
<span>{{prop.num}}</span>
</div>
<div class="item" v-for="number in unlock_td_number"></div>
<div class="item lock" @click="unlock_package()" v-for="number in lock_td_number"></div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
td: 36, // 背包格子总数量
user_id: "", // 当前登陆用户Id
orchard_settings:{}, // 种植园相关公共参数
user_settings:{}, // 用户相关私有参数
user_package:[], // 用户背包信息
prev:{name:"",url:"",params:{}},
current:{name:"package",url:"package.html",params:{}},
}
},
computed:{// 计算属性
lock_td_number(){
// 未解锁的格子
return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number);
},
unlock_td_number(){
// 解锁的格子
return parseInt( this.user_settings.package_number - this.user_package.length);
}
},
created(){
this.user_id = this.game.get("id") || this.game.fget("id");
this.orchard_settings = JSON.parse(this.game.fget("orchard_settings"));
this.user_settings = JSON.parse(this.game.fget("user_settings"));
this.user_package = JSON.parse(this.game.fget("user_package"));
},
methods:{
use_prop(pid){
// 发起使用道具的通知
},
unlock_package(){
// 解锁格子上限
api.confirm({
title: '提示',
msg: '解锁背包上限',
buttons: ['确定', '取消']
}, (ret, err)=>{
if( ret.buttonIndex == 1 ){
api.sendEvent({
name: 'unlock_package_number',
extra: {}
}); api.addEventListener({
name: 'unlock_package_success'
}, (ret, err)=>{
this.user_settings.package_number+=1;
}); }
}); },
close_frame(){
this.game.outFrame("package");
},
}
});
}
</script>
</body>
</html>

背包解锁:package.html

day116:MoFang:显示背包解锁/未解锁格子数&显示背包的道具物品&背包解锁的更多相关文章

  1. 一种读取Exchange的用户未读邮件数方法!

    已好几个月没写博客了,由于之前忙于开发基于Sharepoint上的移动OA(AgilePoint)和采用混合移动开发技术开发一个安卓版的企业通讯录APP(数据与lync一致),并于1月初正式上线.马年 ...

  2. Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒

    http://www.51itong.net/android-badgenumber-9789.html Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒 ...

  3. 关于win7右下角显示“音频服务未运行”的解决方法

    今天打开电脑发现右下角的的小喇叭多了个叉叉,显示“音频服务未运行”,百度了一下,解决方法还是挺多的,一下是百度到的解决方法,希望可以帮到出现这个问题的朋友们. 解决方法:(转载的) 1.Windows ...

  4. js图片未加载完显示loading效果

    <html> <title>js图片未加载完显示loading效果</title> <body> <style> img{float:lef ...

  5. ToastUtil【简单的Toast封装类】【未自定义Toast的显示风格】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 一个简单的Toast封装类. 效果图 API = 6.0 API = 4.4.2 代码分析 实现了不管我们触发多少次Toast调用, ...

  6. 解决使用 Eruda 绑定 dom 未在指定位置显示问题

    前言 开发项目中,使用到 Eruda 打印控制台信息显示 文档:https://github.com/liriliri/eruda 安装 Eruda npm install eruda --save ...

  7. 《连载 | 物联网框架ServerSuperIO教程》- 13.自定义视图显示接口开发,满足不同的显示需求

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  8. Java基础-多线程编程-1.随便选择两个城市作为预选旅游目标。实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市。分别用Runnable接口和Thread类实现。

    1.随便选择两个城市作为预选旅游目标.实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市.分别用Runnable接口和Thread ...

  9. 一个页面中显示多个button时总行数计算公式。

    总行数 = (按钮总数 + 每一行按钮数 - 1) / 每一行按钮数. 同理.假设我们要显示一定总数的item.每页固定数量,则总页数为. 总页数 = (总显示数量 + 每页显示的数量 - 1) / ...

随机推荐

  1. Win10激活失败并提示错误代码0xC004C003的解决方法

    亲测,可用. 步骤如下: 进入cmd,管理员权限登录 slmgr.vbs /upk slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX slmgr /skms zh.us ...

  2. filebeat输出结果到elasticsearch的多个索引

    基本环境: filebeat版本:6.5.4 (Linux,x86-64) elasticsearch版本:6.54   (一)需求说明 在一台服务器上有多个日志需要使用filebeat日志收集到el ...

  3. 深入理解Java虚拟机(四)——HotSpot垃圾收集器详解

    垃圾收集器 新生代收集器 1.Serial收集器 特点: 单线程工作,收集的时候就会停止其他所有工作线程,用户不可知不可控,会使得用户界面出现停顿. 简单高效,是所有收集器中额外内存消耗最少的. 没有 ...

  4. EditPlus各个版本的注册码,亲测可用

    原文链接:https://www.cnblogs.com/shihaiming/p/6422441.html EditPlus4注册码 注册名:host1991    序列号:14F50-CD5C8- ...

  5. 主从复制架构直接转换MGR(manual)

    环境信息 IP port role info 192.168.188.81 3316 node1 master 192.168.188.82 3316 node2 slave1 192.168.188 ...

  6. 八、TestNG忽略测试

    一个TestNG  测试类中如果有的方法不想测试可以使用 enabled 属性 enabled = false  该方法不参与测试 enabled = true  该方法参与测试 @Test 不写en ...

  7. vue第十六单元(element-ui vue-lazyload 等常用插件)

    第十六单元(element-ui vue-lazyload 等常用插件) #课程目标 1.掌握插件的引入方式 2.精通UI框架 3.掌握前端常见的几种效果实现 #知识点 一.elementUI的使用 ...

  8. 移动端 rem和flexible

    一.rem布局 rem是相对于根元素的字体大小单位. 假设html的字体大小为16px,那么1rem = 16px; 一旦根元素html定义的font-size变化,整个页面中运用到的rem都会随之变 ...

  9. angualr8 循环对象

    <div *ngFor="let item of object | keyvalue"> {{item.key}}:{{item.value}} </div> ...

  10. Centos8自动挂载U盘移动硬盘解决办法

    Centos默认是不能识别NTFS文件系统的U盘.移动硬盘的.查阅了很多资料讲到的都是需要安装ntfs-3g安装包. 安装完后每次插入移动存储介质时,都需要手动去挂载. 作为一个做技术的,如果不能解决 ...