local skynet = require "skynet" require "skynet.manager" local redisdriver = require "skynet.db.redis" local util = require "util" local common_fun = require "model.common_fun" local pipeline = require "pipeline" local logger = require "logger" local stringify = require "stringify" local httpc = require "http.httpc" local cjson = require "cjson" local db_list local db_character local KEY_STR = "statistics" local math_min = math.min local math_max = math.max local CMD = {} local next_collect = 0 local function time_key(tm) return os.date("%Y%m%d", tm) end local function check_next() if next_collect == 0 then next_collect = util.today() end local key = time_key(next_collect-DAY_SEC) if db_list:hexists(KEY_STR, key) == 1 then next_collect = next_collect + DAY_SEC else next_collect = next_collect end end local function init() local conf = assert(option.redis) db_character = redisdriver.connect(conf) db_character:select(0) db_list = redisdriver.connect(conf) db_list:select(11) check_next() end local function myfloor(num) return math.floor(num*100)/100 end local function collect(tm) local time = tm local keys = common_fun.redis_keys_wait(db_character, "character:*") local block = pipeline(1024) if next(keys) then for _, k in pairs(keys) do block.add("hmget", k, "nickname", "uid", "level", "lastlogin", "lastlogout", "createtime") end end local list = { create = 0, -- 激活用户 dau = 0, -- 日活 wau = 0, -- 周活 mau = 0, -- 月活 lost = 0, -- 流失 login = 0, -- 再次登录的玩家 keep = 0, -- 次日留存 keep3 = 0, -- 3日留存 keep7 = 0, -- 7日留存 keep30 = 0, -- 30日留存 all = 0, -- 总人数 rcg_player = 0, -- 充值人数(每日) rcg_money = 0, -- 充值金额(每日) arpu = 0, -- 付费玩家价值(每日) rcg_money30 = 0, -- 充值金额(每月) ltv = 0, -- 价值 } local resp = block.execute(db_character, {}) for _, v in ipairs(resp) do assert(v.ok) local rs = v.out if rs[2] then local name = rs[1] local uid = rs[2] local level = tonumber(rs[3]) local lastlogin = tonumber(rs[4]) or 0 local lastlogout = tonumber(rs[5]) or 0 local createtime = tonumber(rs[6]) or 0 -- 当日激活 if createtime >= time and createtime < time + DAY_SEC then list.create = list.create + 1 end if lastlogin - createtime > 10 then list.login = list.login + 1 end if createtime < time + DAY_SEC then local temp = math_min(time - lastlogout, time - lastlogin) if temp < 0 then list.dau = list.dau + 1 end if temp < 6 * DAY_SEC then list.wau = list.wau + 1 end if temp < 29 * DAY_SEC then list.mau = list.mau + 1 end if temp > 30 * DAY_SEC then list.lost = list.lost + 1 end local temp2 = math_max(math_min(lastlogin, time + DAY_SEC), math_min(lastlogout, time + DAY_SEC)) - createtime if temp2 > DAY_SEC then list.keep = list.keep + 1 end if temp2 > 2 * DAY_SEC then list.keep3 = list.keep3 + 1 end if temp2 > 6 * DAY_SEC then list.keep7 = list.keep7 + 1 end if temp2 > 29 * DAY_SEC then list.keep30 = list.keep30 + 1 end list.all = list.all + 1 end end end local ok,herrno,info = pcall(httpc.post, RECHARGE_SERVICE, '/recharge_info', { serverid = option.sid, starttime = time, }) if herrno ~= 200 then logger.trace("查询充值失败.errno %s", herrno) return end -- info 解码 local order = cjson.decode(info) if order.erron ~= 0 then logger.trace("查询充值失败 %s", stringify(order)) return end list.rcg_player = order.data.day_player or 0 list.rcg_money = order.data.day_recharge or 0 list.rcg_money30 = order.data.month_recharge or 0 if list.create > 0 then list.keep = myfloor(list.keep/list.create) list.keep3 = myfloor(list.keep3/list.create) list.keep7 = myfloor(list.keep7/list.create) list.keep30 = myfloor(list.keep30/list.create) else list.keep = "NaN" list.keep3 = "NaN" list.keep7 = "NaN" list.keep30 = "NaN" end if list.rcg_player > 0 then list.arpu = list.rcg_money/list.rcg_player else list.arpu = "NaN" end db_list:hset(KEY_STR, time_key(tm), cjson.encode(list)) return list end local function watchtime() skynet.fork(function() while(true) do if os.time() >= next_collect then collect(next_collect-DAY_SEC) check_next() end skynet.sleep(6000) end end) end local key_index = { "create", "dau", "wau", "mau", "lost", "login", "keep", "keep3", "keep7", "keep30", "all", "rcg_player", "rcg_money", "rcg_money30", "arpu", "ltv", } function CMD.get_statistics_data(time) local activitytime = skynet.localname(".activitytime") local open_time = skynet.call(activitytime, "lua", "query") local now = util.today() if not time then time = now end local start_time = util.month_one(time) local end_time = util.month_one(start_time+31*DAY_SEC)-DAY_SEC if start_time < open_time then start_time = util.today(open_time) end if end_time >= now then end_time = now - DAY_SEC end local ret = {} ret[1] = common_fun.scopy(key_index) table.insert(ret[1], 1, "date") for i = start_time, end_time, DAY_SEC do local key = time_key(i) local data if db_list:hexists(KEY_STR, key) == 1 then data = cjson.decode(db_list:hget(KEY_STR, key)) else data = collect(i) end local temp = {key} for _, v in pairs(key_index) do local value = data and tostring(data[v]) if not value or value == 'nil' then value = 'NaN' end table.insert(temp, value) end table.insert(ret, temp) end return ret end function CMD.start() init() watchtime() end skynet.init(function() skynet.register(".statistics") end) skynet.start(function() skynet.dispatch("lua", function(session, _, cmd, ...) local f = assert(CMD[cmd]) if 0==session then f(...) else skynet.retpack(f(...)) end end) end)