种植园

一、背包显示道具

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

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

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

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

数据迁移, 终端下执行:

python manage.py db migrate -m "add orchard setting table"
python manage.py db upgrade

添加测试数据,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');

服务端在用户登陆种植园得到时候需要返回种植园的公共参数以及用户的私有参数,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('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# 从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)

客户端显示格子,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" @click='go_friends'>好友</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: {},  // 用户私有相关参数},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=>{// this.game.print(response.data);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();});},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>

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: "", // 当前登陆用户Idorchard_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>

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

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

orchard/socket.py ,代码:

...
@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 = 10else: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_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif padding_last != 0:arr.append({'num': padding_last,'image': prop_data.image,'pid': prop_data.id})data = data + arrroom = request.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, 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" @click='go_friends'>好友</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=>{// this.game.print(response.data);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>

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='user_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: "", // 当前登陆用户Idorchard_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:{user_prop(pid){// 发起使用道具的通知},close_frame(){this.game.outFrame("package");},}});}</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
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# 从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 = 10else: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_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif padding_last != 0:arr.append({'num': padding_last,'image': prop_data.image,'pid': prop_data.id})data = data + arrroom = request.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, namespace='/mofang', room=room)

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

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('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 = 10else: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['pdi']])  # 购买前的道具数量new_prop_num = prop_num + int(data['num'])  # 如果成功购买道具以后的数量old_td_num = prop_num // td_prop_maxif prop_num % td_prop_max > 0:old_td_num += 1new_td_num = new_prop_num // td_prop_maxif new_prop_num % td_prop_max > 0:new_td_num += 1td_num = new_td_num - old_td_numelse:"""新增购买的道具"""# 计算本次购买道具需要占用的格子数量if int(data['num']) > td_prop_max:"""需要多个格子"""td_num = int(data['num']) // td_prop_maxif int(data['num']) % td_prop_max > 0:td_num += 1else:"""需要一个格子"""td_num = 1if 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 = 10else: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_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif 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.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, namespace='/mofang', room=room)

status.py代码:

 ...CODE_NO_PACKAGE = 1015  # 背包存储达到上限CODE_NO_CREDIT = 1016  # 果子不足

message.py代码:

 ...no_package = "背包存储达到上限!"credit_no_enough = "果子不足!"

在购买道具的流程中, 增加积分(果子)商品.
改变数据库:

`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 = Goodsfields = ['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

在客户端中, 根据金额或者积分来显示不同商品.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: "", // 当前登陆用户Idgoods_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 = 10else: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_maxif prop_num % td_prop_max > 0:old_td_num += 1new_td_num = new_prop_num // td_prop_maxif new_prop_num % td_prop_max > 0:new_td_num += 1td_num = new_td_num - old_td_numelse:"""新增购买的道具"""# 计算本次购买道具需要占用的格子数量if int(data['num']) > td_prop_max:"""需要多个格子"""td_num = int(data['num']) // td_prop_maxif int(data['num']) % td_prop_max > 0:td_num += 1else:"""需要一个格子"""td_num = 1if 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)returnelse:"""当前通过果子进行购买"""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 = 10else: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_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif 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.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, namespace='/mofang', room=room)

二、背包解锁

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, 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 = 10else: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_maxif prop_num % td_prop_max > 0:old_td_num += 1new_td_num = new_prop_num // td_prop_maxif new_prop_num % td_prop_max > 0:new_td_num += 1td_num = new_td_num - old_td_numelse:"""新增购买的道具"""# 计算本次购买道具需要占用的格子数量if int(data['num']) > td_prop_max:"""需要多个格子"""td_num = int(data['num']) // td_prop_maxif int(data['num']) % td_prop_max > 0:td_num += 1else:"""需要一个格子"""td_num = 1if 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)returnelse:"""当前通过果子进行购买"""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 = 10else: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_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif 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.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, namespace='/mofang', room=room)@socketio.on('unlock_package', namespace='/mofang')
def unlock_package():"""解锁背包"""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('unlock_package_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)returnpackage_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 = 0else:unlock_price = int(setting.value)# 判断是否有足够的积分或者价格room = request.sidif 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" @click='go_friends'>好友</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=>{// this.game.print(response.data);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>

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: "", // 当前登陆用户Idorchard_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>

关闭数据库的提示信息dev.py

 ...SQLALCHEMY_ECHO = False

魔坊APP项目-20-种植园,背包显示道具、用户购买道具的时候,判断背包存储是否达到上限、背包解锁相关推荐

  1. 魔坊APP项目-28-直播、显示房间列表

    直播 显示房间列表 把上面的客户端live_list.html,修改成live.html,并新建live_list.html,代码: <!DOCTYPE html> <html> ...

  2. 魔坊APP项目-19-种植园,我的背包、道具购买

    种植园 一.我的背包 打开背包,orchard.html,代码: <!DOCTYPE html> <html> <head><title>用户中心< ...

  3. 魔坊APP项目-25-种植园,植物的状态改动、当果树种植以后在celery的异步任务中调整浇水的状态、客户端通过倒计时判断时间,显示浇水道具

    种植园 植物的状态改动 一.当果树种植以后在celery的异步任务中调整浇水的状态 在进行果树种植的时候, 在服务端设置当前果树到等待浇水的redis变量中.通过celery不断进行周期任务的处理, ...

  4. 魔坊APP项目-22-种植园,种植栏的功能实现,客户端根据激活状态和未激活状态分别显示树桩、服务端提供种植植物的相关数据、解锁树桩、植物相关道具使用

    种植园 一.种植栏的功能实现 1. 客户端需要的植物相关参数: 总树桩数量, 当前用户激活树桩数量, 当前种植的树桩数量, 树桩列表状态 2. 客户端根据激活状态和未激活状态分别显示树桩 3. 服务端 ...

  5. 魔坊APP项目-21-种植园,宠物栏的功能实现、服务端提供显示宠物的api接口、客户端中展示宠物栏和宠物列表以及饱食度、宠物道具的使用

    种植园 一.宠物栏的功能实现 1. 宠物的显示 2. 宠物的使用 3. 宠物的饱食度 4. 宠物的开锁 1.服务端提供显示宠物的api接口 socket.py,代码 ... import math f ...

  6. 魔坊APP项目-23-种植园,宠物和种植物的状态改变、宠物的状态改动

    种植园 宠物和种植物的状态改变 1. 宠物的状态改动 2. 种植物的状态改动 3. 道具的使用 宠物的状态改动 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们提前在mysql中进行配置 ...

  7. 魔坊APP项目-24-种植园,修复宠物喂食时出现的饱食度没有增加的bug、宠物挂了的bug问题

    种植园 修复宠物喂食时出现的饱食度没有增加的bug 在feed方法中监听是否喂食成功的pet_feed_success通知中, 保存更新后的hp_time. my_orchard.html代码: &l ...

  8. 魔坊APP项目-16-种植园、websocket协议、服务端基于socket提供服务(基于房间管理分发信息)、种植园页面展示

    种植园 我们需要完成的种植园,是一个互动频繁,并且要求有一定即时性的模块,所以如果继续基于http协议开发,那么需要通过ajax发送大量http请求,同时因为http本身属于单向通讯,所以服务端无法主 ...

  9. 魔坊APP项目-17-种植园,商城页面、服务端提供商品api,解决App打包编译以后的跨域限制、客户端获取商品列表并进行展示,集成Alipayplus模块完成支付

    种植园 一.商城页面 orchard.html,代码: <!DOCTYPE html> <html> <head><title>用户中心</tit ...

最新文章

  1. NSOperation下载网络图片(四)
  2. Leetcode PHP题解--D25 500. Keyboard Row
  3. 红色警报 (25 分)【测试点分析】【两种解法】
  4. spring 第一天:1015
  5. C++/Java线程之分
  6. Java解决空引用_在java中检查空引用的乐观方法
  7. 【共享内存】基于共享内存的无锁消息队列设计
  8. 小型数控雕刻机制作Arduino_微型CNC制作基于开源项目GRBL
  9. 如何选择物业管理软件,五大误区需要避免
  10. RapidMiner教程
  11. java前端框架有哪些_web前端框架有哪些
  12. 计算机无法识别1136打印机,惠普HP M1136打印机驱动安装失败的多种解决办法
  13. SPSS(十五)spss之聚类分析(图文+数据集)
  14. sumifs多条件求和步骤?如何运用sumifs函数进行求和
  15. java麻将算法_Java实现的麻将胡牌算法
  16. python下载CVF论文
  17. 构建一阶谓词逻辑和有限域上多项式方程的同构
  18. XBox One 升级后显示黑屏
  19. bingo培训——软件设计
  20. 拜水野三坡 求得一身财气

热门文章

  1. 4、CSS立体盒子动画——复仇者联盟
  2. 实习时候的亚子==(一)
  3. 8月5日邮件服务器故障报告(2008年)
  4. 【 CF1186D,E,F】Vus the Cossack and Numbers/Vus the Cossack and a Field/Vus the Cossack and a Graph
  5. 002 计算bonus
  6. JavaRIM实现(PRC的其中一种方案)
  7. C++基础笔记(一)
  8. 大反转!温莎大师实战大健康,不一样的趋势,架构讲解
  9. 广告词 android,o泡果奶软件-o泡果奶广告词apk预约 v1.0_5577安卓网
  10. 一次郁闷的58同城经历