feat: 同步管理端、用户端与后端业务,并接通线上 Web 接口
补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
这个提交包含在:
@@ -103,3 +103,97 @@ test('5. 页面 locale 必须覆盖筛选与列文案,且禁止 any', () => {
|
||||
assert.ok(content.includes('radarLog.columns.viewedBlocks'), '必须有 viewedBlocks 列文案');
|
||||
assert.ok(!/\bany\b/.test(content), 'locale 禁止使用 any');
|
||||
});
|
||||
|
||||
test('6. 深度访问高亮:停留 ≥180000ms 或滚动深度 ≥0.6;缺字段不高亮', () => {
|
||||
const content = fs.readFileSync(radarLogPagePath, 'utf-8');
|
||||
|
||||
assert.ok(
|
||||
content.includes('180000'),
|
||||
'必须包含停留时长阈值 180000 毫秒'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('0.6'),
|
||||
'必须包含滚动深度阈值 0.6'
|
||||
);
|
||||
assert.ok(
|
||||
/isDeepVisit|deepVisit/.test(content),
|
||||
'必须具备 isDeepVisit / deepVisit 判定'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('rowClassName') || content.includes('onRow'),
|
||||
'必须通过 rowClassName 或 onRow 对深度访问行高亮'
|
||||
);
|
||||
// 缺字段:须对 stayDurationMs / scrollDepth 做有限数值校验后再比阈值
|
||||
assert.ok(
|
||||
/Number\.isFinite|typeof[\s\S]{0,40}number/.test(content),
|
||||
'字段缺失或非有限数时不得误判为深度访问'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes("t['radarLog.deepVisit']") ||
|
||||
content.includes('radarLog.deepVisit'),
|
||||
'深度访问文案必须走 locale'
|
||||
);
|
||||
});
|
||||
|
||||
test('7. locale 必须含深度访问文案;页面禁止写死国家或示例访客', () => {
|
||||
const pageContent = fs.readFileSync(radarLogPagePath, 'utf-8');
|
||||
const localeContent = fs.readFileSync(radarLogLocalePath, 'utf-8');
|
||||
|
||||
assert.ok(
|
||||
localeContent.includes("'radarLog.deepVisit'"),
|
||||
'locale 必须包含 radarLog.deepVisit'
|
||||
);
|
||||
assert.ok(localeContent.includes("'en-US'"), 'locale 必须含 en-US');
|
||||
assert.ok(localeContent.includes("'zh-CN'"), 'locale 必须含 zh-CN');
|
||||
assert.ok(localeContent.includes("'ar-SA'"), 'locale 必须含 ar-SA');
|
||||
|
||||
const bannedCountries = [
|
||||
'United States',
|
||||
'United Kingdom',
|
||||
'Singapore',
|
||||
'Germany',
|
||||
'Japan',
|
||||
'China',
|
||||
'Dubai',
|
||||
'Saudi Arabia',
|
||||
];
|
||||
for (const name of bannedCountries) {
|
||||
assert.ok(!pageContent.includes(name), `禁止硬编码国家/地区名: ${name}`);
|
||||
}
|
||||
assert.ok(
|
||||
!pageContent.includes('示例访客') &&
|
||||
!pageContent.includes('sample-visitor') &&
|
||||
!pageContent.includes('visitor_demo') &&
|
||||
!pageContent.includes('Alex') &&
|
||||
!/visitorId\s*:\s*['"][^'"]+['"]/.test(pageContent),
|
||||
'禁止写死示例访客行'
|
||||
);
|
||||
assert.ok(
|
||||
!/useState\s*<\s*RadarLogRow\s*\[\s*\]\s*>\s*\(\s*\[[^\]]+visitorId/.test(
|
||||
pageContent
|
||||
) &&
|
||||
!/const\s+MOCK_|const\s+SAMPLE_|const\s+DEMO_/.test(pageContent),
|
||||
'禁止用示例访客初始化表格数据'
|
||||
);
|
||||
});
|
||||
|
||||
test('8. 请求失败必须空表,不得残留示例访客', () => {
|
||||
const content = fs.readFileSync(radarLogPagePath, 'utf-8');
|
||||
|
||||
assert.ok(content.includes('setData([])'), '失败必须 setData([])');
|
||||
assert.ok(
|
||||
/useState[<\s(][^)]*RadarLogRow[^)]*\)?\s*\(\s*\[\s*\]\s*\)/.test(content) ||
|
||||
/useState\s*<\s*RadarLogRow\s*\[\s*\]\s*>\s*\(\s*\[\s*\]\s*\)/.test(content),
|
||||
'初始 data 必须为空数组'
|
||||
);
|
||||
// 非 1000 与 catch 均应清空
|
||||
assert.ok(
|
||||
/code\s*===\s*1000[\s\S]*?setData\(\[\]\)/.test(content) ||
|
||||
(content.includes('setData([])') && content.includes('catch')),
|
||||
'code 非 1000 或 catch 必须清空表格'
|
||||
);
|
||||
assert.ok(
|
||||
!content.includes('Mock.js') && !content.includes('mockList'),
|
||||
'失败路径禁止留下 mock/示例列表'
|
||||
);
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户