fix(compliance): 清理全站首屏指标与矢量原型图中的虚构数据,全面规范广告法合规表述
这个提交包含在:
@@ -129,6 +129,50 @@ class TestAdLawCompliance(unittest.TestCase):
|
||||
for metric in unsubstantiated_metrics:
|
||||
self.assertNotIn(metric, self.all_text, f"页面中严禁无权威依据编造成效百分比: '{metric}'")
|
||||
|
||||
def test_svg_showcases_compliance(self):
|
||||
"""排查 public/assets/addons/official/images/showcases/ 目录下所有原型 SVG 是否包含虚假宣传数据"""
|
||||
svg_dir = os.path.join(ROOT_DIR, 'public/assets/addons/official/images/showcases')
|
||||
self.assertTrue(os.path.exists(svg_dir), f"SVG 展示目录不存在: {svg_dir}")
|
||||
|
||||
# 针对每个 SVG 定义违规关键词
|
||||
svg_forbidden_rules = {
|
||||
'case_distrib.svg': [
|
||||
'12,890,400', '65%', '3,420', '10W', 'QPS 12,800', '720 天', '0.02%', '全 5 星评价', '零故障'
|
||||
],
|
||||
'case_catering.svg': [
|
||||
'300+', '99.9%', '70%', '零丢单', '28%', '300 家', '3 年', '彻底解决'
|
||||
],
|
||||
'case_manufacture.svg': [
|
||||
'42%', '68%', '2 天', '86%', '92%', '0 误差', '标杆示范项目'
|
||||
],
|
||||
'case_gov_agent.svg': [
|
||||
'80,000+', '94.6%', '0 泄露', '零泄密'
|
||||
],
|
||||
'product_mall.svg': [
|
||||
'389,450', '18.4%', '4,982', '99.8%'
|
||||
],
|
||||
'product_erp.svg': [
|
||||
'142,800', '72%', '周转率提升 42%'
|
||||
],
|
||||
'product_saas.svg': [
|
||||
'99.99%', '34.2%', '42.8%'
|
||||
],
|
||||
'service_agent.svg': [
|
||||
'96.8%', '85%+'
|
||||
],
|
||||
'service_middle.svg': [
|
||||
'12,800', '89.4%'
|
||||
]
|
||||
}
|
||||
|
||||
for filename, forbidden_list in svg_forbidden_rules.items():
|
||||
svg_path = os.path.join(svg_dir, filename)
|
||||
if os.path.exists(svg_path):
|
||||
with open(svg_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
for forbidden in forbidden_list:
|
||||
self.assertNotIn(forbidden, content, f"SVG 文件 {filename} 中严禁包含虚假宣传或夸大编造数据: '{forbidden}'")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
@@ -23,14 +23,16 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
self.seed_sql_path = os.path.join(BASE_DIR, 'database', 'seeds', 'journalism_articles.sql')
|
||||
|
||||
def test_pc_list_template_spec(self):
|
||||
"""验证 PC 端文章资讯列表页规范"""
|
||||
"""验证 PC 端文章资讯列表页规范(纯标题列表模式)"""
|
||||
self.assertTrue(os.path.exists(self.pc_list_path), "PC 文章列表模板 list.html 必须存在")
|
||||
content = open(self.pc_list_path, 'r', encoding='utf-8').read()
|
||||
self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】")
|
||||
self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】")
|
||||
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', content), "严禁包含任何 emoji")
|
||||
self.assertIn("nhc-article-hero", content, "必须包含现代中式 Hero Banner 容器类")
|
||||
self.assertIn("nhc-article-card", content, "必须包含现代中式文章卡片样式类")
|
||||
self.assertIn("nhc-title-list", content, "必须包含现代中式纯标题列表容器类 nhc-title-list")
|
||||
self.assertIn("nhc-title-item", content, "必须包含纯标题列表单项类 nhc-title-item")
|
||||
self.assertNotIn("nhc-article-card-thumb", content, "文章列表已切换为纯标题列表,严禁展示冗长的大封面图")
|
||||
self.assertIn("render", content, "必须包含分页渲染组件")
|
||||
|
||||
def test_pc_details_template_spec(self):
|
||||
@@ -51,7 +53,7 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
list_content = open(self.mobile_list_path, 'r', encoding='utf-8').read()
|
||||
details_content = open(self.mobile_details_path, 'r', encoding='utf-8').read()
|
||||
|
||||
# 修复历史遗留 bug:循环中错误使用 $item['id'] 替换为正确的遍历项变量
|
||||
self.assertIn("nhc-title-list", list_content, "移动端列表必须包含纯标题列表结构")
|
||||
self.assertNotIn("official_video/list", details_content, "详情页严禁遗留旧 official_video/list 错误路径")
|
||||
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', list_content), "移动端列表严禁 emoji")
|
||||
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', details_content), "移动端详情严禁 emoji")
|
||||
@@ -59,11 +61,12 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
def test_css_design_tokens_for_articles(self):
|
||||
"""验证 CSS 中已包含文章与资讯专属样式定义"""
|
||||
css = open(self.css_path, 'r', encoding='utf-8').read()
|
||||
self.assertIn(".nhc-article-card", css, "CSS 必须定义 .nhc-article-card 样式")
|
||||
self.assertIn(".nhc-title-list", css, "CSS 必须定义 .nhc-title-list 样式")
|
||||
self.assertIn(".nhc-title-item", css, "CSS 必须定义 .nhc-title-item 样式")
|
||||
self.assertIn(".nhc-article-body", css, "CSS 必须定义 .nhc-article-body 样式")
|
||||
|
||||
def test_seed_sql_rich_articles(self):
|
||||
"""验证数据库种子包含不少于 8 篇扎实深度专业文章"""
|
||||
"""验证数据库种子包含不少于 38 篇扎实深度专业文章"""
|
||||
self.assertTrue(os.path.exists(self.seed_sql_path), "数据库种子文件 journalism_articles.sql 必须存在")
|
||||
sql = open(self.seed_sql_path, 'r', encoding='utf-8').read()
|
||||
|
||||
@@ -77,15 +80,13 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
# 验证核心业务主题覆盖
|
||||
self.assertIn("微服务", sql, "必须覆盖微服务架构深度文章")
|
||||
self.assertIn("智能体", sql, "必须覆盖 AI 智能体/大模型与知识库落地文章")
|
||||
self.assertIn("进销存", sql, "必须覆盖全链路进销存与业财一体化文章")
|
||||
self.assertIn("收银", sql, "必须覆盖智慧收银容灾与离线记账机制文章")
|
||||
self.assertIn("测试驱动开发", sql, "必须覆盖 TDD 测试驱动开发规范文章")
|
||||
self.assertTrue("代码" in sql or "软件质量" in sql, "必须覆盖代码规范与交付文章")
|
||||
self.assertIn("缓存", sql, "必须覆盖分布式缓存相关深度文章")
|
||||
self.assertIn("分库分表", sql, "必须覆盖数据库分库分表相关文章")
|
||||
self.assertIn("先进先出", sql, "必须覆盖批次库存先进先出算法相关文章")
|
||||
|
||||
# 严禁假大空与 AI 味营销词汇
|
||||
for bad_word in ["赋能千行百业", "颠覆式创新", "颠覆传统", "未来已来", "数字化新纪元"]:
|
||||
for bad_word in ["赋能千行百业", "颠覆式创新", "颠覆传统", "未来已来", "数字化新纪元", "技术委员会", "研发委员会", "原创技术资产", "工程受控说明"]:
|
||||
self.assertNotIn(bad_word, sql, f"文章种子严禁包含 AI 味空话:{bad_word}")
|
||||
|
||||
def test_home_and_footer_linkage(self):
|
||||
|
||||
@@ -46,7 +46,16 @@ class TestNaturalCopywriting(unittest.TestCase):
|
||||
'开启您的企业数字化与智能化升级项目', # 泛化套话
|
||||
'开启确定性数字化技术合作', # 虚词
|
||||
'严谨商务 · 现代中式 · 高级感专业软件服务', # 泄露的设计Prompt
|
||||
'现代中式 · 高级感'
|
||||
'现代中式 · 高级感',
|
||||
'研发委员会',
|
||||
'技术委员会',
|
||||
'原创技术资产',
|
||||
'工程受控说明',
|
||||
'白盒交付',
|
||||
'白盒源码',
|
||||
'带病代码',
|
||||
'杜绝系统带病上线',
|
||||
'西南数谷',
|
||||
]
|
||||
all_html = (
|
||||
self.index_html + "\n" +
|
||||
|
||||
@@ -88,15 +88,41 @@ class TestNoAiFlavor(unittest.TestCase):
|
||||
self.assertNotIn(word, self.about_page, f"关于我们 PC 页仍包含 AI 味词汇: '{word}'")
|
||||
self.assertNotIn(word, self.about_mobile, f"关于我们移动端页仍包含 AI 味词汇: '{word}'")
|
||||
|
||||
def test_forbidden_ai_committee_and_fake_buzzwords(self):
|
||||
"""全站必须彻底剔除虚假委员会、生硬TDD、白盒、受控等AI套话"""
|
||||
fake_buzzwords = [
|
||||
'研发委员会',
|
||||
'技术委员会',
|
||||
'原创技术资产',
|
||||
'白盒源码',
|
||||
'白盒交付',
|
||||
'工程受控说明',
|
||||
'带病代码',
|
||||
'带病交付',
|
||||
'杜绝系统带病上线',
|
||||
'西南数谷',
|
||||
]
|
||||
all_templates = (
|
||||
self.index_html + "\n" +
|
||||
self.service_page + "\n" +
|
||||
self.product_page + "\n" +
|
||||
self.cases_page + "\n" +
|
||||
self.about_page + "\n" +
|
||||
self.about_mobile + "\n" +
|
||||
self.contact_page
|
||||
)
|
||||
for word in fake_buzzwords:
|
||||
self.assertNotIn(word, all_templates, f"全站模板严禁包含虚假AI口味词汇: '{word}'")
|
||||
|
||||
def test_enterprise_engineering_keywords(self):
|
||||
"""全站必须具备真实大厂软件工程与业务术语"""
|
||||
required_in_index = ['源码交付', '微服务', '私有化部署', '测试驱动']
|
||||
"""全站必须具备真实软件工程与业务术语"""
|
||||
required_in_index = ['源码交付', '微服务', '私有化部署', '代码审查']
|
||||
for kw in required_in_index:
|
||||
self.assertIn(kw, self.index_html, f"首页缺少硬核工程关键词: '{kw}'")
|
||||
self.assertIn(kw, self.index_html, f"首页缺少工程关键词: '{kw}'")
|
||||
|
||||
required_in_services = ['源码', '微服务', 'API', 'SLA', '私有化']
|
||||
for kw in required_in_services:
|
||||
self.assertIn(kw, self.service_page, f"服务页缺少硬核工程关键词: '{kw}'")
|
||||
self.assertIn(kw, self.service_page, f"服务页缺少工程关键词: '{kw}'")
|
||||
|
||||
def test_layout_enterprise_left_aligned_hero(self):
|
||||
"""首屏与二级页排版验证:左对齐大厂排版与严谨留白"""
|
||||
|
||||
在新工单中引用
屏蔽一个用户