Ejemplo n.º 1
0
    def get_update_state(device_code):

        if isinstance(device_code, Device):
            return device_code.update_state

        if isinstance(device_code, basestring):
            device_update_key = RedisClient.get_device_update_status_key(
                device_code)
            update_state = redis_device_client.get(device_update_key)
            if update_state is not None:
                return update_state

            log.info("当前设备游戏更新状态还未缓存: device_code = {}".format(device_code))
            device = DeviceService.get_device_by_code(device_code)
            if device is None:
                log.error(
                    "当前设备号没有搜索到设备信息: device_code = {}".format(device_code))
                return None

            # 缓存游戏更新设备状态
            redis_device_client.setex(device_update_key,
                                      DEFAULT_EXPIRED_DEVICE_STATUS,
                                      device.update_state)
            return device.update_state

        log.error("当前参数既不是字符串类型也不是Device类型: type = {}".format(
            type(device_code)))
        return None
Ejemplo n.º 2
0
    def set_update_state(device, update_state, last_update_time=None):

        # 如果传出的是 设备号 则查找
        if isinstance(device, basestring):
            device = DeviceService.get_device_by_code(device)
            if device is None:
                return False

        update_info = {Device.update_state: update_state}

        if last_update_time is not None:
            update_info[Device.last_update_time] = last_update_time

        rowcount = Device.query.filter_by(id=device.id).update(update_info)
        if rowcount <= 0:
            log.error("设备游戏更新状态更新失败: device_id = {} update_state = {}".format(
                device.id, update_state))
            return False

        log.info("设备更新状态写入数据库完成: rowcount = {} ".format(rowcount))
        device_update_key = RedisClient.get_device_update_status_key(
            device.device_code)

        # 存储状态到redis中 状态只保存一天,防止数据被删除 缓存一直存在
        redis_device_client.setex(device_update_key,
                                  DEFAULT_EXPIRED_DEVICE_STATUS, update_state)
        log.info(
            "设备更新状态设置成功: device_id = {} device_code = {} update_state = {}".
            format(device.id, device.device_code, update_state))

        return True
Ejemplo n.º 3
0
    def keep_device_heart(device_code):
        # 先获得心跳的主键
        device_heart_key = RedisClient.get_device_heart_key(device_code)

        redis_device_client.setex(device_heart_key,
                                  DEFAULT_EXPIRED_DEVICE_HEART,
                                  int(time.time()))
Ejemplo n.º 4
0
    def request_sms(self, mobile):
        if not settings.SMS_ENABLED:
            log.info("当前处于调试状态,没有打开短信验证码功能, 不发送短信验证码请求...")
            return True

        captcha = str(SmsSenderUtil.get_random())
        resp = self.tx_sms_sender.send_with_param("86", mobile,
                                                  self.sms_text_temp_id,
                                                  [captcha], "", "", "")

        try:
            result = json.loads(resp)
            if result.get('result') != 0:
                log.error("发送验证码失败: mobile = {} captcha = {}".format(
                    mobile, captcha))
                log.error("返回错误为: resp = {}".format(resp))
                return False

            # 存储验证码到redis中 只保留五分钟有效
            key = RedisClient.get_captcha_redis_key(mobile)
            self.__redis.setex(key, DEFAULT_EXPIRED_CAPTCHA, captcha)

            log.info("验证码发送成功: mobile = {} captcha = {}".format(
                mobile, captcha))
            return True
        except Exception as e:
            log.error("发送验证码失败: mobile = {} captcha = {}".format(
                mobile, captcha))
            log.exception(e)

        return False
Ejemplo n.º 5
0
    def do_offline_order_by_device_code(device_code):
        device_code_key = RedisClient.get_device_code_key(device_code)
        record_key = redis_cache_client.get(device_code_key)
        if record_key is None:
            log.error("当前通过device_code = {} 下机失败, 没有在redis中找到对应的上机信息".format(
                device_code))
            return False

        # 发送下机指令
        return WindowsService.do_offline_order(record_key)
Ejemplo n.º 6
0
def get_online_status():
    user_key = RedisClient.get_user_key(g.user_id)
    record_key = redis_cache_client.get(user_key)
    if record_key is None:
        return fail(HTTP_OK, u'当前用户没有上机信息', 0)

    charging = redis_cache_client.get(record_key)
    if charging is None:
        return fail(HTTP_OK, u'当前用户没有上机信息', 0)

    return success(WindowsService.get_current_time_charging(charging))
Ejemplo n.º 7
0
    def mobile_reach_rate_limit(self, mobile):
        if not settings.SMS_ENABLED:
            log.info("调试模式下,可以无限次请求验证码!")
            return False

        key = RedisClient.get_mobile_redis_key(mobile)
        value = self.__redis.get(key)
        log.info('redis[%s]: %s', key, value)
        if value is not None:
            return True

        self.__redis.setex(key, DEFAULT_EXPIRED_MOBILE, mobile)
        return False
Ejemplo n.º 8
0
    def delete_device(device_id):

        device = DeviceService.get_device_by_id(device_id)

        if device is None:
            log.warn("当前需要删除的设备不存在: device_id = {}".format(device_id))
            return False

        # 当前设备在线,且设备正在被用户使用,则不能够删除
        if DeviceService.get_device_alive_status(device) == Device.ALIVE_ONLINE and \
                        DeviceService.get_device_status(device) != DeviceStatus.STATUE_FREE:
            log.warn("当前设备不处于空闲状态,不能删除: device_id = {}".format(device.id))
            return False

        # 删除设备状态缓存信息
        device_status_key = RedisClient.get_device_status_key(
            device.device_code)
        redis_device_client.delete(device_status_key)

        # 删除设备更新状态缓存
        device_update_status_key = RedisClient.get_device_update_status_key(
            device.device_code)
        redis_device_client.delete(device_update_status_key)

        # 删除设备心跳缓存
        device_heart_key = RedisClient.get_device_heart_key(device.device_code)
        redis_device_client.delete(device_heart_key)

        # 删除设备上的游戏
        DeviceGameService.delete_deploy_device_game(device.id)

        if not device.delete():
            log.warn("设备信息删除失败: {}".format(
                json.dumps(device.to_dict(), ensure_ascii=False)))
            return False
        return True
Ejemplo n.º 9
0
    def online_recharge(user_id, total_fee):
        # 先获得用户缓存的信息
        user_key = RedisClient.get_user_key(user_id)

        # todo 这里需要加锁, 否则扣费下机时会有影响
        lock = DistributeLock(user_key, redis_cache_client)

        try:
            lock.acquire()
            record_key = redis_cache_client.get(user_key)
            if record_key is None:
                log.info("当前用户没有在线 record_key = None, 不需要同步在线数据: user_id = {}".
                         format(user_id))
                return

            charge_str = redis_cache_client.get(record_key)
            if charge_str is None:
                log.info(
                    "当前用户没有在线 charging = None, 不需要同步在线数据: user_id = {}".format(
                        user_id))
                return

            try:
                charge_dict = json.loads(charge_str)
                if charge_dict is None:
                    log.error("解析json数据失败: {}".format(charge_str))
                    return

                balance_account = charge_dict.get('balance_account')
                if not isinstance(balance_account, int):
                    log.error(
                        "balance_account 数据类型不正确: {}".format(balance_account))
                    return

                charge_dict['balance_account'] = balance_account + total_fee
                redis_cache_client.set(record_key, json.dumps(charge_dict))

                log.info(
                    "同步修改redis中用户余额信息成功! user_id = {} account = {}".format(
                        user_id, balance_account + total_fee))
            except Exception as e:
                log.error("解析json数据失败: {}".format(charge_str))
                log.exception(e)
        finally:
            lock.release()
Ejemplo n.º 10
0
def check_connect():
    if not request.is_json:
        log.warn("参数错误...")
        return fail(HTTP_OK, u"need application/json!!")

    device_code = request.json.get('device_code')
    if device_code is None:
        return fail(HTTP_OK, u"not have device_code!!!")

    # 获得设备使用状态
    device_status = DeviceService.get_device_status(device_code)
    if device_status is None:
        return success({
            'status': -1,
            'device_status': device_status,
            'msg': "not deploy"
        })

    # 保持心跳
    DeviceService.keep_device_heart(device_code)

    # 从维护状态跳转到空闲状态
    if device_status == DeviceStatus.STATUS_MAINTAIN:
        log.info("当前状态为维护状态,设备已经有心跳需要重新设置空闲状态!")
        DeviceService.status_transfer(device_code, device_status,
                                      DeviceStatus.STATUE_FREE)

        # 重新获得设备状态
        device_status = DeviceService.get_device_status(device_code)

    device_code_key = RedisClient.get_device_code_key(device_code)
    record_key = redis_cache_client.get(device_code_key)
    if record_key is None:
        return success({
            'status': 0,
            'device_status': device_status,
            'msg': "not login"
        })

    return success({
        "status": 1,
        "token": record_key,
        'device_status': device_status,
        "msg": "login successed!"
    })
Ejemplo n.º 11
0
    def get_device_alive_status(device):

        if isinstance(device, basestring):
            device_code = device
        elif isinstance(device, Device):
            device_code = device.device_code
        else:
            log.error("当前设备参数获取存活状态不正确: device = {} type = {}".format(
                device, type(device)))
            return Device.ALIVE_OFFLINE

        # 先获得心跳的主键
        device_heart_key = RedisClient.get_device_heart_key(device_code)
        last_heart_time = redis_device_client.get(device_heart_key)
        if last_heart_time is None:
            return Device.ALIVE_OFFLINE

        return Device.ALIVE_ONLINE
Ejemplo n.º 12
0
    def set_device_status(device, device_status):
        '''
        :param device: Device 类型
        :param device_status:
        :return:
        '''
        if not isinstance(device, Device):
            log.error("当前设置设备状态传入参数错误: device = {} type = {}".format(
                device, type(device)))
            return False

        if device_status not in Device.STATUS_VALUES:
            log.error(
                "当前设置设备状态传入参数错误: device_status = {}".format(device_status))
            return False

        # 先更新数据库,确保数据更新成功
        update_info = {
            Device.state: device_status,
            Device.state_version: device.state_version + 1
        }
        rowcount = Device.query.filter_by(
            id=device.id,
            state_version=device.state_version).update(update_info)
        if rowcount <= 0:
            log.error(
                "更新设备状态失败,版本信息已经被修改: id = {} state_version = {} state = {}".
                format(device.id, device.state_version, device_status))
            return False

        log.info("设备状态写入数据库完成: rowcount = {} ".format(rowcount))
        device_status_key = RedisClient.get_device_status_key(
            device.device_code)

        # 存储状态到redis中 状态只保存一天,防止数据被删除 缓存一直存在
        redis_device_client.setex(device_status_key,
                                  DEFAULT_EXPIRED_DEVICE_STATUS, device_status)
        log.info(
            "设备状态设置成功: device_id = {} device_code = {} state = {} state_version = {}"
            .format(device.id, device.device_code, device_status,
                    device.state_version + 1))
        return True
Ejemplo n.º 13
0
def wechat_offline():
    user_key = RedisClient.get_user_key(g.user_id)
    record_key = redis_cache_client.get(user_key)
    if record_key is None:
        return success({
            'status':
            0,
            'msg':
            "logout failed! reason: user device is already offline"
        })

    charging = redis_cache_client.get(record_key)
    if charging is None:
        return success({
            'status':
            0,
            'msg':
            "logout failed! reason: user device is already offline"
        })

    return WindowsService.do_offline(charging)
Ejemplo n.º 14
0
    def get_device_status(device):
        '''
        获取设备使用状态
        :param device: basestring or Device 类型
        :return:
        '''

        if isinstance(device, basestring):
            device_code = device
        elif isinstance(device, Device):
            # 如果传入的是设备信息直接返回设备使用状态即可, 缓存和数据库中的设备信息是保持严格一致的,只要写入则同时写入缓存和数据库
            return device.state
        else:
            log.error("当前参数数据类型不正确: device = {} type = {}".format(
                device, type(device)))
            return None

        # 先判断是否在缓存中
        device_status_key = RedisClient.get_device_status_key(device_code)

        device_status = redis_device_client.get(device_status_key)
        if device_status is not None:
            return device_status

        # 没有从缓存中找到设备状态 则去数据库中找
        device = DeviceService.get_device_by_code(device_code)
        if device is None:
            log.error("当前设备码没有从缓存中找到,也不存在于数据库中: device_code = {}".format(
                device_code))
            return None

        # 存储状态到redis中 状态只保存一天,防止数据被删除 缓存一直存在
        redis_device_client.setex(device_status_key,
                                  DEFAULT_EXPIRED_DEVICE_STATUS, device.state)

        log.info(
            "当前设备状态从数据库中加载, 缓存到redis中: device_code = {}".format(device_code))
        return device.state
Ejemplo n.º 15
0
    def validate_captcha(self, mobile, captcha):
        if not settings.SMS_ENABLED:
            if captcha == settings.SMS_DEBUG_CAPTCHA:
                return True
            log.info("调试模式验证码校验失败: 发送过来的验证码 = {} 需要校验的调试验证码 = {}".format(
                captcha, settings.SMS_DEBUG_CAPTCHA))
            return False

        key = RedisClient.get_captcha_redis_key(mobile)
        value = self.__redis.get(key)
        if value is None:
            log.info("当前手机不存在验证码: {}".format(mobile))
            return False

        if captcha != value:
            log.info("当前手机验证码错误: phone = {} captcha = {} cache = {}".format(
                mobile, captcha, value))
            return False

        # 删除已经验证码完成的验证码
        self.__redis.delete(key)
        log.info("删除手机验证码redis key = {}".format(key))
        return True
Ejemplo n.º 16
0
def keep_alive():
    if not request.is_json:
        log.warn("参数错误...")
        return fail(HTTP_OK, u"need application/json!!")

    record_key = request.json.get('token')
    if record_key is None:
        return fail(HTTP_OK, u"not have token!!!")

    device_code = request.json.get('device_code')
    if device_code is None:
        log.error(
            "无法保持心跳, 没有传入device_code: record_key = {}".format(record_key))
        return fail(HTTP_OK, u"not have device_code!!!")

    # 保持心跳
    DeviceService.keep_device_heart(device_code)

    charging = redis_cache_client.get(record_key)
    if charging is None:
        return success({
            "status": 0,
            "msg": "keepalive failed!reason:token invalid"
        })

    # 获得keep_alive_key 更新最新存活时间
    user_online_key = RedisClient.get_user_online_key(record_key)

    # 设置最新存活时间 最多存在五分钟
    redis_cache_client.setex(user_online_key, settings.MAX_LOST_HEART_TIME,
                             int(time.time()))

    return success({
        "status": 1,
        "msg": "keepalive success",
        "data": WindowsService.get_current_time_charging(charging)
    })
Ejemplo n.º 17
0
def qr_code_online(device_code):
    # # 当前用户没有登录
    # LOGIN_ERROR_BIND = -1
    # # 当前用户已经被删除
    # LOGIN_ERROR_DELETE = -2
    # # 当前用户被禁止使用
    # LOGIN_ERROR_FORBID = -3
    # # 当前设备不存在
    # LOGIN_ERROR_NOT_FIND = -4
    # # 用户余额不足
    # LOGIN_ERROR_NOT_SUFFICIENT_FUNDS = -5
    # # 上机失败 未知错误
    # LOGIN_ERROR_UNKNOW = -6
    # # 设备已经在使用了
    # LOGIN_ERROR_DEVICE_IN_USEING = -7
    # # 当前用户已经在使用上机了,但是不是当前设备在使用
    # LOGIN_ERROR_USER_IN_USEING = -8
    # # 当前设备不处于空闲状态,不能上机
    # LOGIN_ERROR_DEVICE_NOT_FREE = -9

    scan_from = request.args.get('from')
    # 登录链接
    login_url = url_for("wechat.menu", name="login")
    # 通过微信二维码扫描则需要判断当前用户是否已经关注公众号
    if scan_from != 'playing':
        # # 初始化用户关注信息
        # subscribe, nick_name, head_img_url = 0, '', ''

        openid = session.get('openid', None)
        # 如果不是微信二维码扫描 则跳转到登录界面
        if openid is None:
            log.info("当前扫描登录没有openid,需要跳转到登录界面..")
            return redirect(login_url)

        # 获得用户的关注状态 以及头像和昵称信息
        subscribe, nick_name, head_img_url = get_wechat_user_info(openid)
        # 如果用户没有关注微信号 直接跳转到关注页面
        if subscribe != 1:
            log.info("当前用户没有关注公众号: subscribe = {} openid = {}".format(
                subscribe, openid))
            return redirect(ATTENTION_URL)

        # 如果当前用户已经关注 则直接跳转到 祥基指定的链接 2017-10-13 15:26:00
        url = '#/playing?code={}'.format(device_code)
        log.info("当前用户已经关注了公众号,跳转链接: {}".format(url))
        return redirect(url)

    user_id_cookie = session.get('u_id')
    if user_id_cookie is None:
        log.warn("当前session中没有u_id 信息,需要登录...")
        return fail(HTTP_OK, u'当前用户没有登录', LOGIN_ERROR_BIND)

    user_id = decode_user_id(user_id_cookie)
    if user_id is None:
        log.warn(
            "当前用户信息被篡改,需要重新登录: user_id_cookie = {}".format(user_id_cookie))
        return fail(HTTP_OK, u'当前用户登录信息被篡改, 不能登录', LOGIN_ERROR_BIND)

    # 获得用户信息
    user = get_current_user(user_id)
    if user is None:
        log.warn("当前user_id还未绑定手机号码: user_id = {}".format(user_id))
        return fail(HTTP_OK, u"用户还绑定手机号码登录!", LOGIN_ERROR_BIND)

    # 如果当前用户 被禁用 则不能上机
    if user.deleted:
        log.warn("当前用户已经被删除了,无法上机: user_id = {}".format(user.id))
        return fail(HTTP_OK, u"当前用户已经被删除了,不能上机", LOGIN_ERROR_DELETE)

    # 判断当前用户是否已经被禁用了
    if user.state == 'unused':
        log.warn("当前用户已经被禁用了,无法上机: user_id = {}".format(user.id))
        return fail(HTTP_OK, u"当前用户已经被禁用了,不能上机", LOGIN_ERROR_FORBID)

    # 获得设备信息
    device = DeviceService.get_device_by_code(device_code=device_code)
    if device is None:
        log.warn("当前设备号没有对应的设备信息: device_code = {}".format(device_code))
        return fail(HTTP_OK, u"设备信息异常,设备不存在", LOGIN_ERROR_NOT_FIND)

    # 获得最新费率
    charge_mode = DeviceService.get_charge_mode(device)
    log.info("当前费率: charge_mode = {}".format(charge_mode))

    # 判断用户是否余额充足 如果小于一分钟不能上机
    if user.balance_account < charge_mode:
        log.info(
            "用户余额不足,不能上机: user_id = {} device_id = {} account = {}".format(
                user.id, device.id, user.balance_account))
        return fail(HTTP_OK, u"用户余额不足,不能上机!", LOGIN_ERROR_NOT_SUFFICIENT_FUNDS)

    # 判断是否已经在redis中进行记录
    record_key = RedisClient.get_record_key(user.id, device.id)
    # 获得用户上机key
    user_key = RedisClient.get_user_key(user.id)
    # 获得设备上机key
    device_key = RedisClient.get_device_key(device.id)

    # 判断是否已经登录了
    charging = redis_cache_client.get(record_key)
    if charging is None:

        # 判断当前设备是否已经在使用了
        if redis_cache_client.get(device_key):
            log.warn("当前设备{}已经在被使用,但是用户ID = {}又在申请".format(device.id, user.id))
            return fail(HTTP_OK, u"当前设备已经在使用上机了,但是不是当前用户在使用!",
                        LOGIN_ERROR_DEVICE_IN_USING)

        # 判断当前用户是否已经上机了
        if redis_cache_client.get(user_key):
            log.warn("当前用户{}已经在上机,但是又在申请当前设备ID = {}".format(
                user.id, device.id))
            return fail(HTTP_OK, u"当前用户已经在使用上机了,但是不是当前设备在使用!",
                        LOGIN_ERROR_USER_IN_USING)

        # 判断当前设备是否处于空闲状态 且设备必须处于在线状态
        device_status = DeviceService.get_device_status(device)
        device_alive = DeviceService.get_device_alive_status(device)
        if device_status != DeviceStatus.STATUE_FREE or device_alive != Device.ALIVE_ONLINE:
            log.warn("当前设备不处于空闲状态,不能上机: device_id = {} state = {} alive = {}".
                     format(device.id, device_status, device_alive))
            return fail(HTTP_OK, u"当前设备不处于空闲状态,或者当前设备不在线,不能上机!",
                        LOGIN_ERROR_DEVICE_NOT_FREE)

        # 判断当前设备是否正在更新 或者正在自检,这种状态下不能够登录上机
        current_update_state = DeviceService.get_update_state(device)
        if current_update_state == DeviceUpdateStatus.UPDATE_ING or \
                        current_update_state == DeviceUpdateStatus.UPDATE_CHECK:
            log.info(
                "当前设备正在更新或者自检中,不能登录: device_id = {} current_update_state = {}".
                format(device.id, current_update_state))
            return fail(HTTP_OK, u"当前设备处于自检或者更新中,不能上机!",
                        LOGIN_ERROR_DEVICE_NOT_FREE)

        log.info("用户还未上机可以进行上机: user_id = {} device_id = {}".format(
            user.id, device.id))
        if not WindowsService.do_online(user, device, charge_mode):
            log.warn("上机记录创建失败,上机失败: user_id = {} device_id = {}".format(
                user.id, device.id))
            return fail(HTTP_OK, u"上机异常!!", LOGIN_ERROR_UNKNOW)

    log.info("来自微信端游戏仓界面扫描: user_id = {} device_id = {}".format(
        user.id, device.id))
    return success()
Ejemplo n.º 18
0
    def decorator(*args, **kwargs):

        # 判断重新刷新token是否已经过期,如果过期则需要重新授权登录
        openid = session.get('openid', None)
        # 如果两个关键的token都存在 则正常进入下面的流程
        if openid is not None:
            g.openid = openid
            return func(*args, **kwargs)

        log.info("session 中没有openid")
        code = request.args.get('code', None)
        if code is None:
            log.info("url中没有code参数...")

            # 授权跳转到
            url = get_login_oauth_url()
            if url is not None:
                return redirect(url)

            log.info("当前不是get请求访问到这里: {}".format(request.method))
            return fail(HTTP_OK, u"微信未授权,请用微信端进行访问!")

        url = get_token_url(code)
        if url is None:
            log.info("获得token链接失败: code = {}".format(code))
            return fail(HTTP_OK, u"获得微信token失败!")

        try:
            log.info("开始获取openid, 获得token链接为: url = {}".format(url))
            resp = requests.get(url, verify=False, timeout=30)
            if resp.status_code != 200:
                log.warn("访问token链接失败: status_code = {} url = {}".format(
                    resp.status_code, url))
                return fail(HTTP_OK, u"获取access_token失败!")

            data = json.loads(resp.content)
            if data is None:
                log.warn("解析access_token失败: data = {}".format(data))
                return fail(HTTP_OK, u"解析access_token失败!")

            openid = data.get('openid', None)
            if openid is None:
                log.warn("解析openid失败: data = {}".format(resp.content))
                return fail(HTTP_OK, u"解析openid失败!")
            session['openid'] = openid

            # 保存access_token
            access_token = data.get('access_token', None)
            if access_token is not None:
                # session['access_token'] = access_token
                log.info("用户初次使用得到access_token = {}".format(access_token))

            expires_in = data.get("expires_in", None)

            # 存入redis 中
            if access_token is not None and expires_in is not None:
                # 添加到缓存
                redis_cache_client.setex(RedisClient.get_openid_key(openid),
                                         expires_in, access_token)

            g.openid = openid
            log.info("通过url链接获得openid成功: openid = {}".format(openid))
            return func(*args, **kwargs)
        except Exception as e:
            log.error("获取用户openid失败:")
            log.exception(e)
        return fail(HTTP_OK, u"微信授权失败!")
Ejemplo n.º 19
0
@time: 2017/10/15 18:51
"""

# 获得用户的关注状态 以及头像和昵称信息
import json
import sys

import requests

sys.path.append("..")
from exts.common import WECHAT_ACCESS_TOKEN_KEY
from exts.redis_api import RedisClient
from logger import Logger

log = Logger('get_user_info.log').get_logger()
tmp_redis_client = RedisClient(db=0)


# 获得用户的关注状态 以及头像和昵称信息
def get_wechat_user_info(openid):
    # 默认设置是未关注状态
    subscribe, nick_name, head_img_url = 0, '', ''

    if openid is None:
        log.error("openid 为None,未知异常!!!")
        return subscribe, nick_name, head_img_url

    access_token = tmp_redis_client.get(WECHAT_ACCESS_TOKEN_KEY)
    if access_token is None:
        log.error("access_token 为None,刷新token进程异常!!!")
        return subscribe, nick_name, head_img_url
import json
import threading
import time

import requests
from apscheduler.schedulers.background import BackgroundScheduler

import settings
from exts.common import WECHAT_ACCESS_TOKEN_KEY, WECHAT_JSAPI_TICKET_KEY, REDIS_PRE_RECORD_KEY, log, cal_cost_time, \
    DEFAULT_GAME_UPDATE_TIME
from exts.redis_api import RedisClient
from service.device.impl import DeviceGameService
from service.windows.impl import WindowsService

try:
    cache_client = RedisClient(db=0)
except Exception as ex:
    log.error("启动redis失败..")
    log.exception(ex)
    exit(0)

# 最后剩余时间阈值
WX_CACHE_LAST_TIME = 300


def update_access_token():
    url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}'.format(
        settings.WECHAT_APP_ID, settings.WECHAT_APP_SECRET)
    try:
        resp = requests.get(url, verify=False, timeout=30)
        if resp.status_code != 200:
def do_charging(record_key_list):
    if not isinstance(record_key_list, list):
        log.error("当前传入参数不正确: type = {}".format(type(record_key_list)))
        return

    if len(record_key_list) <= 0:
        log.info("当前没有上线用户,不需要计费...")
        return

    # 开始针对用户进行扣费
    for record_key in record_key_list:

        charge_str = cache_client.get(record_key)
        if charge_str is None:
            log.info("当前用户已经下线,不需要再计费: record_key = {}".format(record_key))
            continue

        try:
            charge_dict = json.loads(charge_str)

            user_id = charge_dict.get('user_id')
            if user_id is None:
                log.error("没有关键信息 user_id: charge_str = {}".format(charge_str))
                continue

            device_id = charge_dict.get('device_id')
            if device_id is None:
                log.error(
                    "没有关键信息 device_id: charge_str = {}".format(charge_str))
                continue

            record_key = RedisClient.get_record_key(user_id, device_id)

            # 判断是否已经有5分钟没有收到心跳
            user_online_key = RedisClient.get_user_online_key(record_key)
            last_timestamp = cache_client.get(user_online_key)
            if last_timestamp is None:
                log.info(
                    "没有收到任何心跳信息, 强制下机, 当前上线用户没有最后存活时间: user_id = {} device_id = {}"
                    .format(user_id, device_id))
                # 执行下机流程
                if WindowsService.do_offline_order(record_key):
                    log.info("没有收到任何心跳信息,强制下机完成: record_key = {}".format(
                        record_key))
                else:
                    log.error("强制下机失败: record_key = {}".format(record_key))
                continue

            # # 获得当前时间戳
            # last_timestamp = int(last_timestamp)
            # now_timestamp = int(time.time())
            #
            # # 如果当前丢失心跳的时间超过阈值,则默认离线,需要下机
            # if now_timestamp - last_timestamp >= settings.MAX_LOST_HEART_TIME:
            #     # 下机
            #     log.info("当前用户与机器没有收到任何心跳信息,强制下机: record_key = {} last_timestamp = {}".format(
            #         record_key, last_timestamp))
            #     # 执行下机流程
            #     do_offline_order(record_key)
            #     log.info("没有收到任何心跳信息,强制下机完成: record_key = {}".format(record_key))
            #     continue

            # charge_dict = {
            #     'id': self.id,
            #     'user_id': self.user_id,
            #     'device_id': self.device_id,
            #     # 花费金额数目
            #     'cost_money': self.cost_money,
            #     # 上机时间
            #     'ctime': self.ctime.strftime('%Y-%m-%d %H:%M:%S'),
            #     # 更新时间,主要用户同步计费
            #     'utime': self.utime.strftime('%Y-%m-%d %H:%M:%S'),
            #     # 已经上机时间
            #     'cost_time': self.cost_time,
            #     # 计费方式 目前默认 5分钱/分钟
            #     'charge_mode': 5,
            #     # 当前用户余额
            #     'balance_account': 10000,
            #     # 设备机器码
            #     'device_code': 'xx-xx-xx-xx-xx-xx',
            # }
            # 如果用户余额不足上机了,则强制下机
            ctime = charge_dict.get('ctime')
            if ctime is None:
                log.error("没有关键信息 ctime: charge_str = {}".format(charge_str))
                continue

            charge_mode = charge_dict.get('charge_mode')
            if charge_mode is None:
                log.error(
                    "没有关键信息 charge_mode: charge_str = {}".format(charge_str))
                continue

            balance_account = charge_dict.get('balance_account')
            if balance_account is None:
                log.error("没有关键信息 balance_account: charge_str = {}".format(
                    charge_str))
                continue

            now_timestamp = int(time.time())
            start_time = int(
                time.mktime(time.strptime(ctime, "%Y-%m-%d %H:%M:%S")))
            cost_time = cal_cost_time(now_timestamp - start_time)
            cost_money = cost_time * int(charge_mode)
            # 如果使用的费用超额半分钟的费用,则强制下机
            if cost_money - balance_account >= 0.75 * int(charge_mode):
                log.info(
                    "当前用户余额不足,强制下机: record_key = {} balance_account = {} "
                    "cost_time = {}分钟 cost_money = {} start_time = {} now_time = {}"
                    .format(record_key, balance_account, cost_time, cost_money,
                            start_time, now_timestamp))
                # 执行下机流程
                if WindowsService.do_offline_order(record_key):
                    log.info(
                        "当前用户余额不足, 强制下机完成: record_key = {}".format(record_key))
                else:
                    log.error("强制下机失败: record_key = {}".format(record_key))
                continue

        except Exception as e:
            log.error("当前存入的计费数据格式不正确: charge_str = {}".format(charge_str))
            log.exception(e)
            continue
Ejemplo n.º 22
0
    def do_online(user, device, charge_mode):
        log.info("用户还未上机可以进行上机: user_id = {} device_id = {}".format(
            user.id, device.id))
        record, is_success = UseRecord.create(user.id, device.id,
                                              device.address.province,
                                              device.address.city,
                                              device.address.area,
                                              device.address.location)
        if not is_success:
            return False

        # 判断是否已经在redis中进行记录
        record_key = RedisClient.get_record_key(user.id, device.id)
        # 获得用户上线key
        user_key = RedisClient.get_user_key(user.id)
        # 获得设备上线key
        device_key = RedisClient.get_device_key(device.id)
        # 获得当前设备token
        device_code_key = RedisClient.get_device_code_key(device.device_code)

        # 获得keep_alive_key 更新最新存活时间
        user_online_key = RedisClient.get_user_online_key(record_key)

        log.info(
            "当前上机时间: user_id:{} device_id:{} record_id:{} ctime:{}".format(
                user.id, device.id, record.id,
                record.ctime.strftime('%Y-%m-%d %H:%M:%S')))

        # 获得计费结构体
        charging = record.to_charging()
        # 得到计费方式
        charging['charge_mode'] = charge_mode
        # 得到当前用户总额
        charging['balance_account'] = user.balance_account
        # 填充设备机器码
        charging['device_code'] = device.device_code
        # 填充用户的openid
        charging['openid'] = user.openid

        # charging = {
        #     'id': self.id,
        #     'user_id': self.user_id,
        #     'device_id': self.device_id,
        #     # 花费金额数目
        #     'cost_money': self.cost_money,
        #     # 上机时间
        #     'ctime': self.ctime.strftime('%Y-%m-%d %H:%M:%S'),
        #     # 更新时间,主要用户同步计费
        #     'utime': self.utime.strftime('%Y-%m-%d %H:%M:%S'),
        #     # 已经上机时间
        #     'cost_time': self.cost_time,
        #     # 计费方式 目前默认 5分钱/分钟
        #     'charge_mode': 5,
        #     # 当前用户余额
        #     'balance_account': 10000,
        #     # 设备机器码
        #     'device_code': 'xx-xx-xx-xx-xx-xx',
        # }

        charge_str = json.dumps(charging)

        # 操作redis 需要加锁
        lock = DistributeLock(user_key, redis_cache_client)
        try:
            lock.acquire()

            # 设置设备当前使用状态
            if not DeviceService.set_device_status(device,
                                                   DeviceStatus.STATUE_BUSY):
                log.error("设置设备状态失败, 上机异常!!!")
                return False

            # 开始上线 把上线信息存储redis
            redis_cache_client.set(record_key, charge_str)
            redis_cache_client.set(user_key, record_key)
            redis_cache_client.set(device_key, record_key)
            # 根据设备机器码获得记录token
            redis_cache_client.set(device_code_key, record_key)
            # 设置最新存活时间 最多存活五分钟
            import time
            redis_cache_client.setex(user_online_key,
                                     settings.MAX_LOST_HEART_TIME,
                                     int(time.time()))

            is_success = True
        except Exception as e:
            is_success = False
            log.exception(e)
        finally:
            lock.release()

        # 判断上线是否成功
        if is_success:
            # 发送上线通知
            TemplateService.online(user.openid, record.ctime, device.address,
                                   user.balance_account, charge_mode)

        return True
Ejemplo n.º 23
0
def user_offline():
    if not request.is_json:
        log.warn("参数错误...")
        return fail(HTTP_OK, u"need application/json!!")

    user_id = request.json.get('user_id')
    device_code = request.json.get('device_code')
    device_id = request.json.get('device_id')

    log.info("当前强制下机user_id = {}".format(user_id))
    log.info("当前强制下机device_code = {}".format(device_code))
    log.info("当前强制下机device_id = {}".format(device_id))

    if user_id is not None:
        user_key = RedisClient.get_user_key(user_id)
        record_key = redis_cache_client.get(user_key)
        if record_key is None:
            return success({
                'status':
                0,
                'msg':
                "logout failed! reason: user device is already offline"
            })

        charging = redis_cache_client.get(record_key)
        if charging is None:
            return success({
                'status':
                0,
                'msg':
                "logout failed! reason: user device is already offline"
            })
        log.info("通过user_id下机: user_id = {}".format(user_id))
        return WindowsService.do_offline(charging)

    if device_code is not None:
        device_code_key = RedisClient.get_device_code_key(device_code)
        record_key = redis_cache_client.get(device_code_key)
        if record_key is not None:
            charging = redis_cache_client.get(record_key)
            if charging is None:
                return success({
                    'status':
                    0,
                    'msg':
                    "logout failed! reason: user device is already offline"
                })
            log.info("通过device_code下机: device_code = {}".format(device_code))
            return WindowsService.do_offline(charging)

    if device_id is not None:
        device_key = RedisClient.get_device_key(device_id)

        record_key = redis_cache_client.get(device_key)
        if record_key is None:
            return success({
                'status':
                0,
                'msg':
                "logout failed! reason: user device is already offline"
            })

        charging = redis_cache_client.get(record_key)
        if charging is None:
            return success({
                'status':
                0,
                'msg':
                "logout failed! reason: user device is already offline"
            })
        log.info("通过device_id下机: device_id = {}".format(device_id))
        return WindowsService.do_offline(charging)

    return success(u'当前参数没有使任何机器或用户下机')
Ejemplo n.º 24
0
    def do_offline(charging):

        # offline_lock_key = None
        lock = None
        if charging is None:
            log.error("charging is None 下机异常!!")
            return fail(HTTP_OK, u"下机异常!")

        try:
            charge_dict = json.loads(charging)
            record_id = charge_dict.get('id')
            user_id = charge_dict.get('user_id')
            device_id = charge_dict.get('device_id')
            charge_mode = charge_dict.get('charge_mode')
            device_code = charge_dict.get('device_code')
            openid = charge_dict.get('openid')
            log.info(
                "当前下线信息: user_id = {} device_id = {} charge_mode = {} device_code = {}"
                .format(user_id, device_id, charge_mode, device_code))

            # 获得用户上线key
            user_key = RedisClient.get_user_key(user_id)

            #  下机需要加锁
            lock = DistributeLock(user_key, redis_cache_client)

            log.info("开始加锁下机: user_key = {}".format(user_key))

            # 加锁下机
            lock.acquire()

            # 判断是否已经在redis中进行记录
            record_key = RedisClient.get_record_key(user_id, device_id)
            if redis_cache_client.get(record_key) is None:
                log.warn("当前用户或者设备已经下机: user_id = {} device_id = {}".format(
                    user_id, device_id))
                return success({'status': 1, 'msg': 'logout successed!'})

            # 结账下机
            result, record, user = WindowsService.cal_offline(
                user_id=user_id,
                device_id=device_id,
                record_id=record_id,
                charge_mode=charge_mode)
            if not result:
                log.error(
                    "下机扣费失败: user_id = {} device_id = {} charge_mode = {}".
                    format(user_id, device_id, charge_mode))
                return fail(HTTP_OK, u"下机失败!")

            # 获得设备上线key
            device_key = RedisClient.get_device_key(device_id)
            # 获得当前设备token
            device_code_key = RedisClient.get_device_code_key(device_code)
            # 获得keep_alive_key 更新最新存活时间
            user_online_key = RedisClient.get_user_online_key(record_key)

            # 从redis中删除上机记录
            redis_cache_client.delete(record_key)
            redis_cache_client.delete(user_key)
            redis_cache_client.delete(device_key)
            redis_cache_client.delete(device_code_key)
            redis_cache_client.delete(user_online_key)
            is_success = True
        except Exception as e:
            log.error("数据解析失败: {}".format(charging))
            log.exception(e)
            return fail(HTTP_OK, u"数据解析失败!!")
        finally:
            # 解锁
            if lock is not None:
                lock.release()
                log.info("下机完成: lock_key = {}".format(lock.lock_key))

        # 如果成功则进行下机提醒
        if is_success and openid is not None:
            TemplateService.offline(openid, record, user.balance_account)

        log.info("下机成功: user_id = {} device_id = {}".format(
            user_id, device_id))
        return success({'status': 1, 'msg': 'logout successed!'})