@jx3box/jx3box-ui 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agents/client-stat.md +4 -1
- package/package.json +1 -1
- package/src/utils/client-stat.js +23 -12
- package/tests/client-stat.test.js +18 -1
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
`CommonHeader` 为引用公共头的普通 PC 项目接入新版客户端实例 heartbeat。
|
|
4
4
|
|
|
5
5
|
- 接口:`POST /api/cms/system/stat/heartbeat`。
|
|
6
|
-
- SDK 版本:当前固定为 `v0.0.
|
|
6
|
+
- SDK 版本:当前固定为 `v0.0.2`,只在统计组件协议升级时调整。
|
|
7
7
|
- 实例 ID:复用本地 `jx3box:device_id` 随机 UUID;不使用硬件指纹。
|
|
8
8
|
- 普通桌面浏览器为 `client=pc_web`;手机浏览器访问 PC 页面为 `client=mobile_web`。
|
|
9
9
|
- 小程序和 App 容器按统一客户端枚举识别;游戏内页面的 `pc_game/mobile_game` 由对应页面以后主动指定,不在公共头推断。
|
|
10
|
+
- 通用客户端识别与 JX3BOX 业务规则分离:`src/utils/client-stat-business.js` 负责项目专属覆盖;UA 包含 `jianghudaily` 时不区分大小写地上报 `mobile_game`,以后抽公共包时以业务适配器注入。
|
|
10
11
|
- UID 不进入 payload,由 service-cms 从可选 JWT 中读取;IP 同样由服务端反代请求头读取。
|
|
11
12
|
- 生产环境启动后随机延迟 3~15 秒,同一北京时间日期、UID 和 Web 版本只成功上报一次。
|
|
13
|
+
- Web 版本按 `window.__APP_VERSION__`、`process.env.VUE_APP_VERSION`、`process.env.VITE_APP_VERSION` 依次降级读取;均不存在时使用 `unknown` 参与本地签名,并省略 heartbeat payload 中的 `web_version`。
|
|
12
14
|
- 页面恢复可见时补检;请求失败不写成功标记,也不影响公共头和宿主页面。
|
|
13
15
|
- localhost 每次公共头重新创建都强制上报,方便联调。
|
|
16
|
+
- UA 命中 ArkWeb/HarmonyOS/OpenHarmony 时识别为 `platform=harmony`;明确的 `Phone/Tablet; OpenHarmony x.x` 会在上报前规范为 `os_name=OpenHarmony`、对应版本和设备类型,不保留设备型号或完整固件展示串。
|
package/package.json
CHANGED
package/src/utils/client-stat.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { $cms } from "@jx3box/jx3box-common/js/api";
|
|
2
2
|
import { isMiniProgram, isApp } from "@jx3box/jx3box-common/js/utils";
|
|
3
3
|
import User from "@jx3box/jx3box-common/js/user";
|
|
4
|
+
import { applyJx3boxClientStatRules } from "./client-stat-business";
|
|
4
5
|
|
|
5
6
|
const INSTANCE_ID_KEY = "jx3box:device_id";
|
|
6
7
|
const REPORT_DATE_KEY = "jx3box:client-stat-report-date";
|
|
7
8
|
const REPORT_SIGNATURE_KEY = "jx3box:client-stat-report-signature";
|
|
8
|
-
const STAT_SDK_VERSION = "v0.0.
|
|
9
|
+
const STAT_SDK_VERSION = "v0.0.2";
|
|
9
10
|
const STARTUP_DELAY_MIN = 3 * 1000;
|
|
10
11
|
const STARTUP_DELAY_MAX = 15 * 1000;
|
|
11
12
|
|
|
@@ -69,6 +70,7 @@ function resolveChannel() {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
function resolvePlatform(userAgent, navigatorPlatform) {
|
|
73
|
+
if (/ArkWeb|HarmonyOS|OpenHarmony/i.test(userAgent)) return "harmony";
|
|
72
74
|
if (/iPhone|iPad|iPod/i.test(userAgent)) return "ios";
|
|
73
75
|
if (/Android/i.test(userAgent)) return "android";
|
|
74
76
|
if (/Windows/i.test(userAgent)) return "windows";
|
|
@@ -77,6 +79,16 @@ function resolvePlatform(userAgent, navigatorPlatform) {
|
|
|
77
79
|
return "unknown";
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
function normalizeHarmonySystem(platform, userAgent) {
|
|
83
|
+
if (platform !== "harmony") return { os_name: platform, os_version: null };
|
|
84
|
+
const openHarmony = String(userAgent || "").match(/\b(?:Phone|Tablet);\s*OpenHarmony\s+([0-9]+(?:\.[0-9]+)*)\b/i);
|
|
85
|
+
if (openHarmony) return { os_name: "OpenHarmony", os_version: openHarmony[1] };
|
|
86
|
+
return {
|
|
87
|
+
os_name: /HarmonyOS/i.test(userAgent) ? "HarmonyOS" : "OpenHarmony",
|
|
88
|
+
os_version: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
function resolveClient(platform, userAgent) {
|
|
81
93
|
if (isApp()) return "app";
|
|
82
94
|
if (isMiniProgram() || /miniprogram/i.test(userAgent)) return "miniprogram";
|
|
@@ -130,13 +142,7 @@ function resolveDisplayMode() {
|
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
function resolveWebVersion() {
|
|
133
|
-
return
|
|
134
|
-
window.__JX3BOX_VERSION__ ||
|
|
135
|
-
window.__APP_VERSION__ ||
|
|
136
|
-
process.env.VUE_APP_VERSION ||
|
|
137
|
-
process.env.VUE_APP_BUILD_VERSION ||
|
|
138
|
-
null
|
|
139
|
-
);
|
|
145
|
+
return window.__APP_VERSION__ || process.env.VUE_APP_VERSION || process.env.VITE_APP_VERSION || null;
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
export function buildClientStatPayload(instanceId) {
|
|
@@ -145,20 +151,25 @@ export function buildClientStatPayload(instanceId) {
|
|
|
145
151
|
const connection = nav.connection || nav.mozConnection || nav.webkitConnection || {};
|
|
146
152
|
const userAgent = String(nav.userAgent || "");
|
|
147
153
|
const platform = resolvePlatform(userAgent, nav.platform);
|
|
148
|
-
const
|
|
154
|
+
const context = applyJx3boxClientStatRules(
|
|
155
|
+
{ platform, client: resolveClient(platform, userAgent) },
|
|
156
|
+
{ userAgent }
|
|
157
|
+
);
|
|
149
158
|
const browser = parseBrowser(userAgent);
|
|
159
|
+
const system = normalizeHarmonySystem(context.platform, userAgent);
|
|
150
160
|
|
|
151
161
|
return compactPayload({
|
|
152
162
|
instance_id: instanceId,
|
|
153
163
|
product: "jx3box",
|
|
154
|
-
client,
|
|
155
|
-
platform,
|
|
164
|
+
client: context.client,
|
|
165
|
+
platform: context.platform,
|
|
156
166
|
domain: window.location.hostname,
|
|
157
167
|
channel: resolveChannel(),
|
|
158
168
|
web_version: resolveWebVersion(),
|
|
159
169
|
sdk_version: STAT_SDK_VERSION,
|
|
160
170
|
|
|
161
|
-
os_name:
|
|
171
|
+
os_name: system.os_name,
|
|
172
|
+
os_version: system.os_version,
|
|
162
173
|
device_type: ["ios", "android"].includes(platform) ? (/iPad|Tablet/i.test(userAgent) ? "tablet" : "phone") : "desktop",
|
|
163
174
|
architecture: /arm64|aarch64/i.test(userAgent)
|
|
164
175
|
? "arm64"
|
|
@@ -4,6 +4,7 @@ const path = require("path");
|
|
|
4
4
|
const root = path.resolve(__dirname, "..");
|
|
5
5
|
const commonHeader = fs.readFileSync(path.join(root, "src/CommonHeader.vue"), "utf8");
|
|
6
6
|
const clientStat = fs.readFileSync(path.join(root, "src/utils/client-stat.js"), "utf8");
|
|
7
|
+
const clientStatBusiness = fs.readFileSync(path.join(root, "src/utils/client-stat-business.js"), "utf8");
|
|
7
8
|
|
|
8
9
|
function assert(condition, message) {
|
|
9
10
|
if (!condition) throw new Error(message);
|
|
@@ -16,9 +17,25 @@ assert(
|
|
|
16
17
|
);
|
|
17
18
|
assert(clientStat.includes('const INSTANCE_ID_KEY = "jx3box:device_id"'), "PC should reuse the client instance id key");
|
|
18
19
|
assert(clientStat.includes('product: "jx3box"'), "heartbeat should identify the JX3BOX product");
|
|
19
|
-
assert(clientStat.includes('const STAT_SDK_VERSION = "v0.0.
|
|
20
|
+
assert(clientStat.includes('const STAT_SDK_VERSION = "v0.0.2"'), "statistics SDK version should remain explicit");
|
|
20
21
|
assert(clientStat.includes('return "pc_web"'), "desktop browser traffic should report pc_web");
|
|
21
22
|
assert(clientStat.includes('return "mobile_web"'), "mobile browsers visiting PC pages should report mobile_web");
|
|
23
|
+
assert(
|
|
24
|
+
clientStat.includes("applyJx3boxClientStatRules"),
|
|
25
|
+
"generic client detection should pass through the JX3BOX business rule adapter"
|
|
26
|
+
);
|
|
27
|
+
assert(clientStatBusiness.includes("/jianghudaily/i"), "Jianghu Daily UA matching should be case-insensitive");
|
|
28
|
+
assert(clientStatBusiness.includes('client: "mobile_game"'), "Jianghu Daily should report mobile_game");
|
|
29
|
+
assert(
|
|
30
|
+
clientStat.includes("/ArkWeb|HarmonyOS|OpenHarmony/i") && clientStat.includes('os_name: "OpenHarmony"'),
|
|
31
|
+
"PC statistics should normalize OpenHarmony before reporting"
|
|
32
|
+
);
|
|
33
|
+
assert(
|
|
34
|
+
clientStat.includes("window.__APP_VERSION__ || process.env.VUE_APP_VERSION || process.env.VITE_APP_VERSION"),
|
|
35
|
+
"web version should use the shared app version fallback chain"
|
|
36
|
+
);
|
|
37
|
+
assert(!clientStat.includes("__JX3BOX_VERSION__"), "shared statistics must not depend on a JX3BOX-specific version global");
|
|
38
|
+
assert(!clientStat.includes("VUE_APP_BUILD_VERSION"), "web version should not use the deprecated build-version fallback");
|
|
22
39
|
assert(clientStat.includes("/api/cms/system/stat/heartbeat"), "heartbeat should use the new statistics endpoint");
|
|
23
40
|
assert(clientStat.includes("$cms({ mute: true })"), "heartbeat should reuse the authenticated CMS client");
|
|
24
41
|
assert(
|