analytics-front-sdk.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /**
  2. * 平台埋点 SDK(uni-app / Vue 通用)
  3. *
  4. * 使用方式:
  5. * 1. 复制到前端项目 utils/analytics.js
  6. * 2. 初始化:Analytics.init({ baseUrl: 'https://api.xxx.com', getToken: () => uni.getStorageSync('token') })
  7. * 3. 上报:Analytics.report('MERCHANT_VIEW', { merchantId: 1001 })
  8. *
  9. * 注意:请勿再调用旧接口 POST /track/event
  10. */
  11. let config = {
  12. baseUrl: '',
  13. getToken: () => '',
  14. channel: '',
  15. city: '',
  16. };
  17. function uuid() {
  18. return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
  19. const r = (Math.random() * 16) | 0;
  20. const v = c === 'x' ? r : (r & 0x3) | 0x8;
  21. return v.toString(16);
  22. });
  23. }
  24. function getDeviceType() {
  25. // #ifdef APP-PLUS
  26. const platform = uni.getSystemInfoSync().platform;
  27. if (platform === 'ios') return 'IOS';
  28. if (platform === 'android') return 'ANDROID';
  29. // #endif
  30. // #ifdef MP-WEIXIN
  31. return 'MINI_PROGRAM';
  32. // #endif
  33. return 'H5';
  34. }
  35. function request(url, data) {
  36. return new Promise((resolve, reject) => {
  37. uni.request({
  38. url: `${config.baseUrl}${url}`,
  39. method: 'POST',
  40. data,
  41. header: {
  42. 'Content-Type': 'application/json',
  43. Authorization: config.getToken() || '',
  44. },
  45. success: (res) => {
  46. if (res.data && res.data.success) {
  47. resolve(res.data);
  48. } else {
  49. reject(res.data || res);
  50. }
  51. },
  52. fail: reject,
  53. });
  54. });
  55. }
  56. const Analytics = {
  57. init(options = {}) {
  58. config = { ...config, ...options };
  59. },
  60. /**
  61. * 统一埋点上报
  62. * @param {string} scene 场景码,如 MERCHANT_VIEW
  63. * @param {object} payload 业务参数
  64. */
  65. report(scene, payload = {}) {
  66. const body = {
  67. scene,
  68. eventId: uuid(),
  69. userId: payload.userId,
  70. merchantId: payload.merchantId,
  71. targetId: payload.targetId || payload.contentId,
  72. contentType: payload.contentType,
  73. amount: payload.amount,
  74. durationMs: payload.durationMs,
  75. deviceType: payload.deviceType || getDeviceType(),
  76. channel: payload.channel || config.channel,
  77. city: payload.city || config.city,
  78. };
  79. return request('/analytics/front/report', body);
  80. },
  81. /** 批量上报 */
  82. batch(events) {
  83. const list = (events || []).map((item) => ({
  84. scene: item.scene,
  85. eventId: uuid(),
  86. userId: item.userId,
  87. merchantId: item.merchantId,
  88. targetId: item.targetId || item.contentId,
  89. contentType: item.contentType,
  90. amount: item.amount,
  91. durationMs: item.durationMs,
  92. deviceType: item.deviceType || getDeviceType(),
  93. channel: item.channel || config.channel,
  94. city: item.city || config.city,
  95. }));
  96. return request('/analytics/front/batch', { events: list });
  97. },
  98. /** 在线心跳,durationMs 为距上次心跳的毫秒数;userId 必填 */
  99. heartbeat(durationMs, extra = {}) {
  100. const userId = typeof extra.userId === 'function' ? extra.userId() : extra.userId;
  101. return request('/analytics/front/heartbeat', {
  102. userId,
  103. durationMs,
  104. deviceType: extra.deviceType || getDeviceType(),
  105. channel: extra.channel || config.channel,
  106. city: extra.city || config.city,
  107. });
  108. },
  109. /** App 启动 */
  110. onLaunch(extra = {}) {
  111. return this.report('APP_LAUNCH', extra);
  112. },
  113. /** 商家浏览埋点 */
  114. merchantView(data) {
  115. return request('/analytics/front/merchant/view', {
  116. eventId: data.eventId || uuid(),
  117. merchantId: data.merchantId,
  118. shopType: data.shopType,
  119. visitUv: data.visitUv,
  120. visitPv: data.visitPv,
  121. userId: data.userId,
  122. });
  123. },
  124. /** 访问商户(委托 merchantView) */
  125. onMerchantView(merchantId, extra = {}) {
  126. return this.merchantView({
  127. merchantId,
  128. shopType: extra.shopType,
  129. visitUv: extra.visitUv != null ? extra.visitUv : 0,
  130. visitPv: extra.visitPv != null ? extra.visitPv : 1,
  131. userId: extra.userId,
  132. eventId: extra.eventId,
  133. });
  134. },
  135. /** 内容互动(点赞/评论/收藏),increment 默认 +1 */
  136. onContentInteract(contentId, contentType, extra = {}) {
  137. return this.contentInteract({
  138. contentId,
  139. contentType,
  140. increment: extra.increment != null ? extra.increment : 1,
  141. userId: extra.userId,
  142. eventId: extra.eventId,
  143. });
  144. },
  145. /** 内容互动埋点 */
  146. contentInteract(data) {
  147. return request('/analytics/front/content/interact', {
  148. eventId: data.eventId || uuid(),
  149. contentId: data.contentId,
  150. contentType: data.contentType,
  151. increment: data.increment,
  152. userId: data.userId,
  153. });
  154. },
  155. /** AI 请求上报 */
  156. aiRequest(data) {
  157. return request('/analytics/front/ai-request', {
  158. requestId: data.requestId || uuid(),
  159. apiName: data.apiName,
  160. apiUrl: data.apiUrl,
  161. responseDurationMs: data.responseDurationMs,
  162. isTimeout: data.isTimeout,
  163. });
  164. },
  165. /** 用户注册埋点 */
  166. userRegister(data) {
  167. return request('/analytics/front/user/register', {
  168. eventId: data.eventId || uuid(),
  169. userId: data.userId,
  170. userPhone: data.userPhone,
  171. registerTime: data.registerTime,
  172. channel: data.channel,
  173. city: data.city,
  174. });
  175. },
  176. /** 用户登录埋点 */
  177. userLogin(data) {
  178. return request('/analytics/front/user/login', {
  179. eventId: data.eventId || uuid(),
  180. userId: data.userId,
  181. firstLaunchTime: data.firstLaunchTime,
  182. lastActiveTime: data.lastActiveTime,
  183. city: data.city,
  184. deviceName: data.deviceName,
  185. });
  186. },
  187. /** 用户登出埋点 */
  188. userLogout(data) {
  189. return request('/analytics/front/user/logout', {
  190. eventId: data.eventId || uuid(),
  191. userId: data.userId,
  192. firstLaunchTime: data.firstLaunchTime,
  193. lastActiveTime: data.lastActiveTime,
  194. city: data.city,
  195. deviceName: data.deviceName,
  196. onlineDurationMin: data.onlineDurationMin,
  197. });
  198. },
  199. /** AI 对话结束埋点 */
  200. aiChatEnd(data) {
  201. return request('/analytics/front/ai-chat/end', {
  202. eventId: data.eventId || uuid(),
  203. chatId: data.chatId,
  204. userId: data.userId,
  205. startTime: data.startTime,
  206. messageCount: data.messageCount,
  207. aiResponseDurationMs: data.aiResponseDurationMs,
  208. });
  209. },
  210. /** 内容发布埋点 */
  211. contentPublish(data) {
  212. return request('/analytics/front/content/publish', {
  213. eventId: data.eventId || uuid(),
  214. contentId: data.contentId,
  215. contentType: data.contentType,
  216. contentTitle: data.contentTitle,
  217. businessCategory: data.businessCategory,
  218. authorType: data.authorType,
  219. authorId: data.authorId,
  220. publishTime: data.publishTime,
  221. });
  222. },
  223. /** 启动心跳定时器,默认60秒;extra.userId 可传固定值或 getter 函数 */
  224. startHeartbeat(intervalMs = 60000, extra = {}) {
  225. if (this._heartbeatTimer) {
  226. clearInterval(this._heartbeatTimer);
  227. }
  228. let last = Date.now();
  229. this._heartbeatTimer = setInterval(() => {
  230. const now = Date.now();
  231. this.heartbeat(now - last, extra).catch(() => {});
  232. last = now;
  233. }, intervalMs);
  234. },
  235. stopHeartbeat() {
  236. if (this._heartbeatTimer) {
  237. clearInterval(this._heartbeatTimer);
  238. this._heartbeatTimer = null;
  239. }
  240. },
  241. };
  242. export default Analytics;