pageDetailResult.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /**
  2. * @class PageResult 页面详情结果统计模型,既页面内容统计模型
  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 PageLog = require('./pageLog')
  10. const ShareLog = require('./shareLog')
  11. const {
  12. DateTime
  13. } = require('../lib')
  14. module.exports = class PageResult extends BaseMod {
  15. constructor() {
  16. super()
  17. this.tableName = 'page-detail-result'
  18. this.platforms = []
  19. this.channels = []
  20. this.versions = []
  21. }
  22. /**
  23. * 数据统计
  24. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  25. * @param {Date|Time} date 指定日期或时间戳
  26. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  27. */
  28. async stat(type, date, reset) {
  29. if(!this.getConfig('pageDetailStat')) {
  30. return {
  31. code: 1001,
  32. msg: 'The page detail module not opened'
  33. }
  34. }
  35. //允许的类型
  36. const allowedType = ['day']
  37. if (!allowedType.includes(type)) {
  38. return {
  39. code: 1002,
  40. msg: 'This type is not allowed'
  41. }
  42. }
  43. this.fillType = type
  44. //获取当前统计的时间范围
  45. const dateTime = new DateTime()
  46. const dateDimension = dateTime.getTimeDimensionByType(type, -1, date)
  47. this.startTime = dateDimension.startTime
  48. this.endTime = dateDimension.endTime
  49. if (this.debug) {
  50. console.log('dimension time', this.startTime + '--' + this.endTime)
  51. }
  52. // 查看当前时间段日志是否已存在,防止重复执行
  53. if (!reset) {
  54. const checkRes = await this.getCollection(this.tableName).where({
  55. start_time: this.startTime,
  56. end_time: this.endTime
  57. }).get()
  58. if (checkRes.data.length > 0) {
  59. console.error('This page detail stat log have exists')
  60. return {
  61. code: 1003,
  62. msg: 'This page detail stat log have existed'
  63. }
  64. }
  65. } else {
  66. const delRes = await this.delete(this.tableName, {
  67. start_time: this.startTime,
  68. end_time: this.endTime
  69. })
  70. console.log('Delete old data result:', JSON.stringify(delRes))
  71. }
  72. // 数据获取
  73. this.pageLog = new PageLog()
  74. const statRes = await this.aggregate(this.pageLog.tableName, {
  75. match: {
  76. create_time: {
  77. $gte: this.startTime,
  78. $lte: this.endTime
  79. },
  80. page_detail_id: {
  81. $exists: true
  82. }
  83. },
  84. group: {
  85. _id: {
  86. appid: '$appid',
  87. version: '$version',
  88. platform: '$platform',
  89. channel: '$channel',
  90. page_detail_id: '$page_detail_id'
  91. },
  92. page_id: {
  93. $first: '$page_id'
  94. },
  95. visit_times: {
  96. $sum: 1
  97. }
  98. },
  99. sort: {
  100. visit_times: 1
  101. },
  102. getAll: true
  103. })
  104. let res = {
  105. code: 0,
  106. msg: 'success'
  107. }
  108. if (this.debug) {
  109. console.log('Page detail statRes', JSON.stringify(statRes.data))
  110. }
  111. if (statRes.data.length > 0) {
  112. this.fillData = []
  113. //获取填充数据
  114. for (const pdd of statRes.data) {
  115. try {
  116. await this.fill(pdd)
  117. } catch (e) {
  118. console.error('Page detail result data filled error', e, pdd)
  119. }
  120. }
  121. //数据批量入库
  122. if (this.fillData.length > 0) {
  123. res = await this.batchInsert(this.tableName, this.fillData)
  124. }
  125. }
  126. return res
  127. }
  128. /**
  129. * 页面详情(内容)统计数据填充
  130. * @param {Object} data 统计数据
  131. */
  132. async fill(data) {
  133. // 平台信息
  134. let platformInfo = null
  135. if (this.platforms && this.platforms[data._id.platform]) {
  136. //暂存下数据,减少读库
  137. platformInfo = this.platforms[data._id.platform]
  138. } else {
  139. const platform = new Platform()
  140. platformInfo = await platform.getPlatformAndCreate(data._id.platform, null)
  141. if (!platformInfo || platformInfo.length === 0) {
  142. platformInfo._id = ''
  143. }
  144. this.platforms[data._id.platform] = platformInfo
  145. if (this.debug) {
  146. console.log('platformInfo', JSON.stringify(platformInfo))
  147. }
  148. }
  149. // 渠道信息
  150. let channelInfo = null
  151. const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel
  152. if (this.channels && this.channels[channelKey]) {
  153. channelInfo = this.channels[channelKey]
  154. } else {
  155. const channel = new Channel()
  156. channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel)
  157. if (!channelInfo || channelInfo.length === 0) {
  158. channelInfo._id = ''
  159. }
  160. this.channels[channelKey] = channelInfo
  161. if (this.debug) {
  162. console.log('channelInfo', JSON.stringify(channelInfo))
  163. }
  164. }
  165. // 版本信息
  166. let versionInfo = null
  167. const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version
  168. if (this.versions && this.versions[versionKey]) {
  169. versionInfo = this.versions[versionKey]
  170. } else {
  171. const version = new Version()
  172. versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version)
  173. if (!versionInfo || versionInfo.length === 0) {
  174. versionInfo._id = ''
  175. }
  176. this.versions[versionKey] = versionInfo
  177. if (this.debug) {
  178. console.log('versionInfo', JSON.stringify(versionInfo))
  179. }
  180. }
  181. const matchCondition = data._id
  182. Object.assign(matchCondition, {
  183. create_time: {
  184. $gte: this.startTime,
  185. $lte: this.endTime
  186. }
  187. })
  188. if (this.debug) {
  189. console.log('matchCondition', JSON.stringify(matchCondition))
  190. }
  191. // 当前页面详情访问设备数
  192. const statPageDetailDeviceRes = await this.aggregate(this.pageLog.tableName, {
  193. match: matchCondition,
  194. group: [{
  195. _id: {
  196. device_id: '$device_id'
  197. }
  198. }, {
  199. _id: {},
  200. total_devices: {
  201. $sum: 1
  202. }
  203. }]
  204. })
  205. let pageDetailVisitDevices = 0
  206. if (statPageDetailDeviceRes.data.length > 0) {
  207. pageDetailVisitDevices = statPageDetailDeviceRes.data[0].total_devices
  208. }
  209. //当前页面详情访问人数
  210. const statPageDetailUserRes = await this.aggregate(this.pageLog.tableName, {
  211. match: {
  212. ...matchCondition,
  213. uid: {
  214. $ne: ''
  215. }
  216. },
  217. group: [{
  218. _id: {
  219. uid: '$uid'
  220. }
  221. }, {
  222. _id: {},
  223. total_users: {
  224. $sum: 1
  225. }
  226. }]
  227. })
  228. let pageDetailVisitUsers = 0
  229. if (statPageDetailUserRes.data.length > 0) {
  230. pageDetailVisitUsers = statPageDetailUserRes.data[0].total_users
  231. }
  232. // 访问时长
  233. const statPageDetailDurationRes = await this.aggregate(this.pageLog.tableName, {
  234. match: {
  235. appid: data._id.appid,
  236. version: data._id.version,
  237. platform: data._id.platform,
  238. channel: data._id.channel,
  239. previous_page_detail_id: data._id.page_detail_id,
  240. create_time: {
  241. $gte: this.startTime,
  242. $lte: this.endTime
  243. }
  244. },
  245. group: {
  246. _id: {},
  247. total_duration: {
  248. $sum: '$previous_page_duration'
  249. }
  250. }
  251. })
  252. let totalDuration = 0
  253. if (statPageDetailDurationRes.data.length > 0) {
  254. totalDuration = statPageDetailDurationRes.data[0].total_duration
  255. }
  256. // 分享次数
  257. const shareLog = new ShareLog()
  258. const statShareRes = await this.aggregate(shareLog.tableName, {
  259. match: {
  260. appid: data._id.appid,
  261. version: data._id.version,
  262. platform: data._id.platform,
  263. channel: data._id.channel,
  264. page_detail_id: data._id.page_detail_id,
  265. create_time: {
  266. $gte: this.startTime,
  267. $lte: this.endTime
  268. }
  269. },
  270. group: {
  271. _id: {},
  272. share_count: {
  273. $sum: 1
  274. }
  275. }
  276. })
  277. let shareCount = 0
  278. if (statShareRes.data.length > 0) {
  279. shareCount = statShareRes.data[0].share_count
  280. }
  281. // 数据填充
  282. const datetime = new DateTime()
  283. const insertParams = {
  284. appid: data._id.appid,
  285. platform_id: platformInfo._id,
  286. channel_id: channelInfo._id,
  287. version_id: versionInfo._id,
  288. page_id: data.page_id,
  289. page_detail_id: data._id.page_detail_id,
  290. visit_times: data.visit_times,
  291. visit_devices: pageDetailVisitDevices,
  292. visit_users: pageDetailVisitUsers,
  293. duration: totalDuration > 0 ? totalDuration : 1,
  294. share_count: shareCount,
  295. dimension: this.fillType,
  296. stat_date: datetime.getDate('Ymd', this.startTime),
  297. start_time: this.startTime,
  298. end_time: this.endTime
  299. }
  300. this.fillData.push(insertParams)
  301. return insertParams
  302. }
  303. }