activeDevices.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /**
  2. * @class ActiveDevices 活跃设备模型 - 每日跑批合并,仅添加本周/本月首次访问的设备。
  3. */
  4. const BaseMod = require('./base')
  5. const Platform = require('./platform')
  6. const Channel = require('./channel')
  7. const Version = require('./version')
  8. const SessionLog = require('./sessionLog')
  9. const {
  10. DateTime,
  11. UniCrypto
  12. } = require('../lib')
  13. module.exports = class ActiveDevices extends BaseMod {
  14. constructor() {
  15. super()
  16. this.tableName = 'active-devices'
  17. this.platforms = []
  18. this.channels = []
  19. this.versions = []
  20. }
  21. /**
  22. * @desc 活跃设备统计 - 为周统计/月统计提供周活/月活数据
  23. * @param {date|time} date
  24. * @param {bool} reset
  25. */
  26. async stat(date, reset) {
  27. const dateTime = new DateTime()
  28. const dateDimension = dateTime.getTimeDimensionByType('day', -1, date)
  29. this.startTime = dateDimension.startTime
  30. // 查看当前时间段数据是否已存在,防止重复生成
  31. if (!reset) {
  32. const checkRes = await this.getCollection(this.tableName).where({
  33. create_time: {
  34. $gte: dateDimension.startTime,
  35. $lte: dateDimension.endTime
  36. }
  37. }).get()
  38. if (checkRes.data.length > 0) {
  39. console.log('data have exists')
  40. return {
  41. code: 1003,
  42. msg: 'Devices data in this time have already existed'
  43. }
  44. }
  45. } else {
  46. const delRes = await this.delete(this.tableName, {
  47. create_time: {
  48. $gte: dateDimension.startTime,
  49. $lte: dateDimension.endTime
  50. }
  51. })
  52. console.log('Delete old data result:', JSON.stringify(delRes))
  53. }
  54. const sessionLog = new SessionLog()
  55. const statRes = await this.aggregate(sessionLog.tableName, {
  56. project: {
  57. appid: 1,
  58. version: 1,
  59. platform: 1,
  60. channel: 1,
  61. is_first_visit: 1,
  62. create_time: 1,
  63. device_id: 1
  64. },
  65. match: {
  66. create_time: {
  67. $gte: dateDimension.startTime,
  68. $lte: dateDimension.endTime
  69. }
  70. },
  71. group: {
  72. _id: {
  73. appid: '$appid',
  74. version: '$version',
  75. platform: '$platform',
  76. channel: '$channel',
  77. device_id: '$device_id'
  78. },
  79. is_new: {
  80. $max: '$is_first_visit'
  81. },
  82. create_time: {
  83. $min: '$create_time'
  84. }
  85. },
  86. sort: {
  87. create_time: 1
  88. },
  89. getAll: true
  90. })
  91. let res = {
  92. code: 0,
  93. msg: 'success'
  94. }
  95. // if (this.debug) {
  96. // console.log('statRes', JSON.stringify(statRes))
  97. // }
  98. if (statRes.data.length > 0) {
  99. const uniCrypto = new UniCrypto()
  100. // 同应用、平台、渠道、版本的数据合并
  101. const statData = [];
  102. let statKey;
  103. let data
  104. for (const sti in statRes.data) {
  105. data = statRes.data[sti]
  106. statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id
  107. .channel)
  108. if (!statData[statKey]) {
  109. statData[statKey] = {
  110. appid: data._id.appid,
  111. platform: data._id.platform,
  112. version: data._id.version,
  113. channel: data._id.channel,
  114. device_ids: [],
  115. info: []
  116. }
  117. statData[statKey].device_ids.push(data._id.device_id)
  118. statData[statKey].info[data._id.device_id] = {
  119. is_new: data.is_new,
  120. create_time: data.create_time
  121. }
  122. } else {
  123. statData[statKey].device_ids.push(data._id.device_id)
  124. statData[statKey].info[data._id.device_id] = {
  125. is_new: data.is_new,
  126. create_time: data.create_time
  127. }
  128. }
  129. }
  130. this.fillData = []
  131. for (const sk in statData) {
  132. await this.getFillData(statData[sk])
  133. }
  134. if (this.fillData.length > 0) {
  135. res = await this.batchInsert(this.tableName, this.fillData)
  136. }
  137. }
  138. return res
  139. }
  140. /**
  141. * 获取填充数据
  142. * @param {Object} data
  143. */
  144. async getFillData(data) {
  145. // 平台信息
  146. let platformInfo = null
  147. if (this.platforms && this.platforms[data.platform]) {
  148. platformInfo = this.platforms[data.platform]
  149. } else {
  150. const platform = new Platform()
  151. platformInfo = await platform.getPlatformAndCreate(data.platform, null)
  152. if (!platformInfo || platformInfo.length === 0) {
  153. platformInfo._id = ''
  154. }
  155. this.platforms[data.platform] = platformInfo
  156. if (this.debug) {
  157. console.log('platformInfo', JSON.stringify(platformInfo))
  158. }
  159. }
  160. // 渠道信息
  161. let channelInfo = null
  162. const channelKey = data.appid + '_' + platformInfo._id + '_' + data.channel
  163. if (this.channels && this.channels[channelKey]) {
  164. channelInfo = this.channels[channelKey]
  165. } else {
  166. const channel = new Channel()
  167. channelInfo = await channel.getChannelAndCreate(data.appid, platformInfo._id, data.channel)
  168. if (!channelInfo || channelInfo.length === 0) {
  169. channelInfo._id = ''
  170. }
  171. this.channels[channelKey] = channelInfo
  172. if (this.debug) {
  173. console.log('channelInfo', JSON.stringify(channelInfo))
  174. }
  175. }
  176. // 版本信息
  177. let versionInfo = null
  178. const versionKey = data.appid + '_' + data.platform + '_' + data.version
  179. if (this.versions && this.versions[versionKey]) {
  180. versionInfo = this.versions[versionKey]
  181. } else {
  182. const version = new Version()
  183. versionInfo = await version.getVersionAndCreate(data.appid, data.platform, data.version)
  184. if (!versionInfo || versionInfo.length === 0) {
  185. versionInfo._id = ''
  186. }
  187. this.versions[versionKey] = versionInfo
  188. if (this.debug) {
  189. console.log('versionInfo', JSON.stringify(versionInfo))
  190. }
  191. }
  192. // 是否在本周内已存在
  193. const datetime = new DateTime()
  194. const dateDimension = datetime.getTimeDimensionByType('week', 0, this.startTime)
  195. // 取出本周已经存储的device_id
  196. const weekHaveDeviceList = []
  197. const haveWeekList = await this.selectAll(this.tableName, {
  198. appid: data.appid,
  199. version_id: versionInfo._id,
  200. platform_id: platformInfo._id,
  201. channel_id: channelInfo._id,
  202. device_id: {
  203. $in: data.device_ids
  204. },
  205. dimension: 'week',
  206. create_time: {
  207. $gte: dateDimension.startTime,
  208. $lte: dateDimension.endTime
  209. }
  210. }, {
  211. device_id: 1
  212. })
  213. if (haveWeekList.data.length > 0) {
  214. for (const hui in haveWeekList.data) {
  215. weekHaveDeviceList.push(haveWeekList.data[hui].device_id)
  216. }
  217. }
  218. if (this.debug) {
  219. console.log('weekHaveDeviceList', JSON.stringify(weekHaveDeviceList))
  220. }
  221. // 取出本月已经存储的device_id
  222. const dateMonthDimension = datetime.getTimeDimensionByType('month', 0, this.startTime)
  223. const monthHaveDeviceList = []
  224. const haveMonthList = await this.selectAll(this.tableName, {
  225. appid: data.appid,
  226. version_id: versionInfo._id,
  227. platform_id: platformInfo._id,
  228. channel_id: channelInfo._id,
  229. device_id: {
  230. $in: data.device_ids
  231. },
  232. dimension: 'month',
  233. create_time: {
  234. $gte: dateMonthDimension.startTime,
  235. $lte: dateMonthDimension.endTime
  236. }
  237. }, {
  238. device_id: 1
  239. })
  240. if (haveMonthList.data.length > 0) {
  241. for (const hui in haveMonthList.data) {
  242. monthHaveDeviceList.push(haveMonthList.data[hui].device_id)
  243. }
  244. }
  245. if (this.debug) {
  246. console.log('monthHaveDeviceList', JSON.stringify(monthHaveDeviceList))
  247. }
  248. //数据填充
  249. for (const ui in data.device_ids) {
  250. //周活跃数据填充
  251. if (!weekHaveDeviceList.includes(data.device_ids[ui])) {
  252. this.fillData.push({
  253. appid: data.appid,
  254. platform_id: platformInfo._id,
  255. channel_id: channelInfo._id,
  256. version_id: versionInfo._id,
  257. is_new: data.info[data.device_ids[ui]].is_new,
  258. device_id: data.device_ids[ui],
  259. dimension: 'week',
  260. create_time: data.info[data.device_ids[ui]].create_time
  261. })
  262. }
  263. //月活跃数据填充
  264. if (!monthHaveDeviceList.includes(data.device_ids[ui])) {
  265. this.fillData.push({
  266. appid: data.appid,
  267. platform_id: platformInfo._id,
  268. channel_id: channelInfo._id,
  269. version_id: versionInfo._id,
  270. is_new: data.info[data.device_ids[ui]].is_new,
  271. device_id: data.device_ids[ui],
  272. dimension: 'month',
  273. create_time: data.info[data.device_ids[ui]].create_time
  274. })
  275. }
  276. }
  277. return true
  278. }
  279. /**
  280. * 日志清理,此处日志为临时数据并不需要自定义清理,默认为固定值即可
  281. */
  282. async clean() {
  283. // 清除周数据,周留存统计最高需要10周数据,多余的为无用数据
  284. const weeks = 10
  285. console.log('Clean device\'s weekly logs - week:', weeks)
  286. const dateTime = new DateTime()
  287. const res = await this.delete(this.tableName, {
  288. dimension: 'week',
  289. create_time: {
  290. $lt: dateTime.getTimeBySetWeek(0 - weeks)
  291. }
  292. })
  293. if (!res.code) {
  294. console.log('Clean device\'s weekly logs - res:', res)
  295. }
  296. // 清除月数据,月留存统计最高需要10个月数据,多余的为无用数据
  297. const monthes = 10
  298. console.log('Clean device\'s monthly logs - month:', monthes)
  299. const monthRes = await this.delete(this.tableName, {
  300. dimension: 'month',
  301. create_time: {
  302. $lt: dateTime.getTimeBySetMonth(0 - monthes)
  303. }
  304. })
  305. if (!monthRes.code) {
  306. console.log('Clean device\'s monthly logs - res:', res)
  307. }
  308. return monthRes
  309. }
  310. }