From dc19189bc6897c1505aa6a859a177e9263780776 Mon Sep 17 00:00:00 2001
From: xcbyy <2578898740@qq.com>
Date: Sat, 10 Oct 2026 09:47:53 +0800
Subject: [PATCH] feat(system): implement dynamic platform configs for customer
service, safety notice, and service agreement
---
.../arco-design-pro-vite/src/locale/en-US.ts | 1 +
.../arco-design-pro-vite/src/locale/zh-CN.ts | 1 +
.../src/router/routes/modules/safety.ts | 10 +
.../views/safety/platform-config/index.vue | 259 ++++++++++++++++++
pwuniapp/common/api.js | 13 +
pwuniapp/pages/user/index.vue | 69 ++++-
server/src/db/mysql.ts | 51 ++++
server/src/db/repository.ts | 85 ++++++
server/src/index.ts | 51 ++++
server/tests/platform-config.spec.ts | 83 ++++++
10 files changed, 613 insertions(+), 10 deletions(-)
create mode 100644 admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
create mode 100644 server/tests/platform-config.spec.ts
diff --git a/admin/arco-design-pro-vite/src/locale/en-US.ts b/admin/arco-design-pro-vite/src/locale/en-US.ts
index bbeff0f..e3fe327 100644
--- a/admin/arco-design-pro-vite/src/locale/en-US.ts
+++ b/admin/arco-design-pro-vite/src/locale/en-US.ts
@@ -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',
diff --git a/admin/arco-design-pro-vite/src/locale/zh-CN.ts b/admin/arco-design-pro-vite/src/locale/zh-CN.ts
index 23347a6..780ae1b 100644
--- a/admin/arco-design-pro-vite/src/locale/zh-CN.ts
+++ b/admin/arco-design-pro-vite/src/locale/zh-CN.ts
@@ -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': '金刚区导航品类',
diff --git a/admin/arco-design-pro-vite/src/router/routes/modules/safety.ts b/admin/arco-design-pro-vite/src/router/routes/modules/safety.ts
index 96c6004..de51c52 100644
--- a/admin/arco-design-pro-vite/src/router/routes/modules/safety.ts
+++ b/admin/arco-design-pro-vite/src/router/routes/modules/safety.ts
@@ -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: ['*'],
+ },
+ },
],
};
diff --git a/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue b/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
new file mode 100644
index 0000000..8cc5d07
--- /dev/null
+++ b/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
@@ -0,0 +1,259 @@
+
+
+
+
+
+
+
+ 刷新配置
+
+
+
+
+
+
+
+
+ 在此配置小程序个人中心“官方在线客服”弹层的联系电话与专属微信号,支持即时保存生效。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存客服配置
+
+
+
+
+
+
+
+
+
+
+ 在此配置小程序个人中心“平台消息与通知”弹层的履约安全指引与风险防范须知。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存公告指引
+
+
+
+
+
+
+
+
+
+
+ 在此配置“平台规范与服务协议”弹层的协议条款与合规内容,严格遵循微信内容安全风控标准。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存协议规范
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pwuniapp/common/api.js b/pwuniapp/common/api.js
index 3fe9943..52239cc 100644
--- a/pwuniapp/common/api.js
+++ b/pwuniapp/common/api.js
@@ -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;
+ }
+}
+
diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue
index 4268ab1..9a55447 100644
--- a/pwuniapp/pages/user/index.vue
+++ b/pwuniapp/pages/user/index.vue
@@ -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
});
}
diff --git a/server/src/db/mysql.ts b/server/src/db/mysql.ts
index 65e4a1a..6d77d59 100644
--- a/server/src/db/mysql.ts
+++ b/server/src/db/mysql.ts
@@ -372,6 +372,21 @@ export async function initDatabase(): Promise {
) 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 {
(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]);
+ }
}
diff --git a/server/src/db/repository.ts b/server/src/db/repository.ts
index 002a6e9..a849ae0 100644
--- a/server/src/db/repository.ts
+++ b/server/src/db/repository.ts
@@ -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 {
+ 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 {
+ 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 {
diff --git a/server/src/index.ts b/server/src/index.ts
index 5e1a349..8d878f2 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -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 {
diff --git a/server/tests/platform-config.spec.ts b/server/tests/platform-config.spec.ts
new file mode 100644
index 0000000..8cdb436
--- /dev/null
+++ b/server/tests/platform-config.spec.ts
@@ -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 = {
+ customer_service: { hotline: '400-888-6666', wechat: 'peiwan_vip_kefu' },
+ safety_notice: { title: '官方公告', points: ['注意安全'] }
+ };
+
+ const remoteConfigs: Record = {
+ 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('官方公告');
+ });
+});