sessionLog.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /**
  2. * @class SessionLog 基础会话日志模型
  3. */
  4. const BaseMod = require('./base')
  5. const Page = require('./page')
  6. const Platform = require('./platform')
  7. const Channel = require('./channel')
  8. const UserSessionLog = require('./userSessionLog')
  9. const Device = require('./device')
  10. const {
  11. DateTime
  12. } = require('../lib')
  13. module.exports = class SessionLog extends BaseMod {
  14. constructor() {
  15. super()
  16. this.tableName = 'session-logs'
  17. }
  18. /**
  19. * 会话日志批量填充
  20. * @param {Object} reportParams 上报参数
  21. */
  22. async batchFill(reportParams) {
  23. let params, pageInfo, nowTime, firstVistTime, lastVistTime;
  24. const fillParams = []
  25. const page = new Page()
  26. const platform = new Platform()
  27. const dateTime = new DateTime()
  28. const channel = new Channel()
  29. const device = new Device()
  30. let res
  31. for (const pk in reportParams) {
  32. params = reportParams[pk]
  33. res = await this.fill(params)
  34. if (res.code) {
  35. console.error(res.msg)
  36. } else {
  37. //添加设备信息
  38. await device.setDevice(params)
  39. }
  40. }
  41. return res
  42. }
  43. /**
  44. * 会话日志填充
  45. * @param {Object} params 上报参数
  46. */
  47. async fill(params) {
  48. // 应用信息
  49. if (!params.ak) {
  50. return {
  51. code: 200,
  52. msg: 'Parameter "ak" not found'
  53. }
  54. }
  55. // 平台信息
  56. if (!params.ut) {
  57. return {
  58. code: 200,
  59. msg: 'Parameter "ut" not found'
  60. }
  61. }
  62. // 设备信息
  63. if (!params.did) {
  64. return {
  65. code: 200,
  66. msg: 'Parameter "did" not found'
  67. }
  68. }
  69. // 页面信息
  70. const page = new Page()
  71. const pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj)
  72. if (!pageInfo || pageInfo.length === 0) {
  73. return {
  74. code: 300,
  75. msg: 'Not found this entry page'
  76. }
  77. }
  78. if (this.debug) {
  79. console.log('pageInfo', JSON.stringify(pageInfo))
  80. }
  81. const platform = new Platform()
  82. const dateTime = new DateTime()
  83. const channel = new Channel()
  84. const nowTime = dateTime.getTime()
  85. const firstVistTime = params.fvts ? dateTime.strToTime(params.fvts) : nowTime
  86. const lastVistTime = (params.lvts && params.lvts !== '0') ? dateTime.strToTime(params.lvts) : 0
  87. const fillParams = {
  88. appid: params.ak,
  89. version: params.v ? params.v : '',
  90. platform: platform.getPlatformCode(params.ut, params.p),
  91. channel: channel.getChannelCode(params),
  92. type: params.cst ? parseInt(params.cst) : 0,
  93. // 访问设备
  94. device_id: params.did,
  95. //是否为首次访问,判断标准:最后一次访问时间为0
  96. is_first_visit: (params.lt === '1' && !lastVistTime) ? 1 : 0,
  97. first_visit_time: firstVistTime,
  98. last_visit_time: nowTime,
  99. visit_count: params.tvc ? parseInt(params.tvc) : 1,
  100. // 用户相关
  101. last_visit_user_id: params.uid ? params.uid : '',
  102. // 页面相关
  103. entry_page_id: pageInfo._id,
  104. exit_page_id: pageInfo._id,
  105. page_count: 0,
  106. event_count: 0,
  107. duration: 1,
  108. // 版本
  109. sdk_version: params.mpsdk ? params.mpsdk : '',
  110. platform_version: params.mpv ? params.mpv : '',
  111. // 设备相关
  112. device_os_name: params.on ? params.on : platform.getOsName(params.p),
  113. device_os_version: params.sv ? params.sv : '',
  114. device_vendor: params.brand ? params.brand : '',
  115. device_model: params.md ? params.md : '',
  116. device_language: params.lang ? params.lang : '',
  117. device_pixel_ratio: params.pr ? params.pr : '',
  118. device_window_width: params.ww ? params.ww : '',
  119. device_window_height: params.wh ? params.wh : '',
  120. device_screen_width: params.sw ? params.sw : '',
  121. device_screen_height: params.sh ? params.sh : '',
  122. // 地区相关
  123. location_ip: params.ip ? params.ip : '',
  124. location_latitude: params.lat ? parseFloat(params.lat) : -1,
  125. location_longitude: params.lng ? parseFloat(params.lng) : -1,
  126. location_country: params.cn ? params.cn : '',
  127. location_province: params.pn ? params.pn : '',
  128. location_city: params.ct ? params.ct : '',
  129. is_finish: 0,
  130. create_time: nowTime
  131. }
  132. if(this.isHaveOldDeviceId(params)) {
  133. fillParams.old_device_id = params.odid
  134. }
  135. const res = await this.insert(this.tableName, fillParams)
  136. if (res && res.id) {
  137. //填充用户的会话日志
  138. if (params.uid) {
  139. await new UserSessionLog().fill({
  140. ...params,
  141. page_id: pageInfo._id,
  142. sid: res.id
  143. })
  144. }
  145. return {
  146. code: 0,
  147. msg: 'success',
  148. data: {
  149. pageId: pageInfo._id,
  150. sessionLogId: res.id,
  151. entryPageId: fillParams.entry_page_id,
  152. eventCount: fillParams.event_count,
  153. startTime: fillParams.first_visit_time,
  154. createTime: fillParams.create_time,
  155. pageCount: fillParams.page_count,
  156. uid: fillParams.last_visit_user_id
  157. }
  158. }
  159. } else {
  160. return {
  161. code: 500,
  162. msg: 'Session log filled error'
  163. }
  164. }
  165. }
  166. /**
  167. * 判断是否包含老版本sdk生成的device_id, 此功能用于处理前端sdk升级后 device_id 发生变化导致日活、留存数据不准确的问题
  168. * @param {Object} params
  169. */
  170. isHaveOldDeviceId(params) {
  171. if(params.odid && params.odid !== params.did && params.odid !== 'YluY92BA6nJ6NfixI77sFQ%3D%3D&ie=1') {
  172. console.log('params.odid', params.odid)
  173. return true
  174. }
  175. return false
  176. }
  177. /**
  178. * 获取会话
  179. * @param {Object} params 上报参数
  180. */
  181. async getSession(params) {
  182. // 页面信息
  183. const page = new Page()
  184. const pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj)
  185. if (!pageInfo || pageInfo.length === 0) {
  186. return {
  187. code: 300,
  188. msg: 'Not found this entry page'
  189. }
  190. }
  191. const platformObj = new Platform()
  192. const platform = platformObj.getPlatformCode(params.ut, params.p)
  193. // 查询日志
  194. const sessionLogInfo = await this.getCollection(this.tableName).where({
  195. appid: params.ak,
  196. platform: platform,
  197. device_id: params.did,
  198. is_finish: 0
  199. }).orderBy('create_time', 'desc').limit(1).get()
  200. if (sessionLogInfo.data.length > 0) {
  201. const userSessionLog = new UserSessionLog()
  202. const sessionLogInfoData = sessionLogInfo.data[0]
  203. // 最后一次访问时间距现在超过半小时算上次会话已结束并生成一次新的会话
  204. let sessionExpireTime = this.getConfig('sessionExpireTime')
  205. sessionExpireTime = sessionExpireTime ? sessionExpireTime : 1800
  206. const sessionTime = new DateTime().getTime() - sessionLogInfoData.last_visit_time
  207. if (sessionTime >= sessionExpireTime * 1000) {
  208. if (this.debug) {
  209. console.log('session log time expired', sessionTime)
  210. }
  211. await this.update(this.tableName, {
  212. is_finish: 1
  213. }, {
  214. appid: params.ak,
  215. platform: platform,
  216. device_id: params.did,
  217. is_finish: 0
  218. })
  219. //关闭用户会话
  220. await userSessionLog.closeUserSession()
  221. return await this.fill(params)
  222. } else {
  223. //如果当前会话切换了用户则生成新的用户会话
  224. if (params.uid != sessionLogInfoData.last_visit_user_id) {
  225. await userSessionLog.checkUserSession({
  226. ...params,
  227. page_id: pageInfo._id,
  228. sid: sessionLogInfoData._id,
  229. last_visit_user_id: sessionLogInfoData.last_visit_user_id
  230. })
  231. await this.update(this.tableName, {
  232. last_visit_user_id: params.uid ? params.uid : ''
  233. }, {
  234. _id: sessionLogInfoData._id
  235. })
  236. }
  237. return {
  238. code: 0,
  239. msg: 'success',
  240. data: {
  241. pageId: pageInfo._id,
  242. sessionLogId: sessionLogInfoData._id,
  243. entryPageId: sessionLogInfoData.entry_page_id,
  244. eventCount: sessionLogInfoData.event_count,
  245. startTime: sessionLogInfoData.first_visit_time,
  246. createTime: sessionLogInfoData.create_time,
  247. pageCount: sessionLogInfoData.page_count,
  248. uid: sessionLogInfoData.last_visit_user_id
  249. }
  250. }
  251. }
  252. } else {
  253. return await this.fill(params)
  254. }
  255. }
  256. /**
  257. * 更新会话信息
  258. * @param {String} sid 会话编号
  259. * @param {Object} data 更新数据
  260. */
  261. async updateSession(sid, data) {
  262. const nowTime = new DateTime().getTime()
  263. const accessTime = nowTime - data.createTime
  264. const accessSenconds = accessTime > 1000 ? parseInt(accessTime / 1000) : 1
  265. const updateData = {
  266. last_visit_time: nowTime,
  267. duration: accessSenconds,
  268. }
  269. //访问页面数量
  270. if (data.addPageCount) {
  271. updateData.page_count = data.pageCount
  272. }
  273. //最终访问的页面编号
  274. if (data.pageId) {
  275. updateData.exit_page_id = data.pageId
  276. }
  277. //产生事件次数
  278. if (data.eventCount) {
  279. updateData.event_count = data.eventCount
  280. }
  281. if (this.debug) {
  282. console.log('update session log by sid-' + sid, updateData)
  283. }
  284. //更新会话
  285. await this.update(this.tableName, updateData, {
  286. _id: sid
  287. })
  288. //更新用户会话
  289. if (data.uid) {
  290. data.nowTime = nowTime
  291. await new UserSessionLog().updateUserSession(sid, data)
  292. }
  293. return true
  294. }
  295. /**
  296. * 清理日志数据
  297. * @param {Number} days 保留天数, 留存统计需要计算30天后留存率,因此至少应保留31天的日志数据
  298. */
  299. async clean(days) {
  300. days = Math.max(parseInt(days), 1)
  301. console.log('clean session logs - day:', days)
  302. const dateTime = new DateTime()
  303. const res = await this.delete(this.tableName, {
  304. create_time: {
  305. $lt: dateTime.getTimeBySetDays(0 - days)
  306. }
  307. })
  308. if (!res.code) {
  309. console.log('clean session log:', res)
  310. }
  311. return res
  312. }
  313. }