statistics.lua 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. local skynet = require "skynet"
  2. require "skynet.manager"
  3. local redisdriver = require "skynet.db.redis"
  4. local util = require "util"
  5. local common_fun = require "model.common_fun"
  6. local pipeline = require "pipeline"
  7. local logger = require "logger"
  8. local stringify = require "stringify"
  9. local httpc = require "http.httpc"
  10. local cjson = require "cjson"
  11. local db_list
  12. local db_character
  13. local KEY_STR = "statistics"
  14. local math_min = math.min
  15. local math_max = math.max
  16. local CMD = {}
  17. local next_collect = 0
  18. local function time_key(tm)
  19. return os.date("%Y%m%d", tm)
  20. end
  21. local function check_next()
  22. if next_collect == 0 then
  23. next_collect = util.today()
  24. end
  25. local key = time_key(next_collect-DAY_SEC)
  26. if db_list:hexists(KEY_STR, key) == 1 then
  27. next_collect = next_collect + DAY_SEC
  28. else
  29. next_collect = next_collect
  30. end
  31. end
  32. local function init()
  33. local conf = assert(option.redis)
  34. db_character = redisdriver.connect(conf)
  35. db_character:select(0)
  36. db_list = redisdriver.connect(conf)
  37. db_list:select(11)
  38. check_next()
  39. end
  40. local function myfloor(num)
  41. return math.floor(num*100)/100
  42. end
  43. local function collect(tm)
  44. local time = tm
  45. local keys = common_fun.redis_keys_wait(db_character, "character:*")
  46. local block = pipeline(1024)
  47. if next(keys) then
  48. for _, k in pairs(keys) do
  49. block.add("hmget", k, "nickname", "uid", "level", "lastlogin", "lastlogout", "createtime")
  50. end
  51. end
  52. local list = {
  53. create = 0, -- 激活用户
  54. dau = 0, -- 日活
  55. wau = 0, -- 周活
  56. mau = 0, -- 月活
  57. lost = 0, -- 流失
  58. login = 0, -- 再次登录的玩家
  59. keep = 0, -- 次日留存
  60. keep3 = 0, -- 3日留存
  61. keep7 = 0, -- 7日留存
  62. keep30 = 0, -- 30日留存
  63. all = 0, -- 总人数
  64. rcg_player = 0, -- 充值人数(每日)
  65. rcg_money = 0, -- 充值金额(每日)
  66. arpu = 0, -- 付费玩家价值(每日)
  67. rcg_money30 = 0, -- 充值金额(每月)
  68. ltv = 0, -- 价值
  69. }
  70. local resp = block.execute(db_character, {})
  71. for _, v in ipairs(resp) do
  72. assert(v.ok)
  73. local rs = v.out
  74. if rs[2] then
  75. local name = rs[1]
  76. local uid = rs[2]
  77. local level = tonumber(rs[3])
  78. local lastlogin = tonumber(rs[4]) or 0
  79. local lastlogout = tonumber(rs[5]) or 0
  80. local createtime = tonumber(rs[6]) or 0
  81. -- 当日激活
  82. if createtime >= time and createtime < time + DAY_SEC then
  83. list.create = list.create + 1
  84. end
  85. if lastlogin - createtime > 10 then
  86. list.login = list.login + 1
  87. end
  88. if createtime < time + DAY_SEC then
  89. local temp = math_min(time - lastlogout, time - lastlogin)
  90. if temp < 0 then
  91. list.dau = list.dau + 1
  92. end
  93. if temp < 6 * DAY_SEC then
  94. list.wau = list.wau + 1
  95. end
  96. if temp < 29 * DAY_SEC then
  97. list.mau = list.mau + 1
  98. end
  99. if temp > 30 * DAY_SEC then
  100. list.lost = list.lost + 1
  101. end
  102. local temp2 = math_max(math_min(lastlogin, time + DAY_SEC), math_min(lastlogout, time + DAY_SEC)) - createtime
  103. if temp2 > DAY_SEC then
  104. list.keep = list.keep + 1
  105. end
  106. if temp2 > 2 * DAY_SEC then
  107. list.keep3 = list.keep3 + 1
  108. end
  109. if temp2 > 6 * DAY_SEC then
  110. list.keep7 = list.keep7 + 1
  111. end
  112. if temp2 > 29 * DAY_SEC then
  113. list.keep30 = list.keep30 + 1
  114. end
  115. list.all = list.all + 1
  116. end
  117. end
  118. end
  119. local ok,herrno,info = pcall(httpc.post, RECHARGE_SERVICE, '/recharge_info', {
  120. serverid = option.sid,
  121. starttime = time,
  122. })
  123. if herrno ~= 200 then
  124. logger.trace("查询充值失败.errno %s", herrno)
  125. return
  126. end
  127. -- info 解码
  128. local order = cjson.decode(info)
  129. if order.erron ~= 0 then
  130. logger.trace("查询充值失败 %s", stringify(order))
  131. return
  132. end
  133. list.rcg_player = order.data.day_player or 0
  134. list.rcg_money = order.data.day_recharge or 0
  135. list.rcg_money30 = order.data.month_recharge or 0
  136. if list.create > 0 then
  137. list.keep = myfloor(list.keep/list.create)
  138. list.keep3 = myfloor(list.keep3/list.create)
  139. list.keep7 = myfloor(list.keep7/list.create)
  140. list.keep30 = myfloor(list.keep30/list.create)
  141. else
  142. list.keep = "NaN"
  143. list.keep3 = "NaN"
  144. list.keep7 = "NaN"
  145. list.keep30 = "NaN"
  146. end
  147. if list.rcg_player > 0 then
  148. list.arpu = list.rcg_money/list.rcg_player
  149. else
  150. list.arpu = "NaN"
  151. end
  152. db_list:hset(KEY_STR, time_key(tm), cjson.encode(list))
  153. return list
  154. end
  155. local function watchtime()
  156. skynet.fork(function()
  157. while(true) do
  158. if os.time() >= next_collect then
  159. collect(next_collect-DAY_SEC)
  160. check_next()
  161. end
  162. skynet.sleep(6000)
  163. end
  164. end)
  165. end
  166. local key_index = {
  167. "create",
  168. "dau",
  169. "wau",
  170. "mau",
  171. "lost",
  172. "login",
  173. "keep",
  174. "keep3",
  175. "keep7",
  176. "keep30",
  177. "all",
  178. "rcg_player",
  179. "rcg_money",
  180. "rcg_money30",
  181. "arpu",
  182. "ltv",
  183. }
  184. function CMD.get_statistics_data(time)
  185. local activitytime = skynet.localname(".activitytime")
  186. local open_time = skynet.call(activitytime, "lua", "query")
  187. local now = util.today()
  188. if not time then
  189. time = now
  190. end
  191. local start_time = util.month_one(time)
  192. local end_time = util.month_one(start_time+31*DAY_SEC)-DAY_SEC
  193. if start_time < open_time then
  194. start_time = util.today(open_time)
  195. end
  196. if end_time >= now then
  197. end_time = now - DAY_SEC
  198. end
  199. local ret = {}
  200. ret[1] = common_fun.scopy(key_index)
  201. table.insert(ret[1], 1, "date")
  202. for i = start_time, end_time, DAY_SEC do
  203. local key = time_key(i)
  204. local data
  205. if db_list:hexists(KEY_STR, key) == 1 then
  206. data = cjson.decode(db_list:hget(KEY_STR, key))
  207. else
  208. data = collect(i)
  209. end
  210. local temp = {key}
  211. for _, v in pairs(key_index) do
  212. local value = data and tostring(data[v])
  213. if not value or value == 'nil' then
  214. value = 'NaN'
  215. end
  216. table.insert(temp, value)
  217. end
  218. table.insert(ret, temp)
  219. end
  220. return ret
  221. end
  222. function CMD.start()
  223. init()
  224. watchtime()
  225. end
  226. skynet.init(function()
  227. skynet.register(".statistics")
  228. end)
  229. skynet.start(function()
  230. skynet.dispatch("lua", function(session, _, cmd, ...)
  231. local f = assert(CMD[cmd])
  232. if 0==session then
  233. f(...)
  234. else
  235. skynet.retpack(f(...))
  236. end
  237. end)
  238. end)