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) })
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
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!'})
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