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

种植园

植物的状态改动

一、当果树种植以后在celery的异步任务中调整浇水的状态

在进行果树种植的时候, 在服务端设置当前果树到等待浇水的redis变量中.通过celery不断进行周期任务的处理, 改动果树的浇水状态
socket.py

...
from datetime import datetime
@socketio.on('use_prop', namespace='/mofang')
def use_prop(data):
    """使用道具"""
    pid = data.get('pid')
    pet_key = data.get('pet_key', 0)
    room = request.sid
    # 获取mongo中的用户信息
    user_info = mongo.db.user_info_list.find_one({'sid': request.sid})
    # 获取mysql中的用户信息
    user = User.query.get(user_info.get('_id'))
    if user is None:
        socketio.emit('pet_use_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)
        return

    # 获取道具
    prop_data = Goods.query.get(pid)
    if prop_data is None:
        socketio.emit('pet_use_response', {'errno': status.CODE_NO_SUCH_PROP, 'errmsg': errmsg.not_such_prop}, namespace='/mofang', room=room)
        return

    if int(prop_data.prop_type) == 0:
        """使用植物道具"""
        # 1.判断当前的植物数量是否有空余
        tree_list = user_info.get('user_tree_list', [])

        # 当前用户最多可种植的数量
        setting = Setting.query.filter(Setting.name == 'user_active_tree').first()
        if setting is None:
            user_tree_number = 3
        else:
            user_tree_number = int(setting.value)
        user_tree_number = user_info.get('user_tree_number', user_tree_number)

        if len(tree_list) >= user_tree_number:
            socketio.emit('prop_use_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.prop_not_empty}, namespace='/mofang', room=room)
            return

        # 使用道具
        mongo.db.user_info_list.update_one({'sid': room}, {'$push': {'user_tree_list':
            {  # 植物状态
                "time": int(datetime.now().timestamp()),  # 种植时间
                "status": 2,  # 植物状态,2表示幼苗状态
                'allow_water': False,  # 是否允许浇水
                "waters": 0,  # 浇水次数
                'shears': 0,  # 使用剪刀次数
            }
        }})

        # 从种下去到浇水的时间
        pipe = redis.pipeline()
        pipe.multi()
        setting = Setting.query.filter(Setting.name == 'tree_water_time').first()
        if setting is None:
            tree_water_time = 3600
        else:
            tree_water_time = int(setting.value)
        # 必须等时间到了才可以浇水
        pipe.setex('user_tree_water_%s_%s' % (user.id, len(tree_list)), int(tree_water_time), '_')
        # 必须等时间到了才可以到成长期
        setting = Setting.query.filter(Setting.name == 'tree_growup_time').first()
        if setting is None:
            tree_growup_time = 3600
        else:
            tree_growup_time = int(setting.value)
        pipe.setex('user_tree_growup_%s_%s' % (user.id, len(tree_list)), tree_growup_time, '_')
        pipe.execute()
        
        # 设置定时任务进行浇水
        redis.append('tree_list_water', '%s_%s,' % (user.id, len(tree_list)))
        user_login({'uid': user.id})

    if int(prop_data.prop_type) == 1:
        """使用宠物道具"""
        # 1. 判断当前的宠物数量
        # 获取宠物列表
        pet_list = user_info.get('pet_list', [])
        if len(pet_list) > 1 and pet_list[0]['is_die'] == 0 and pet_list[1]['is_die'] == 0:
            socketio.emit('pet_use_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.prop_not_empty}, namespace='/mofang', room=room)
            return

        # 2. 是否有空余的宠物栏位
        pet_number = user_info.get('pet_number', 1)
        length = len(pet_list)

        if length == 2:
            live_leng = 0
            if pet_list[0]['is_die'] == 0:
                live_leng += 1
            if pet_list[1]['is_die'] == 0:
                live_leng += 1
        elif length == 1 and pet_list[0]['is_die'] == 0:
            live_leng = 1
        else:
            live_leng = 0
        if live_leng >= pet_number:
            socketio.emit('pet_use_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.prop_not_empty}, namespace='/mofang', room=room)
            return

        # 3. 初始化当前宠物信息
        # 获取有效期和防御值
        exp_data = Setting.query.filter(Setting.name == 'pet_expire_%s' % pid).first()
        ski_data = Setting.query.filter(Setting.name == 'pet_skill_%s' % pid).first()

        if exp_data is None:
            # 默认7天有效期
            expire = 7
        else:
            expire = exp_data.value

        if ski_data is None:
            skill = 10
        else:
            skill = ski_data.value

        # 在redis中设置当前宠物的饱食度
        pipe = redis.pipeline()
        pipe.multi()
        setting = Setting.query.filter(Setting.name == ('pet_hp_max_%s' % pid)).first()
        if setting is None:
            pet_hp_max = 7200
        else:
            pet_hp_max = int(setting.value)

        # 保存到mongo
        # 判断是否有挂了的宠物在列表中
        pet_data = {
            'pid': pid,
            'image': prop_data.image,
            'created_time': int(datetime.now().timestamp()),
            'skill': skill,
            'is_die': 0,
            }

        # 如果第一个宠物是挂了的
        if len(pet_list) == 0:
            mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': [pet_data]}})

            pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')
            pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')
        elif len(pet_list) == 1 and int(pet_list[0]['is_die']) == 1:
            """只有一个挂了的宠物"""
            mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': [pet_data]}})

            pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')
            pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')

        elif len(pet_list) == 1 and int(pet_list[0]['is_die']) == 0:
            """只有一个活着的宠物"""
            mongo.db.user_info_list.update_one({'sid': room}, {'$push': {'pet_list': pet_data}})
            pipe.setex('pet_%s_%s_hp' % (user.id, 2), pet_hp_max, '_')
            pipe.setex('pet_%s_%s_expire' % (user.id, 2), int(expire) * 24 * 60 * 60, '_')
        elif len(pet_list) == 2 and int(pet_list[0]['is_die']) == 1:
            """有2个宠物,但是第1个挂了"""
            pet_list[0] = pet_data
            mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': pet_list}})
            pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')
            pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')
        elif len(pet_list) == 2 and int(pet_list[1]['is_die']) == 1:
            """有2个宠物,但是第2个挂了"""
            pet_list[1] = pet_data
            pipe.setex('pet_%s_%s_hp' % (user.id, 2), pet_hp_max, '_')
            pipe.setex('pet_%s_%s_expire' % (user.id, 2), int(expire) * 24 * 60 * 60, '_')
            mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': pet_list}})

        pipe.execute()
        pet_show()

    if int(prop_data.prop_type) == 3:
        """宠物喂食"""
        pet_list = user_info.get('pet_list')
        if len(pet_list) < 1:
            socketio.emit('pet_use_response', {'errno': status.CODE_NO_PET, 'errmsg': errmsg.not_pet}, namespace='/mofang', room=room)
            return

        current_hp_time = redis.ttl('pet_%s_%s_hp' % (user.id, pet_key + 1))
        setting = Setting.query.filter(Setting.name == ('pet_hp_max_%s' % (pet_list[pet_key]['pid']))).first()
        if setting is None:
            pet_hp_max = 7200
        else:
            pet_hp_max = int(setting.value)

        current_pet_hp = math.ceil(current_hp_time / pet_hp_max * 100)
        if current_pet_hp > 90:
            """饱食度高于90%无法喂养"""
            socketio.emit('pet_use_response', {'errno': status.CODE_NO_FEED, 'errmsg': errmsg.no_feed}, namespace='/mofang', room=room)
            return

        if current_pet_hp <= 0:
            socketio.emit('pet_use_response', {'errno': status.CODE_NO_PET, 'errmsg': errmsg.not_pet}, namespace='/mofang', room=room)
            return
        setting = Setting.query.filter(Setting.name == 'pet_feed_number').first()
        if setting is None:
            pet_feed_number = 0.1
        else:
            pet_feed_number = float(setting.value)
        prop_time = pet_hp_max * pet_feed_number
        time = int(current_hp_time + prop_time)
        redis.expire('pet_%s_%s_hp' % (user.id, pet_key + 1), time)
        socketio.emit('pet_feed_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok, 'pet_key': pet_key, 'hp_time': time}, namespace='/mofang', room=room)

    # 扣除背包中的道具数量
    prop_list = user_info.get('prop_list', {})
    for key, value in prop_list.items():
        if key == ('prop_%s' % pid):
            if int(value) > 1:
                prop_list[key] = int(value) - 1
            else:
                prop_list.pop(key)
            break

    mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'prop_list': prop_list}})
    user_prop()

    socketio.emit('prop_use_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok}, namespace='/mofang', room=room)

@socketio.on('active_tree', namespace='/mofang')
def active_tree():
    """激活树桩"""
    room = request.sid
    # 获取mongo中的用户信息
    user_info = mongo.db.user_info_list.find_one({'sid': request.sid})
    # 获取mysql中的用户信息
    user = User.query.get(user_info.get('_id'))
    if user is None:
        socketio.emit('active_tree_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)
        return

    # 判断树桩是否达到上限
    tree_number_data = Setting.query.filter(Setting.name == 'user_active_tree').first()
    total_tree_data = Setting.query.filter(Setting.name == 'user_total_tree').first()

    if tree_number_data is None:
        tree_number = 1
    else:
        tree_number = tree_number_data.value

    if total_tree_data is None:
        total_tree = 9
    else:
        total_tree = int(total_tree_data.value)

    user_tree_number = int(user_info.get('user_tree_number', tree_number))
    if user_tree_number >= total_tree:
        socketio.emit('active_tree_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.prop_not_empty}, namespace='/mofang', room=room)
        return

    # 扣除激活的果子数量
    ret = Setting.query.filter(Setting.name == 'active_tree_price').first()
    if ret is None:
        active_tree_price = 100 * user_tree_number
    else:
        active_tree_price = int(ret.value) * user_tree_number

    if active_tree_price > int(user.credit):
        socketio.emit('active_tree_response', {'errno': status.CODE_NO_CREDIT, 'errmsg': errmsg.credit_no_enough}, namespace='/mofang', room=room)
        return

    user.credit = int(user.credit) - active_tree_price
    db.session.commit()

    mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'user_tree_number': user_tree_number+1}})

    socketio.emit('active_tree_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok}, namespace='/mofang', room=room)
    return

mycelery/tree/tasks.py代码:


from application import redis
from mycelery.main import app, flask_app
from application import mongo

@app.task(name='tree_write', bind=True)
def tree_write(self):
    """允许浇水"""
    try:
        tree_list_water = redis.get('tree_list_water')
        if tree_list_water is None:
            return 
        tree_list_water = tree_list_water.decode()
        tree_list = tree_list_water.split(',')[:-1]
        for tree in tree_list:
            timer = redis.ttl('user_tree_water_%s' % tree)
            if timer == -2:
                print('%s可以浇水了' % tree)
                treeinfo = tree.split('_')
                query = {'_id': treeinfo[0]}
                user_info = mongo.db.user_info_list.find_one(query)
                user_tree_list = user_info.get('user_tree_list', [])
                user_tree_list[int(treeinfo[1])]['allow_water'] = True
                mongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})
                tree_list_water_str = ''.join(tree_list_water.split(tree+','))
                redis.set('tree_list_water', tree_list_water_str)
    except Exception as exc:
        print(exc)
                              

mycelery/main.py,代码:

from celery import Celery
from application import init_app
# 初始化celery对象
app = Celery("flask")

# 初始化flask
flask_app = init_app("application.settings.dev").app
# 加载配置
app.config_from_object("mycelery.config")
# 自动注册任务
app.autodiscover_tasks(["mycelery.sms", 'mycelery.tree'])


# 运行celery
# 主程序终端下启动: celery -A mycelery.main worker -l info
# 调度器终端下启动: celery -A mycelery.main beat

mycelery/config.py,代码:

# 任务队列地址
broker_url = 'redis://127.0.0.1:6379/15'
# 结果队列地址
result_backend = "redis://127.0.0.1:6379/14"

# 周期任务
from mycelery.main import app
app.conf.beat_schedule = {
    'tree_write_task': {
        'task': 'tree_write',
        'schedule': 5,
    },
}

接下来在终端下运行celery

celery -A mycelery.main worker -l info
celery -A mycelery.main beat
二、客户端通过倒计时判断时间,显示浇水道具

客户端显示浇水图标, main.css,代码:

...
.tree-popped{
  top: 4rem;
  right: 3rem;
  height: 3.4rem;
  width: 3.4rem;
  border-radius: 3.4rem;
}
.popped:after {
  content: '';
  position: absolute;
  top: 18%;
  left: 18%;
  background-color: rgba(191, 255, 255, 0.6);
  width: 1.2rem;
  height: 1.5rem;
  border-radius: 50%;
  transform: rotate(45deg) scale(0.8);
}
.popped img{
  width: 3rem!important;
  height: 3rem!important;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}
.tree-popped img{
  width: 2.2rem!important;
  height: 2.2rem!important;
}
.orchard-frame .pet-box .turned_off .turned_image{
  width: 5.14rem;
  height: 6.83rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}
.orchard-frame .pet-box .turned_off p{
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  border: 1px solid #fff;
  border-radius: 1rem;
  width: 8rem;
  height: 3rem;
  line-height: 3rem;
  font-size: 1.5rem;
  word-wrap: break-word;
  padding: 1rem;
  color: #000;
  text-align: center;
  background: rgba(255,255,255,.6);
}
.orchard-frame .pet-box .pet-item{
  width: 10rem;
  height: 10rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}
.orchard-frame .tree-list{
  position: absolute;
  top: 9rem;
  width: 100%;
}
.orchard-frame .tree-box{
  margin-left: 3rem;
  margin-right: 3rem;
}
.orchard-frame .tree-box .tree{
  width: 9rem;
  height: 4rem;
  margin-bottom: 2rem;
  float: left;
  position: relative;
}
...

my_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 orchard-frame" id="app">
    <div class="background">
      <img class="grassland2" src="../static/images/grassland2.png" alt="">
      <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
      <img class="stake1" src="../static/images/stake1.png" alt="">
      <img class="stake2" src="../static/images/stake2.png" alt="">
    </div>
    <div class="pet-box">
      <div class="pet">
				<span @click='feed(0)' class="popped" v-if='pet_list[0] && pet_list[0].hp<90 && pet_list[0].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
        <img v-if='pet_list.length > 0 && pet_list[0].hp>0' class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
      </div>
			<div class="pet" v-if='pet_number > 1'>
				<span @click='feed(1)' class="popped" v-if='pet_list[1] && pet_list[1].hp<90 && pet_list[1].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
				<img v-if='pet_list.length > 1 && pet_list[1].hp>0' class='pet-item' :src="settings.static_url+pet_list[1].image" alt="">
			</div>
      <div class="pet turned_off" v-if='pet_number == 1'>
        <img class="turned_image" src="../static/images/turned_off.png" alt="">
        <p>请购买宠物</p>
      </div>
    </div>
    <div class="tree-list">
      <div class="tree-box">
				<div class="tree" v-for='tree in user_tree_data.user_tree_list'>
					<span class="popped tree-popped">
						<img src="../static/images/prop3.png" alt="">
					</span>
					<img :src="tree_img(tree.status)" alt="">
				</div>
				<!-- 已激活但是未种植的树桩列表 -->
        <div class="tree" v-for='i in active_tree'>
          <img src="../static/images/tree1.png" alt="">
        </div>
				<!-- 未激活树桩列表 -->
        <div @click='unlock_tree' class="tree" v-for='i in lock_tree'>
          <img src="../static/images/tree0.png" alt="">
        </div>
      </div>
    </div>
    <div class="prop-list">
      <div class="prop">
        <img src="../static/images/prop1.png" alt="">
        <span>{{fertilizer_num}}</span>
        <p>化肥</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop2.png" alt="">
        <span>{{shears}}</span>
        <p>修剪</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop3.png" alt="">
        <span>{{waters}}</span>
        <p>浇水</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop4.png" alt="">
        <span>{{pet_food_num}}</span>
        <p>宠物粮</p>
      </div>
    </div>
    <div class="pet-hp-list">
			<div class="pet-hp" v-for='pet, key in pet_list'>
				<p>宠物{{key+1}} 饱食度</p>
				<div class="hp">
					<div :style="{width: pet.hp+'%', backgroundColor: bg(pet.hp)}" class="process">{{pet.hp}}%</div>
        </div>
      </div>
    </div>
	</div>
	<script>
	apiready = function(){
		init();
		new Vue({
			el:"#app",
			data(){
				return {
		          	namespace: '/mofang',
		          	token:"",
		          	socket: null,
					pet_food_num: 0,  // 宠物粮数量
					fertilizer_num: 0,  // 化肥数量
					waters: 0,  // 浇水次数
					shears: 0,  // 剪刀次数
					pet_list: [],
					user_tree_data:{
						'total_tree': 9,  // 总树桩数量
						'user_tree_number': 0,  // 当前用户激活树桩数量
						'user_tree_list': [  // 当前种植的树桩列表状态

						],
					},

					tree_status:{

					},

					// user_tree_data:{
					// 	'total_tree': 9,  // 总树桩数量
					// 	'user_tree_number': 5,  // 当前用户激活树桩数量
					// 	'user_tree_list': [  // 当前种植的树桩列表状态
					// 		{  // 树桩状态
					// 			'time': 1609808084,  // 种植时间
					// 			'status': 4,  // 植物状态
					// 			'has_time': 300,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// tree_status:{
					// 	'tree_status_0': 'tree0.png',  // 树桩
					// 	'tree_status_1': 'tree1.png',  // 空桩
					// 	'tree_status_2': 'tree2.png',  // 幼苗
					// 	'tree_status_3': 'tree3.png',  // 成长
					// 	'tree_status_4': 'tree4.png',  // 成熟
					// },
					pet_number: [],
          			timeout: 0,
					prev:{name:"",url:"",params:{}},
					current:{name:"orchard",url:"orchard.html",params:{}},
				}
			},
			computed:{
				// 已激活但是未使用的树桩
				active_tree(){
					return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
				},
				// 未激活的剩余树桩
				lock_tree(){
					return parseInt(this.user_tree_data.total_tree - this.user_tree_data.user_tree_number);
				},
			},
      created(){
				this.show_pet_list();
				this.show_tree_list();
				this.get_prop_list();
      },
			methods:{
				feed(pet_key){
					// 我的背包
					this.game.save({'pet_key': pet_key});  // 记录本次喂养的宠物下标
					this.game.goFrame('package', 'package.html', this.current, null, {
						type: 'push',
						subType: 'from_top',
						duration: 300
					});
					api.addEventListener({
					    name: 'pet_feed_success'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_list[ret.value.pet_key].hp = Math.ceil(ret.value.hp_time / this.pet_list[ret.value.pet_key].pet_hp_max * 100)
								this.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;
								this.game.save({'pet_list': this.pet_list})
					    }
					});

				},
				bg(hp){
					if(hp>90){
						return '#f00';
					}else if(hp>60){
						return '#a00';
					}else if(hp>30){
						return '#600';
					}else {
						return '#300';
					}
				},
				get_prop_list(){
					// 更新道具列表信息
					api.addEventListener({
					    name: 'update_prop_data'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
					    }
					});

				},
				tree_img(status){
					return '../static/images/'+this.tree_status[status];
				},
				show_pet_list(){
					api.addEventListener({
					    name: 'pet_show_success'
					}, (ret, err)=>{
					    if( ret ){
								// 显示宠物相关
								this.pet_list = this.game.get('pet_list');
								this.pet_number = parseInt(this.game.get('pet_number'));
								var timer = null;
								setInterval(()=>{
									// 保证定时器中每次读取的都是最新的宠物信息
									this.pet_list = this.game.get('pet_list');
									for(let i in this.pet_list){
											if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){
												// 宠物挂了
												api.sendEvent({
														name: 'pet_die',
														extra: {
														}
												});
											}
											if(this.pet_list[i].hp_time>0){
												this.pet_list[i].hp_time -= 0.5;
												this.pet_list[i].hp = Math.ceil(this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100)
											}
										}
										this.game.save({'pet_list': this.pet_list});
										}, 500);
					    }
					});
				},
				show_tree_list(){
					api.addEventListener({
					    name: 'user_tree_data'
					}, (ret, err)=>{
					    if( ret ){
								// 用户种植植物信息
								this.user_tree_data.tree_total = parseInt(this.game.get('tree_total'));
								this.user_tree_data.user_tree_number = parseInt(this.game.get('user_tree_number'));
								this.user_tree_data.user_tree_list = this.game.get('user_tree_list');
								this.tree_status = this.game.get('tree_status');
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
								this.waters = this.game.get('waters');
								this.shears = this.game.get('shears');
					    }
					});
				},
				unlock_tree(){
					// 激活树桩通知
					api.confirm({
					    title: '提示',
					    msg: '是否激活树桩?',
					    buttons: ['确定', '取消']
					}, (ret, err)=>{
					    if( ret.buttonIndex == 1 ){
								api.sendEvent({
								    name: 'active_tree',
								    extra: {
								    }
								});
					    }
					});
					// 激活成功!
					api.addEventListener({
					    name: 'active_tree_success'
					}, (ret, err)=>{
					    if( ret ){
								// 新增树桩的数量
								this.user_tree_data.user_tree_number+=1;
								this.game.save({'user_tree_number': this.user_tree_data.user_tree_number});
					    }
					});

				}

			}
		});
	}
	</script>
</body>
</html>

客户端判断当前种植物状态控制图标的显示和隐藏
my_orhcard.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 orchard-frame" id="app">
    <div class="background">
      <img class="grassland2" src="../static/images/grassland2.png" alt="">
      <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
      <img class="stake1" src="../static/images/stake1.png" alt="">
      <img class="stake2" src="../static/images/stake2.png" alt="">
    </div>
    <div class="pet-box">
      <div class="pet">
				<span @click='feed(0)' class="popped" v-if='pet_list[0] && pet_list[0].hp<90 && pet_list[0].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
        <img v-if='pet_list.length > 0 && pet_list[0].hp>0' class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
      </div>
			<div class="pet" v-if='pet_number > 1'>
				<span @click='feed(1)' class="popped" v-if='pet_list[1] && pet_list[1].hp<90 && pet_list[1].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
				<img v-if='pet_list.length > 1 && pet_list[1].hp>0' class='pet-item' :src="settings.static_url+pet_list[1].image" alt="">
			</div>
      <div class="pet turned_off" v-if='pet_number == 1'>
        <img class="turned_image" src="../static/images/turned_off.png" alt="">
        <p>请购买宠物</p>
      </div>
    </div>
    <div class="tree-list">
      <div class="tree-box">
				<div class="tree" v-for='tree in user_tree_data.user_tree_list'>
					<span class="popped tree-popped" v-if='tree.allow_water && tree.waters<1'>
						<img src="../static/images/prop3.png" alt="">
					</span>
					<img :src="tree_img(tree.status)" alt="">
				</div>
				<!-- 已激活但是未种植的树桩列表 -->
        <div class="tree" v-for='i in active_tree'>
          <img src="../static/images/tree1.png" alt="">
        </div>
				<!-- 未激活树桩列表 -->
        <div @click='unlock_tree' class="tree" v-for='i in lock_tree'>
          <img src="../static/images/tree0.png" alt="">
        </div>
      </div>
    </div>
    <div class="prop-list">
      <div class="prop">
        <img src="../static/images/prop1.png" alt="">
        <span>{{fertilizer_num}}</span>
        <p>化肥</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop2.png" alt="">
        <span>{{shears}}</span>
        <p>修剪</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop3.png" alt="">
        <span>{{waters}}</span>
        <p>浇水</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop4.png" alt="">
        <span>{{pet_food_num}}</span>
        <p>宠物粮</p>
      </div>
    </div>
    <div class="pet-hp-list">
			<div class="pet-hp" v-for='pet, key in pet_list'>
				<p>宠物{{key+1}} 饱食度</p>
				<div class="hp">
					<div :style="{width: pet.hp+'%', backgroundColor: bg(pet.hp)}" class="process">{{pet.hp}}%</div>
        </div>
      </div>
    </div>
	</div>
	<script>
	apiready = function(){
		init();
		new Vue({
			el:"#app",
			data(){
				return {
		          	namespace: '/mofang',
		          	token:"",
		          	socket: null,
					pet_food_num: 0,  // 宠物粮数量
					fertilizer_num: 0,  // 化肥数量
					waters: 0,  // 浇水次数
					shears: 0,  // 剪刀次数
					pet_list: [],
					user_tree_data:{
						'total_tree': 9,  // 总树桩数量
						'user_tree_number': 0,  // 当前用户激活树桩数量
						'user_tree_list': [  // 当前种植的树桩列表状态

						],
					},

					tree_status:{

					},

					// user_tree_data:{
					// 	'total_tree': 9,  // 总树桩数量
					// 	'user_tree_number': 5,  // 当前用户激活树桩数量
					// 	'user_tree_list': [  // 当前种植的树桩列表状态
					// 		{  // 树桩状态
					// 			'time': 1609808084,  // 种植时间
					// 			'status': 4,  // 植物状态
					// 			'has_time': 300,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// tree_status:{
					// 	'tree_status_0': 'tree0.png',  // 树桩
					// 	'tree_status_1': 'tree1.png',  // 空桩
					// 	'tree_status_2': 'tree2.png',  // 幼苗
					// 	'tree_status_3': 'tree3.png',  // 成长
					// 	'tree_status_4': 'tree4.png',  // 成熟
					// },
					pet_number: [],
          			timeout: 0,
					prev:{name:"",url:"",params:{}},
					current:{name:"orchard",url:"orchard.html",params:{}},
				}
			},
			computed:{
				// 已激活但是未使用的树桩
				active_tree(){
					return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
				},
				// 未激活的剩余树桩
				lock_tree(){
					return parseInt(this.user_tree_data.total_tree - this.user_tree_data.user_tree_number);
				},
			},
      created(){
				this.show_pet_list();
				this.show_tree_list();
				this.get_prop_list();
      },
			methods:{
				feed(pet_key){
					// 我的背包
					this.game.save({'pet_key': pet_key});  // 记录本次喂养的宠物下标
					this.game.goFrame('package', 'package.html', this.current, null, {
						type: 'push',
						subType: 'from_top',
						duration: 300
					});
					api.addEventListener({
					    name: 'pet_feed_success'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_list[ret.value.pet_key].hp = Math.ceil(ret.value.hp_time / this.pet_list[ret.value.pet_key].pet_hp_max * 100)
								this.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;
								this.game.save({'pet_list': this.pet_list})
					    }
					});

				},
				bg(hp){
					if(hp>90){
						return '#f00';
					}else if(hp>60){
						return '#a00';
					}else if(hp>30){
						return '#600';
					}else {
						return '#300';
					}
				},
				get_prop_list(){
					// 更新道具列表信息
					api.addEventListener({
					    name: 'update_prop_data'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
					    }
					});

				},
				tree_img(status){
					return '../static/images/'+this.tree_status[status];
				},
				show_pet_list(){
					var pet_timer = null;
					api.addEventListener({
					    name: 'pet_show_success'
					}, (ret, err)=>{
					    if( ret ){
								// 显示宠物相关
								this.pet_list = this.game.get('pet_list');
								this.pet_number = parseInt(this.game.get('pet_number'));
								clearInterval(pet_timer);
								pet_timer = setInterval(()=>{
									// 保证定时器中每次读取的都是最新的宠物信息
									this.pet_list = this.game.get('pet_list');
									for(let i in this.pet_list){
											if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){
												// 宠物挂了
												api.sendEvent({
														name: 'pet_die',
														extra: {
														}
												});
											}
											if(this.pet_list[i].hp_time>0){
												this.pet_list[i].hp_time -= 0.5;
												this.pet_list[i].hp = Math.ceil(this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100)
											}
										}
										this.game.save({'pet_list': this.pet_list});
										}, 500);
					    }
					});
				},
				show_tree_list(){
					var tree_timer = null;  // 定时器标记符
					api.addEventListener({
					    name: 'user_tree_data'
					}, (ret, err)=>{
					    if( ret ){
								// 用户种植植物信息
								this.user_tree_data.tree_total = parseInt(this.game.get('tree_total'));
								this.user_tree_data.user_tree_number = parseInt(this.game.get('user_tree_number'));
								this.user_tree_data.user_tree_list = this.game.get('user_tree_list');
								this.tree_status = this.game.get('tree_status');
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
								this.waters = this.game.get('waters');
								this.shears = this.game.get('shears');
								clearInterval(tree_timer);
								tree_timer = setInterval(()=>{
									var user_tree_list = this.game.get('user_tree_list');
									for(let tree of user_tree_list){
										if(tree.water_time>-2){
											tree.water_time-=1;
										}
										if(tree.water_time<=-2 && tree.allow_water==false){
											// 通知服务端允许用户浇水
											tree.allow_water = true;
											this.waters+=1;
										}
									}
									this.user_tree_data.user_tree_list = user_tree_list;
									this.game.save({'user_tree_list': user_tree_list});
								},1000);
					    }
					});
				},
				unlock_tree(){
					// 激活树桩通知
					api.confirm({
					    title: '提示',
					    msg: '是否激活树桩?',
					    buttons: ['确定', '取消']
					}, (ret, err)=>{
					    if( ret.buttonIndex == 1 ){
								api.sendEvent({
								    name: 'active_tree',
								    extra: {
								    }
								});
					    }
					});
					// 激活成功!
					api.addEventListener({
					    name: 'active_tree_success'
					}, (ret, err)=>{
					    if( ret ){
								// 新增树桩的数量
								this.user_tree_data.user_tree_number+=1;
								this.game.save({'user_tree_number': this.user_tree_data.user_tree_number});
					    }
					});

				}

			}
		});
	}
	</script>
</body>
</html>

当用户单击浇水图标, 则根据当前果树的种植时间和状态确定是否进入成长期
socket.py,代码:

...
@socketio.on('water_tree', namespace='/mofang')
def water_tree(key):
    """浇水"""
    room = request.sid
    # 获取mongo中的用户信息
    query = {'sid': request.sid}
    user_info = mongo.db.user_info_list.find_one(query)
    # 获取mysql中的用户信息
    user = User.query.get(user_info.get('_id'))
    if user is None:
        socketio.emit('water_tree_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)
        return
    
    print('给%s的植物%s' % (user.id, key))
    user_tree_list = user_info.get('user_tree_list', [])
    try:
        tree_data = user_tree_list[key]
    except Exception as e:
        socketio.emit('water_tree_response', {'errno': status.CODE_NO_SUCH_TREE, 'errmsg': errmsg.tree_not_exists}, namespace='/mofang', room=room)
        return 
    
    if tree_data.get('allow_water', False) and int(tree_data.get('waters', 1)) == 0:
        """允许浇水"""
        tree_data['waters'] = 1
        # 如果种植物的种植时间达到成长期,则修改种植物的成长状态
        growup = redis.ttl('user_tree_growup_%s_%s' % (user.id, key))
        if growup == -2:
            tree_data['status'] = 3
            
    mongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})
    socketio.emit('water_tree_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok}, namespace='/mofang', room=room)
    user_login({'uid': user.id})

status.py

...
    CODE_NO_SUCH_TREE = 1021  # 没有对应的种植物

message.py

...
    tree_not_exists = "没有对应的种植物"

mycelery/tree/tasks.py,代码:


from application import redis
from mycelery.main import app, flask_app
from application import mongo

@app.task(name='tree_write', bind=True)
def tree_write(self):
    """允许浇水"""
    try:
        tree_list_water = redis.get('tree_list_water')
        if tree_list_water is None:
            return 
        tree_list_water = tree_list_water.decode()
        tree_list = tree_list_water.split(',')[:-1]
        print(tree_list)
        for tree in tree_list:
            treeinfo = tree.split('_')
            query = {'_id': treeinfo[0]}
            user_info = mongo.db.user_info_list.find_one(query)
            user_tree_list = user_info.get('user_tree_list', [])
            # 是否允许浇水
            timer = redis.ttl('user_tree_water_%s' % tree)
            if timer == -2 and user_tree_list[int(treeinfo[1])]['allow_water'] == False:
                user_tree_list[int(treeinfo[1])]['allow_water'] = True
            
            # 是否进入成长期
            timer = redis.ttl('user_tree_growup_%s' % tree)
            if timer == -2 and user_tree_list[int(treeinfo[1])]['status'] == 2:
                if user_tree_list[int(treeinfo[1])]['waters'] > 0:
                    user_tree_list[int(treeinfo[1])]['status'] = 3
                    # tree_list_water_str = ''.join(tree_list_water.split(tree + ','))
                    # redis.set('tree_list_water', tree_list_water_str)
            mongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})
    except Exception as exc:
        # 重新尝试执行失败任务
        print(exc)
                             

客户端发起浇水通知,my_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 orchard-frame" id="app">
    <div class="background">
      <img class="grassland2" src="../static/images/grassland2.png" alt="">
      <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
      <img class="stake1" src="../static/images/stake1.png" alt="">
      <img class="stake2" src="../static/images/stake2.png" alt="">
    </div>
    <div class="pet-box">
      <div class="pet">
				<span @click='feed(0)' class="popped" v-if='pet_list[0] && pet_list[0].hp<90 && pet_list[0].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
        <img v-if='pet_list.length > 0 && pet_list[0].hp>0' class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
      </div>
			<div class="pet" v-if='pet_number > 1'>
				<span @click='feed(1)' class="popped" v-if='pet_list[1] && pet_list[1].hp<90 && pet_list[1].hp>0 && pet_food_num > 0'>
					<img class='pet-prop' src="../static/images/prop4.png" alt="">
				</span>
				<img v-if='pet_list.length > 1 && pet_list[1].hp>0' class='pet-item' :src="settings.static_url+pet_list[1].image" alt="">
			</div>
      <div class="pet turned_off" v-if='pet_number == 1'>
        <img class="turned_image" src="../static/images/turned_off.png" alt="">
        <p>请购买宠物</p>
      </div>
    </div>
    <div class="tree-list">
      <div class="tree-box">
				<div class="tree" v-for='tree, key in user_tree_data.user_tree_list'>
					<span @click='water_tree(key)' class="popped tree-popped" v-if='tree.allow_water && tree.waters<1'>
						<img src="../static/images/prop3.png" alt="">
					</span>
					<img :src="tree_img(tree.status)" alt="">
				</div>
				<!-- 已激活但是未种植的树桩列表 -->
        <div class="tree" v-for='i in active_tree'>
          <img src="../static/images/tree1.png" alt="">
        </div>
				<!-- 未激活树桩列表 -->
        <div @click='unlock_tree' class="tree" v-for='i in lock_tree'>
          <img src="../static/images/tree0.png" alt="">
        </div>
      </div>
    </div>
    <div class="prop-list">
      <div class="prop">
        <img src="../static/images/prop1.png" alt="">
        <span>{{fertilizer_num}}</span>
        <p>化肥</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop2.png" alt="">
        <span>{{shears}}</span>
        <p>修剪</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop3.png" alt="">
        <span>{{waters}}</span>
        <p>浇水</p>
      </div>
      <div class="prop">
        <img src="../static/images/prop4.png" alt="">
        <span>{{pet_food_num}}</span>
        <p>宠物粮</p>
      </div>
    </div>
    <div class="pet-hp-list">
			<div class="pet-hp" v-for='pet, key in pet_list'>
				<p>宠物{{key+1}} 饱食度</p>
				<div class="hp">
					<div :style="{width: pet.hp+'%', backgroundColor: bg(pet.hp)}" class="process">{{pet.hp}}%</div>
        </div>
      </div>
    </div>
	</div>
	<script>
	apiready = function(){
		init();
		new Vue({
			el:"#app",
			data(){
				return {
		          	namespace: '/mofang',
		          	token:"",
		          	socket: null,
					pet_food_num: 0,  // 宠物粮数量
					fertilizer_num: 0,  // 化肥数量
					waters: 0,  // 浇水次数
					shears: 0,  // 剪刀次数
					pet_list: [],
					user_tree_data:{
						'total_tree': 9,  // 总树桩数量
						'user_tree_number': 0,  // 当前用户激活树桩数量
						'user_tree_list': [  // 当前种植的树桩列表状态

						],
					},

					tree_status:{

					},

					// user_tree_data:{
					// 	'total_tree': 9,  // 总树桩数量
					// 	'user_tree_number': 5,  // 当前用户激活树桩数量
					// 	'user_tree_list': [  // 当前种植的树桩列表状态
					// 		{  // 树桩状态
					// 			'time': 1609808084,  // 种植时间
					// 			'status': 4,  // 植物状态
					// 			'has_time': 300,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// tree_status:{
					// 	'tree_status_0': 'tree0.png',  // 树桩
					// 	'tree_status_1': 'tree1.png',  // 空桩
					// 	'tree_status_2': 'tree2.png',  // 幼苗
					// 	'tree_status_3': 'tree3.png',  // 成长
					// 	'tree_status_4': 'tree4.png',  // 成熟
					// },
					pet_number: [],
          			timeout: 0,
					prev:{name:"",url:"",params:{}},
					current:{name:"orchard",url:"orchard.html",params:{}},
				}
			},
			computed:{
				// 已激活但是未使用的树桩
				active_tree(){
					return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
				},
				// 未激活的剩余树桩
				lock_tree(){
					return parseInt(this.user_tree_data.total_tree - this.user_tree_data.user_tree_number);
				},
			},
      created(){
				this.show_pet_list();
				this.show_tree_list();
				this.get_prop_list();
      },
			methods:{
				water_tree(tree_key){
					// 给种植物浇水
					api.sendEvent({
					    name: 'water_tree',
					    extra: {
					        key: tree_key,
					    }
					});
					api.addEventListener({
					    name: 'water_tree_success'
					}, (ret, err)=>{
					    if( ret ){
								this.user_tree_data.user_tree_list[ret.value.key]['waters'] = 1;
								this.waters-=1;
								this.game.save({'user_tree_list': this.user_tree_data.user_tree_list});
					    }
					});

				},
				feed(pet_key){
					// 我的背包
					this.game.save({'pet_key': pet_key});  // 记录本次喂养的宠物下标
					this.game.goFrame('package', 'package.html', this.current, null, {
						type: 'push',
						subType: 'from_top',
						duration: 300
					});
					api.addEventListener({
					    name: 'pet_feed_success'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_list[ret.value.pet_key].hp = Math.ceil(ret.value.hp_time / this.pet_list[ret.value.pet_key].pet_hp_max * 100)
								this.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;
								this.game.save({'pet_list': this.pet_list})
					    }
					});

				},
				bg(hp){
					if(hp>90){
						return '#f00';
					}else if(hp>60){
						return '#a00';
					}else if(hp>30){
						return '#600';
					}else {
						return '#300';
					}
				},
				get_prop_list(){
					// 更新道具列表信息
					api.addEventListener({
					    name: 'update_prop_data'
					}, (ret, err)=>{
					    if( ret ){
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
					    }
					});

				},
				tree_img(status){
					return '../static/images/'+this.tree_status[status];
				},
				show_pet_list(){
					var pet_timer = null;
					api.addEventListener({
					    name: 'pet_show_success'
					}, (ret, err)=>{
					    if( ret ){
								// 显示宠物相关
								this.pet_list = this.game.get('pet_list');
								this.pet_number = parseInt(this.game.get('pet_number'));
								clearInterval(pet_timer);
								pet_timer = setInterval(()=>{
									// 保证定时器中每次读取的都是最新的宠物信息
									this.pet_list = this.game.get('pet_list');
									for(let i in this.pet_list){
											if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){
												// 宠物挂了
												api.sendEvent({
														name: 'pet_die',
														extra: {
														}
												});
											}
											if(this.pet_list[i].hp_time>0){
												this.pet_list[i].hp_time -= 0.5;
												this.pet_list[i].hp = Math.ceil(this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100)
											}
										}
										this.game.save({'pet_list': this.pet_list});
										}, 500);
					    }
					});
				},
				show_tree_list(){
					var tree_timer = null;  // 定时器标记符
					api.addEventListener({
					    name: 'user_tree_data'
					}, (ret, err)=>{
					    if( ret ){
								// 用户种植植物信息
								this.user_tree_data.tree_total = parseInt(this.game.get('tree_total'));
								this.user_tree_data.user_tree_number = parseInt(this.game.get('user_tree_number'));
								this.user_tree_data.user_tree_list = this.game.get('user_tree_list');
								this.tree_status = this.game.get('tree_status');
								this.pet_food_num = this.game.get('pet_food_num');
								this.fertilizer_num = this.game.get('fertilizer_num');
								this.waters = this.game.get('waters');
								this.shears = this.game.get('shears');
								clearInterval(tree_timer);
								tree_timer = setInterval(()=>{
									var user_tree_list = this.game.get('user_tree_list');
									for(let tree of user_tree_list){
										if(tree.water_time>-2){
											tree.water_time-=1;
										}
										if(tree.water_time<=-2 && tree.allow_water==false){
											// 通知服务端允许用户浇水
											tree.allow_water = true;
											this.waters+=1;
										}
										if(tree.growup_time>=-2){
											tree.growup_time-=1;
										}
										if(tree.growup_time<=-2 && tree.waters>0 && tree.status=='tree_status_2'){
											// 通知服务端植物成长了
											tree.status='tree_status_3';
										}
									}
									this.user_tree_data.user_tree_list = user_tree_list;
									this.game.print(this.user_tree_data.user_tree_list)
									this.game.save({'user_tree_list': user_tree_list});
								},1000);
					    }
					});
				},
				unlock_tree(){
					// 激活树桩通知
					api.confirm({
					    title: '提示',
					    msg: '是否激活树桩?',
					    buttons: ['确定', '取消']
					}, (ret, err)=>{
					    if( ret.buttonIndex == 1 ){
								api.sendEvent({
								    name: 'active_tree',
								    extra: {
								    }
								});
					    }
					});
					// 激活成功!
					api.addEventListener({
					    name: 'active_tree_success'
					}, (ret, err)=>{
					    if( ret ){
								// 新增树桩的数量
								this.user_tree_data.user_tree_number+=1;
								this.game.save({'user_tree_number': this.user_tree_data.user_tree_number});
					    }
					});

				}

			}
		});
	}
	</script>
</body>
</html>

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:{}},
				}
			},
      beforeCreate(){
        this.game.goFrame('orchard', 'my_orchard.html', this.current, {
          x: 0,
          y: 180,
          w: 'auto',
          h: 410,
        }, null);
			},
			created(){
        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();
						this.show_pet();
						this.use_prop();
						this.active_tree();
						this.water_tree();
          });
        },
        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,
						});
						this.game.save({
							'tree_total': message.tree_total,
							'user_tree_number': message.user_tree_number,
							'user_tree_list': message.user_tree_list,
							'tree_status': message.tree_status,
							'pet_food_num': message.pet_food_num,
							'fertilizer_num': message.fertilizer_num,
							'waters': message.waters,
							'shears': message.shears,
						});
						setTimeout(()=>{
							// 通知种植园页面获取到了当前用户的种植信息
							api.sendEvent({
							    name: 'user_tree_data',
							    extra: {}
							});
						}, 500);
					});
				},
				user_package(){
					// 用户背包道具列表
					this.socket.on('user_prop_response', (message)=>{
						this.game.fsave({
							'user_package': message.data,
						});
						// 界面中的道具信息
						this.game.save({
							'pet_food_num': message.pet_food_num,
							'fertilizer_num': message.fertilizer_num,
						});
						api.sendEvent({
						    name: 'update_prop_data',
						    extra: {

						    }
						});

					})
				},
        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);
					})
				},
				use_prop(){
					// 使用道具
					var pid = 0;
					api.addEventListener({
					    name: 'use_prop'
					}, (ret, err)=>{
					    if( ret ){
								// 用户使用道具
								pid = ret.value.pid;
								var pet_key = this.game.get('pet_key');
								if(pet_key<1){
									pet_key = 0;
								}
					      this.socket.emit('use_prop', {pid: ret.value.pid, pet_key: pet_key});
					    }
					});
					this.socket.on('prop_use_response', (message)=>{
						if(parseInt(message.errno) === 1000){
							api.sendEvent({
							    name: 'prop_use_success',
							    extra: {
										pid: pid
							    }
							});
						}else{
							api.alert({
							    title: '提示',
							    msg: message.errmsg,
							});

						}
					});
					this.socket.on('pet_feed_response', (message)=>{
						if(parseInt(message.errno) === 1000){
							api.sendEvent({
									name: 'pet_feed_success',
									extra: {
										pet_key: message.pet_key,
										hp_time: message.hp_time
									}
							});
						}else{
							api.alert({
									title: '提示',
									msg: 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,
							});

						}
					})
				},
				show_pet(){
					// 显示宠物
					this.socket.emit('pet_show');
					this.socket.on('pet_show_response', (message)=>{
						if(parseInt(message.errno) === 1000){
							// 把宠物信息保存到本地
							this.game.save({'pet_list': message.pet_list})
							this.game.save({'pet_number': message.pet_number})
							setTimeout(()=>{
								api.sendEvent({
								    name: 'pet_show_success',
								    extra: {}
								});
							}, 500);
						}else {
							api.alert({
							    title: '提示',
							    msg: message.errmsg,
							});
						}
					});
					api.addEventListener({
					    name: 'pet_die'
					}, (ret, err)=>{
					    if( ret ){
								this.socket.emit('pet_show');
					    }
					});

				},
				active_tree(){
					// 激活树桩
					api.addEventListener({
					    name: 'active_tree'
					}, (ret, err)=>{
					    if( ret ){
								this.socket.emit('active_tree');
					    }
					});
					this.socket.on('active_tree_response', (message)=>{
						if(parseInt(message.errno) === 1000){
							// 更新数据到本地
							api.sendEvent({
							    name: 'active_tree_success',
							    extra: {}
							});
						}else {
							api.alert({
							    title: '提示',
							    msg: message.errmsg,
							});
						}
					})
				},
				water_tree(){
					var key = null;
					api.addEventListener({
					    name: 'water_tree'
					}, (ret, err)=>{
					    if( ret ){
								key = ret.value.key;
								this.socket.emit('water_tree', key=key);
					    }
					});
					this.socket.on('water_tree_response', (message)=>{
						if(parseInt(message.errno) == 1000){
							// 更新数据到本地
							api.sendEvent({
							    name: 'water_tree_success',
							    extra: {key: key}
							});
						}else {
							api.alert({
							    title: '提示',
							    msg: 'message.errmsg',
							});
							
						}
					})
					
				}
			}
		});
	}
	</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值