feat(system): implement dynamic platform configs for customer service, safety notice, and service agreement

这个提交包含在:
xcbyy
2026-10-10 09:47:53 +08:00
父节点 d9c8388711
当前提交 dc19189bc6
共有 10 个文件被更改,包括 613 次插入 和 10 次删除

查看文件

@@ -48,6 +48,7 @@ export default {
'menu.safety': 'Safety & Risk Control',
'menu.safety.places': 'Approved Venues',
'menu.safety.auditLog': 'Risk Intercept Log',
'menu.safety.platformConfig': 'Platform & Support Config',
'menu.banner': 'Banner & Operations',
'menu.banner.manage': 'Banner Management',
'menu.banner.navCategory': 'Category Navigation',

查看文件

@@ -48,6 +48,7 @@ export default {
'menu.safety': '履约安全与风控',
'menu.safety.places': '线下商圈场所库',
'menu.safety.auditLog': '场所与内容拦截日志',
'menu.safety.platformConfig': '客服与公告协议配置',
'menu.banner': '首页轮播与运营',
'menu.banner.manage': '轮播图管理',
'menu.banner.navCategory': '金刚区导航品类',

查看文件

@@ -32,6 +32,16 @@ const SAFETY: AppRouteRecordRaw = {
roles: ['*'],
},
},
{
path: 'platform-config',
name: 'PlatformConfig',
component: () => import('@/views/safety/platform-config/index.vue'),
meta: {
locale: 'menu.safety.platformConfig',
requiresAuth: true,
roles: ['*'],
},
},
],
};

查看文件

@@ -0,0 +1,259 @@
<template>
<div class="container">
<Breadcrumb :items="['menu.safety', 'menu.safety.platformConfig']" />
<a-card class="general-card" :title="'平台客服与官方指引/协议运营配置'">
<template #extra>
<a-button type="primary" :loading="loading" @click="fetchConfigs">
<template #icon><icon-refresh /></template>
刷新配置
</a-button>
</template>
<a-tabs default-active-key="customer_service" type="rounded">
<!-- Tab 1: 官方客服配置 -->
<a-tab-pane key="customer_service" title="官方客服与热线配置">
<div class="tab-pane-content">
<a-alert type="info" style="margin-bottom: 20px">
在此配置小程序个人中心“官方在线客服”弹层的联系电话与专属微信号,支持即时保存生效。
</a-alert>
<a-form :model="csForm" layout="vertical" style="max-width: 650px">
<a-form-item label="配置名称">
<a-input v-model="csForm.name" placeholder="如:官方在线客服与服务热线" />
</a-form-item>
<a-form-item label="7x24h 官方客服服务热线">
<a-input v-model="csForm.valueJson.hotline" placeholder="例如:400-888-6666" />
</a-form-item>
<a-form-item label="热线展示说明">
<a-input v-model="csForm.valueJson.hotlineDesc" placeholder="例如:7x24h 官方客服服务热线" />
</a-form-item>
<a-form-item label="官方 VIP 客服微信号">
<a-input v-model="csForm.valueJson.wechat" placeholder="例如:peiwan_vip_kefu" />
</a-form-item>
<a-form-item label="微信号展示说明">
<a-input v-model="csForm.valueJson.wechatDesc" placeholder="例如:官方 VIP 客服微信号" />
</a-form-item>
<a-form-item label="客服服务时间">
<a-input v-model="csForm.valueJson.workHours" placeholder="例如:周一至周日 00:00 - 24:00" />
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="csForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveConfig('customer_service', csForm)">
保存客服配置
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
<!-- Tab 2: 官方安全履约公告 -->
<a-tab-pane key="safety_notice" title="安全履约公告指引">
<div class="tab-pane-content">
<a-alert type="warning" style="margin-bottom: 20px">
在此配置小程序个人中心“平台消息与通知”弹层的履约安全指引与风险防范须知。
</a-alert>
<a-form :model="noticeForm" layout="vertical" style="max-width: 750px">
<a-form-item label="公告标题">
<a-input v-model="noticeForm.valueJson.title" placeholder="如:平台官方公告与指引" />
</a-form-item>
<a-form-item label="安全须知要点 (每行一条)">
<a-textarea
v-model="noticePointsText"
placeholder="每行一条须知要点"
:auto-size="{ minRows: 5, maxRows: 10 }"
/>
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="noticeForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveNoticeConfig">
保存公告指引
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
<!-- Tab 3: 用户服务协议与规范 -->
<a-tab-pane key="platform_protocol" title="平台规范与服务协议">
<div class="tab-pane-content">
<a-alert type="success" style="margin-bottom: 20px">
在此配置“平台规范与服务协议”弹层的协议条款与合规内容,严格遵循微信内容安全风控标准。
</a-alert>
<a-form :model="protoForm" layout="vertical" style="max-width: 800px">
<a-form-item label="协议标题">
<a-input v-model="protoForm.valueJson.title" placeholder="如:用户服务协议与隐私规范" />
</a-form-item>
<a-form-item label="协议摘要概要">
<a-input v-model="protoForm.valueJson.summary" placeholder="协议概要,一两句话总结" />
</a-form-item>
<a-form-item label="服务协议全文内容">
<a-textarea
v-model="protoForm.valueJson.content"
placeholder="请输入完整协议条款"
:auto-size="{ minRows: 8, maxRows: 15 }"
/>
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="protoForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveConfig('platform_protocol', protoForm)">
保存协议规范
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import axios from 'axios';
const loading = ref(false);
const saving = ref(false);
const csForm = reactive({
name: '官方在线客服与服务热线',
description: '小程序端个人中心客服联系方式',
status: 1,
valueJson: {
hotline: '400-888-6666',
hotlineDesc: '7x24h 官方客服服务热线',
wechat: 'peiwan_vip_kefu',
wechatDesc: '官方 VIP 客服微信号',
workHours: '周一至周日 00:00 - 24:00'
}
});
const noticeForm = reactive({
name: '安全履约须知与官方公告',
description: '小程序端官方指引与安全红线弹窗',
status: 1,
valueJson: {
title: '平台官方公告与指引',
points: [
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务,严禁脱离平台私下交易。'
]
}
});
const noticePointsText = ref('');
const protoForm = reactive({
name: '平台服务协议与合规规范',
description: '小程序端服务协议与隐私政策详情',
status: 1,
valueJson: {
title: '用户服务协议与隐私规范',
summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
content: ''
}
});
const fetchConfigs = async () => {
loading.value = true;
try {
const { data: res } = await axios.get('/api/admin/system/configs');
if (res && res.code === 20000 && Array.isArray(res.data)) {
res.data.forEach((item: any) => {
if (item.configKey === 'customer_service') {
csForm.name = item.name;
csForm.description = item.description;
csForm.status = item.status;
csForm.valueJson = { ...csForm.valueJson, ...(item.valueJson || {}) };
} else if (item.configKey === 'safety_notice') {
noticeForm.name = item.name;
noticeForm.description = item.description;
noticeForm.status = item.status;
noticeForm.valueJson = { ...noticeForm.valueJson, ...(item.valueJson || {}) };
if (Array.isArray(noticeForm.valueJson.points)) {
noticePointsText.value = noticeForm.valueJson.points.join('\n');
}
} else if (item.configKey === 'platform_protocol') {
protoForm.name = item.name;
protoForm.description = item.description;
protoForm.status = item.status;
protoForm.valueJson = { ...protoForm.valueJson, ...(item.valueJson || {}) };
}
});
}
} catch (err: any) {
Message.error(err.response?.data?.message || '获取系统配置失败');
} finally {
loading.value = false;
}
};
const saveConfig = async (key: string, form: any) => {
saving.value = true;
try {
const payload = {
name: form.name,
description: form.description,
status: form.status,
valueJson: form.valueJson
};
const { data: res } = await axios.put(`/api/admin/system/configs/${key}`, payload);
if (res && res.code === 20000) {
Message.success('配置已保存并同步至小程序端');
fetchConfigs();
} else {
Message.error(res?.message || '保存配置失败');
}
} catch (err: any) {
Message.error(err.response?.data?.message || '保存配置异常,请检查内容是否含敏感词');
} finally {
saving.value = false;
}
};
const saveNoticeConfig = () => {
const points = noticePointsText.value
.split('\n')
.map((p) => p.trim())
.filter((p) => Boolean(p));
noticeForm.valueJson.points = points;
saveConfig('safety_notice', noticeForm);
};
onMounted(() => {
fetchConfigs();
});
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
.general-card {
border-radius: 4px;
}
.tab-pane-content {
padding: 16px 8px;
}
</style>

查看文件

@@ -737,3 +737,16 @@ export async function checkTextSecurity(content) {
}
}
/**
* 获取平台通用配置(官方客服、履约公告指引、平台规范协议等)
*/
export async function getPlatformConfigs() {
try {
const res = await http.get('/system/configs');
return res;
} catch (err) {
console.warn('[API] 获取平台通用配置降级:', err.message);
return null;
}
}

查看文件

@@ -218,7 +218,7 @@ import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey, setTheme, THEME_KEYS, applyThemeToNativeUI } from '@/common/theme.js';
import { getCurrentRole, setCurrentRole, ROLE_TYPES, toggleRole as toggleUserRole, isExpertCertified, getExpertCertStatus, setExpertCertStatus, CERT_STATUS } from '@/common/role.js';
import { getNavBarInfo } from '@/common/nav.js';
import { getUserProfile, rechargeBalance, withdrawCommission, getUserFinanceRecords, updateUserProfile, uploadImage, getRechargeTiers } from '@/common/api.js';
import { getUserProfile, rechargeBalance, withdrawCommission, getUserFinanceRecords, updateUserProfile, uploadImage, getRechargeTiers, getPlatformConfigs } from '@/common/api.js';
const user = ref({
id: 1001,
@@ -236,6 +236,40 @@ const currentRole = ref(getCurrentRole());
const currentThemeKey = ref(getCurrentThemeKey());
const navInfo = ref(getNavBarInfo());
const privacyModalRef = ref(null);
const platformConfigs = ref({
customer_service: {
hotline: '400-888-6666',
hotlineDesc: '7x24h 官方客服服务热线',
wechat: 'peiwan_vip_kefu',
wechatDesc: '官方 VIP 客服微信号'
},
safety_notice: {
title: '平台官方公告与指引',
points: [
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务。'
]
},
platform_protocol: {
title: '用户服务协议与隐私规范',
summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。'
}
});
const loadPlatformConfigs = async () => {
try {
const res = await getPlatformConfigs();
if (res && typeof res === 'object') {
platformConfigs.value = {
...platformConfigs.value,
...res
};
}
} catch (e) {
console.warn('[User] 加载系统配置失败降级:', e);
}
};
const navStyle = computed(() => {
return {
@@ -274,6 +308,7 @@ onMounted(() => {
uni.$on('theme-change', onThemeChanged);
uni.$on('role-change', onRoleChanged);
loadUserProfile();
loadPlatformConfigs();
});
onShow(() => {
@@ -283,6 +318,7 @@ onShow(() => {
applyThemeToNativeUI(currentThemeKey.value);
uni.hideTabBar({ animation: false, fail: () => {} });
loadUserProfile();
loadPlatformConfigs();
});
onUnmounted(() => {
@@ -588,23 +624,28 @@ const applyExpert = () => {
};
const contactService = () => {
const cs = platformConfigs.value.customer_service || {};
const hotline = cs.hotline || '400-888-6666';
const wechat = cs.wechat || 'peiwan_vip_kefu';
const cleanPhone = hotline.replace(/[^\d]/g, '');
uni.showActionSheet({
itemList: [
'拨打 7x24h 官方服务热线 (400-888-6666)',
'复制平台 VIP 客服微信号 (peiwan_vip_kefu)',
`拨打官方服务热线 (${hotline})`,
`复制平台 VIP 客服微信号 (${wechat})`,
'查看平台官方安全指引'
],
success: (res) => {
if (res.tapIndex === 0) {
uni.makePhoneCall({
phoneNumber: '4008886666',
phoneNumber: cleanPhone,
fail: () => {
uni.showToast({ title: '服务热线: 400-888-6666', icon: 'none' });
uni.showToast({ title: `服务热线: ${hotline}`, icon: 'none' });
}
});
} else if (res.tapIndex === 1) {
uni.setClipboardData({
data: 'peiwan_vip_kefu',
data: wechat,
success: () => {
uni.showToast({ title: '官方客服微信号已复制', icon: 'success' });
}
@@ -617,9 +658,16 @@ const contactService = () => {
};
const openNotice = () => {
const notice = platformConfigs.value.safety_notice || {};
const title = notice.title || '平台官方公告与指引';
let content = '【安全履约须知】\n1. 为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。\n2. 现场碰面后请出示 4 位数字核销码进行签到确认。\n3. 平台提供 7x24 小时争议仲裁与资金托管保障服务。';
if (Array.isArray(notice.points) && notice.points.length > 0) {
content = notice.points.map((p, idx) => `${idx + 1}. ${p}`).join('\n');
}
uni.showModal({
title: '平台官方公告与指引',
content: '【安全履约须知】\n1. 为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。\n2. 现场碰面后请出示 4 位数字核销码进行签到确认。\n3. 平台提供 7x24 小时争议仲裁与资金托管保障服务。',
title,
content,
showCancel: false,
confirmText: '我知道了',
confirmColor: '#6366F1'
@@ -627,12 +675,13 @@ const openNotice = () => {
};
const openAbout = () => {
const proto = platformConfigs.value.platform_protocol || {};
if (privacyModalRef.value && privacyModalRef.value.show) {
privacyModalRef.value.show();
} else {
uni.showModal({
title: '用户服务协议与隐私规范',
content: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
title: proto.title || '用户服务协议与隐私规范',
content: proto.content || proto.summary || '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
showCancel: false
});
}

查看文件

@@ -372,6 +372,21 @@ export async function initDatabase(): Promise<void> {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 20. 平台官方客服、公告及服务协议通用配置表
await conn.query(`
CREATE TABLE IF NOT EXISTS platform_configs (
id INT PRIMARY KEY AUTO_INCREMENT,
config_key VARCHAR(64) UNIQUE NOT NULL,
name VARCHAR(128) NOT NULL,
value_json JSON NOT NULL,
description VARCHAR(255) DEFAULT '',
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_key (config_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 初始化配置单条记录
const [cfgRows]: any = await conn.query('SELECT COUNT(*) as count FROM finance_config WHERE id = 1');
if (cfgRows[0].count === 0) {
@@ -626,4 +641,40 @@ async function seedInitialDataIfEmpty(conn: any): Promise<void> {
(4, '电竞开黑连胜冲星券', 10.00, 30.00, 30, 5000, 340, 'ALL', '线上游戏陪玩下单立减', 1);
`);
}
// 17. 种子平台通用配置(官方客服、履约公告指引、规范协议)
const [cfgItemRows]: any = await conn.query('SELECT COUNT(*) as count FROM platform_configs');
if (cfgItemRows[0].count === 0) {
const customerServiceVal = JSON.stringify({
hotline: '400-888-6666',
hotlineDesc: '7x24h 官方客服服务热线',
wechat: 'peiwan_vip_kefu',
wechatDesc: '官方 VIP 客服微信号',
workHours: '周一至周日 00:00 - 24:00'
});
const safetyNoticeVal = JSON.stringify({
title: '平台官方公告与指引',
points: [
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务,严禁脱离平台私下交易。'
],
noticePopup: false
});
const protocolVal = JSON.stringify({
title: '用户服务协议与隐私规范',
summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
content: '1. 账号使用规范:用户注册与使用陪玩服务须符合国家法律法规,严禁利用平台从事涉黄、赌博、欺诈等违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,核销确认后自动结算至大神钱包。\n3. 争议与售后:若遇服务不符或搭子违约,可随时提交争议仲裁由运营平台人工介入裁决。'
});
await conn.query(`
INSERT INTO platform_configs (config_key, name, value_json, description, status)
VALUES
('customer_service', '官方在线客服与服务热线', ?, '小程序端个人中心客服联系方式', 1),
('safety_notice', '安全履约须知与官方公告', ?, '小程序端官方指引与安全红线弹窗', 1),
('platform_protocol', '平台服务协议与合规规范', ?, '小程序端服务协议与隐私政策详情', 1);
`, [customerServiceVal, safetyNoticeVal, protocolVal]);
}
}

查看文件

@@ -1247,6 +1247,91 @@ export class MysqlRepository {
return res.affectedRows > 0;
}
// 3. 平台通用配置与指引 (Platform Configs)
async getPlatformConfigs(): Promise<{ [key: string]: any }> {
const [rows]: any = await pool.query(`
SELECT config_key as configKey, name, value_json as valueJson, description, status
FROM platform_configs
WHERE status = 1
`);
const result: { [key: string]: any } = {};
for (const r of rows) {
let val = r.valueJson;
if (typeof val === 'string') {
try {
val = JSON.parse(val);
} catch (_) {}
}
result[r.configKey] = val;
}
return result;
}
async getAdminPlatformConfigs(): Promise<any[]> {
const [rows]: any = await pool.query(`
SELECT id, config_key as configKey, name, value_json as valueJson, description, status,
created_at as createdAt, updated_at as updatedAt
FROM platform_configs
ORDER BY id ASC
`);
return rows.map((r: any) => {
let val = r.valueJson;
if (typeof val === 'string') {
try {
val = JSON.parse(val);
} catch (_) {}
}
return {
...r,
valueJson: val
};
});
}
async updatePlatformConfig(configKey: string, data: { name?: string; valueJson?: any; description?: string; status?: number }): Promise<any> {
const fields: string[] = [];
const values: any[] = [];
if (data.name !== undefined) {
fields.push('name = ?');
values.push(data.name);
}
if (data.valueJson !== undefined) {
fields.push('value_json = ?');
values.push(JSON.stringify(data.valueJson));
}
if (data.description !== undefined) {
fields.push('description = ?');
values.push(data.description);
}
if (data.status !== undefined) {
fields.push('status = ?');
values.push(Number(data.status));
}
if (fields.length > 0) {
values.push(configKey);
await pool.query(`UPDATE platform_configs SET ${fields.join(', ')} WHERE config_key = ?`, values);
}
const [rows]: any = await pool.query(`
SELECT id, config_key as configKey, name, value_json as valueJson, description, status,
created_at as createdAt, updated_at as updatedAt
FROM platform_configs
WHERE config_key = ?
`, [configKey]);
if (!rows || rows.length === 0) return null;
const r = rows[0];
let val = r.valueJson;
if (typeof val === 'string') {
try {
val = JSON.parse(val);
} catch (_) {}
}
return { ...r, valueJson: val };
}
async getArbitrations(): Promise<any[]> {

查看文件

@@ -1051,6 +1051,57 @@ app.delete('/api/admin/coupons/:id', async (req: Request, res: Response) => {
}
});
// 3. 平台官方客服、履约公告与规范协议配置接口
// 移动端:获取全部有效系统通用配置
app.get('/api/system/configs', async (_req: Request, res: Response) => {
try {
const configs = await repo.getPlatformConfigs();
res.json({ code: 20000, data: configs });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
// 管理后台:获取全部配置项列表
app.get('/api/admin/system/configs', async (_req: Request, res: Response) => {
try {
const list = await repo.getAdminPlatformConfigs();
res.json({ code: 20000, data: list });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
// 管理后台:更新指定配置项
app.put('/api/admin/system/configs/:key', async (req: Request, res: Response) => {
try {
const configKey = String(req.params.key);
const { name, valueJson, description, status } = req.body;
// 微信内容安全审查(检查文本内容或字符串化后的配置)
const textToCheck = `${name || ''} ${description || ''} ${JSON.stringify(valueJson || '')}`;
const sec = contentSecurityService.checkText(textToCheck);
if (!sec.pass) {
return res.status(400).json({ code: sec.code, message: sec.message });
}
const updated = await repo.updatePlatformConfig(configKey, {
name,
valueJson,
description,
status: status !== undefined ? Number(status) : undefined
});
if (!updated) {
return res.status(404).json({ code: 40400, message: '配置项不存在' });
}
res.json({ code: 20000, data: updated, message: '系统配置更新成功' });
} catch (err: any) {
res.status(400).json({ code: 40000, message: err.message });
}
});
// 创建新订单 (落盘 MySQL)
app.post('/api/orders', async (req: Request, res: Response) => {
try {

查看文件

@@ -0,0 +1,83 @@
// 已读取AGENTS.md
import { ContentSecurityService } from '../src/core/security/content-security.service';
describe('平台通用配置与合规管理 TDD 自动化测试', () => {
const secService = new ContentSecurityService();
it('1. 平台默认客服与公告配置数据结构验证', () => {
const customerService = {
hotline: '400-888-6666',
hotlineDesc: '7x24h 官方客服服务热线',
wechat: 'peiwan_vip_kefu',
wechatDesc: '官方 VIP 客服微信号',
workHours: '周一至周日 00:00 - 24:00'
};
expect(customerService.hotline).toBe('400-888-6666');
expect(customerService.wechat).toBe('peiwan_vip_kefu');
const safetyNotice = {
title: '平台官方公告与指引',
points: [
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务,严禁脱离平台私下交易。'
]
};
expect(safetyNotice.points.length).toBe(3);
expect(safetyNotice.points[0]).toContain('公开商业街区');
});
it('2. 更新公告与客服信息时实施微信内容安全审查 (合规文本应放行)', () => {
const safeData = {
hotline: '400-999-8888',
wechat: 'peiwan_official_service',
points: ['请大家在公共场合文明约单,遵守平台履约秩序']
};
const check = secService.checkText(JSON.stringify(safeData));
expect(check.pass).toBe(true);
expect(check.suggest).toBe('pass');
});
it('3. 更新配置时拦截涉黄违规词 (security.msgSecCheck 拦截)', () => {
const vulgarConfig = {
wechat: 'fake_kefu',
content: '添加客服微信提供上门特殊服务'
};
const check = secService.checkText(JSON.stringify(vulgarConfig));
expect(check.pass).toBe(false);
expect(check.code).toBe(87014);
expect(check.label).toBe('VULGAR_PORN');
expect(check.message).toContain('上门特殊服务');
});
it('4. 更新配置时拦截欺诈导流或线下私单 (防脱离平台交易)', () => {
const fraudConfig = {
notice: '联系微信号,私下转账免手续费支持线下私单优惠'
};
const check = secService.checkText(JSON.stringify(fraudConfig));
expect(check.pass).toBe(false);
expect(check.code).toBe(87014);
expect(check.label).toBe('FRAUD_SCAM');
});
it('5. 系统配置合并与动态降级容错机制', () => {
const defaultConfigs: Record<string, any> = {
customer_service: { hotline: '400-888-6666', wechat: 'peiwan_vip_kefu' },
safety_notice: { title: '官方公告', points: ['注意安全'] }
};
const remoteConfigs: Record<string, any> = {
customer_service: { hotline: '400-123-4567', wechat: 'peiwan_kefu_02' }
};
const merged = {
...defaultConfigs,
...remoteConfigs
};
expect(merged.customer_service.hotline).toBe('400-123-4567');
expect(merged.safety_notice.title).toBe('官方公告');
});
});