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 index 5b2027d..e784e39 100644 --- 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 @@ -86,27 +86,43 @@ - +
- 在此配置“平台规范与服务协议”弹层的协议条款与合规内容,严格遵循微信内容安全风控标准。 + 在此灵活配置小程序端的《用户服务协议》与《隐私保护指引》,支持独立条款配置与全平台动态更新,严格遵循微信内容安全风控标准。 - - - - - + + + + + + + + + + + + + + + + + + + + - - - - + - 登录注册即代表您已阅读并同意 + 登录注册即同意 《用户服务协议》 与 《隐私保护指引》 @@ -69,6 +69,7 @@ import { ref, onMounted } from 'vue'; import { onLoad, onShow } from '@dcloudio/uni-app'; import { ICONS } from '@/common/icons.js'; import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js'; +import { getPlatformConfigs } from '@/common/api.js'; const APP_LOGO = 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200'; const DEFAULT_AVATAR = 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200'; @@ -77,6 +78,35 @@ const currentThemeKey = ref(getCurrentThemeKey()); const agreedProtocol = ref(true); const redirectUrl = ref(''); +// 动态协议与隐私政策配置 (支持管理后台灵活自定义配置与动态热更新) +const platformProtocol = ref({ + userAgreementTitle: '用户服务协议', + userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。', + privacyPolicyTitle: '隐私保护指引', + privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。' +}); + +const loadProtocolConfig = async () => { + try { + const res = await getPlatformConfigs(); + if (res && res.platform_protocol) { + const p = res.platform_protocol; + if (typeof p === 'object') { + if (p.userAgreementTitle) platformProtocol.value.userAgreementTitle = p.userAgreementTitle; + if (p.userAgreementContent || p.content) { + platformProtocol.value.userAgreementContent = p.userAgreementContent || p.content; + } + if (p.privacyPolicyTitle) platformProtocol.value.privacyPolicyTitle = p.privacyPolicyTitle; + if (p.privacyPolicyContent) { + platformProtocol.value.privacyPolicyContent = p.privacyPolicyContent; + } + } + } + } catch (err) { + console.warn('[Login] 动态拉取后台协议配置降级:', err); + } +}; + const DEMO_ACCOUNTS = [ { id: 1001, @@ -117,11 +147,13 @@ onLoad((options) => { onMounted(() => { currentThemeKey.value = getCurrentThemeKey(); applyThemeToNativeUI(currentThemeKey.value); + loadProtocolConfig(); }); onShow(() => { currentThemeKey.value = getCurrentThemeKey(); applyThemeToNativeUI(currentThemeKey.value); + loadProtocolConfig(); }); const completeLogin = (userId, customData = {}) => { @@ -218,8 +250,8 @@ const loginWithDemo = (acc) => { const openUserAgreement = () => { uni.showModal({ - title: '用户服务协议', - content: '欢迎使用同城生活陪伴服务平台。平台遵循合法合规与安全原则,所有同城活动履约均须在公开透明街区展开,严禁发布违规涉黄及私下交易导流信息。', + title: platformProtocol.value.userAgreementTitle || '用户服务协议', + content: platformProtocol.value.userAgreementContent, showCancel: false, confirmText: '我知道了' }); @@ -227,8 +259,8 @@ const openUserAgreement = () => { const openPrivacyAgreement = () => { uni.showModal({ - title: '隐私保护指引', - content: '平台高度重视用户个人信息安全,全面遵守国家网信办与微信小程序开发者隐私规范要求。您的手机号与身份信息仅用于平台核销和交易安全保障,未经授权绝不对外公开。', + title: platformProtocol.value.privacyPolicyTitle || '隐私保护指引', + content: platformProtocol.value.privacyPolicyContent, showCancel: false, confirmText: '我知道了' }); @@ -437,20 +469,23 @@ const openPrivacyAgreement = () => { } } -/* 协议勾选 */ +/* 协议勾选 (一行紧凑完整展示) */ .protocol-section { display: flex; align-items: center; justify-content: center; - gap: 12rpx; + gap: 10rpx; margin-top: auto; - padding: 16rpx 0; + padding: 20rpx 0; + width: 100%; + box-sizing: border-box; + flex-wrap: nowrap; .checkbox { - width: 32rpx; - height: 32rpx; - border-radius: 8rpx; - border: 2px solid #64748B; + width: 28rpx; + height: 28rpx; + border-radius: 6rpx; + border: 2rpx solid #64748B; display: flex; align-items: center; justify-content: center; @@ -461,18 +496,22 @@ const openPrivacyAgreement = () => { border-color: #07C160; .checkbox-inner { - width: 12rpx; - height: 12rpx; + width: 10rpx; + height: 10rpx; background: #FFFFFF; - border-radius: 3rpx; + border-radius: 2rpx; } } } .protocol-text { - font-size: 22rpx; + font-size: 20rpx; color: #94A3B8; - line-height: 1.5; + line-height: 1.2; + white-space: nowrap; + display: flex; + align-items: center; + flex-shrink: 0; .protocol-link { color: #07C160; diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue index 977af13..a9a5257 100644 --- a/pwuniapp/pages/user/index.vue +++ b/pwuniapp/pages/user/index.vue @@ -820,9 +820,11 @@ const openAbout = () => { if (privacyModalRef.value && privacyModalRef.value.show) { privacyModalRef.value.show(); } else { + const fullText = (proto.userAgreementContent ? `【用户服务协议】\n${proto.userAgreementContent}\n\n` : '') + + (proto.privacyPolicyContent ? `【隐私保护指引】\n${proto.privacyPolicyContent}` : (proto.content || proto.summary || '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。')); uni.showModal({ title: proto.title || '用户服务协议与隐私规范', - content: proto.content || proto.summary || '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。', + content: fullText, showCancel: false }); } diff --git a/server/src/db/mysql.ts b/server/src/db/mysql.ts index 238b67e..8dc3236 100644 --- a/server/src/db/mysql.ts +++ b/server/src/db/mysql.ts @@ -770,7 +770,11 @@ async function seedInitialDataIfEmpty(conn: any): Promise { const protocolVal = JSON.stringify({ title: '用户服务协议与隐私规范', summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。', - content: '1. 账号使用规范:用户注册与使用陪玩服务须符合国家法律法规,严禁利用平台从事涉黄、赌博、欺诈等违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,核销确认后自动结算至大神钱包。\n3. 争议与售后:若遇服务不符或搭子违约,可随时提交争议仲裁由运营平台人工介入裁决。' + userAgreementTitle: '用户服务协议', + userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。', + privacyPolicyTitle: '隐私保护指引', + privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。', + content: '1. 账号使用规范:用户注册与使用陪玩服务须符合国家法律法规,严禁利用平台从事违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,核销确认后自动结算至大神钱包。\n3. 争议与售后:若遇服务不符或搭子违约,可随时提交争议仲裁由运营平台人工介入裁决。' }); await conn.query(` @@ -845,5 +849,29 @@ async function seedInitialDataIfEmpty(conn: any): Promise { VALUES ('activity_rules', '同城组局发单与预算预设规范', ?, '招募席位上下限、快捷单人赏金预设与包场预算梯队', 1) `, [activityRulesVal]); } + if (existingKeys.has('platform_protocol')) { + const [protoRows]: any = await conn.query("SELECT value_json FROM platform_configs WHERE config_key = 'platform_protocol'"); + if (protoRows && protoRows.length > 0) { + let curVal = protoRows[0].value_json; + if (typeof curVal === 'string') { + try { curVal = JSON.parse(curVal); } catch (_) {} + } + if (curVal && (!curVal.userAgreementContent || !curVal.privacyPolicyContent)) { + const defaultProto = { + userAgreementTitle: '用户服务协议', + userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。', + privacyPolicyTitle: '隐私保护指引', + privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。' + }; + const mergedProto = { + ...defaultProto, + ...curVal + }; + if (!mergedProto.userAgreementContent) mergedProto.userAgreementContent = defaultProto.userAgreementContent; + if (!mergedProto.privacyPolicyContent) mergedProto.privacyPolicyContent = defaultProto.privacyPolicyContent; + await conn.query("UPDATE platform_configs SET value_json = ? WHERE config_key = 'platform_protocol'", [JSON.stringify(mergedProto)]); + } + } + } } } diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index a4528f2..ed91613 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -473,6 +473,15 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u; expect(emojiRegex.test(content)).toBe(false); }); + + it('登录页面底部协议必须支持后台灵活配置与单行紧凑展示规范', () => { + const loginPath = path.join(rootDir, 'pwuniapp', 'pages', 'auth', 'login.vue'); + const content = fs.readFileSync(loginPath, 'utf-8'); + expect(content).toContain('getPlatformConfigs'); + expect(content).toContain('platformProtocol'); + expect(content).toContain('loadProtocolConfig'); + expect(content).toContain('white-space: nowrap'); + }); }); }); diff --git a/server/tests/platform-config.spec.ts b/server/tests/platform-config.spec.ts index b89d39c..b7cd729 100644 --- a/server/tests/platform-config.spec.ts +++ b/server/tests/platform-config.spec.ts @@ -126,4 +126,33 @@ describe('平台通用配置与合规管理 TDD 自动化测试', () => { const check = secService.checkText(JSON.stringify(faqList)); expect(check.pass).toBe(true); }); + + it('8. 用户服务协议与隐私保护指引双独立配置结构与合规内容审查', () => { + const protocolConfig = { + title: '用户服务协议与隐私规范', + summary: '平台合规履约协议与隐私保护规范', + userAgreementTitle: '用户服务协议', + userAgreementContent: '1. 账号使用规范:严禁利用平台从事低俗违规与欺诈交易行为。\n2. 资金保障:平台提供全额担保交易,订单完成前款项由官方账户托管。', + privacyPolicyTitle: '隐私保护指引', + privacyPolicyContent: '1. 信息收集范围:依法收集手机号与位置信息。\n2. 数据加密保护:实施高强度加密脱敏存储。' + }; + + expect(protocolConfig.userAgreementTitle).toBe('用户服务协议'); + expect(protocolConfig.privacyPolicyTitle).toBe('隐私保护指引'); + expect(protocolConfig.userAgreementContent).toContain('全额担保交易'); + expect(protocolConfig.privacyPolicyContent).toContain('加密脱敏存储'); + + const check = secService.checkText(JSON.stringify(protocolConfig)); + expect(check.pass).toBe(true); + }); + + it('9. 协议内容包含涉黄或违规敏感词时应触发微信安全拦截', () => { + const badProtocol = { + userAgreementTitle: '用户服务协议', + userAgreementContent: '支持线下提供上门特殊服务,私下转账可打折免抽成' + }; + const badCheck = secService.checkText(JSON.stringify(badProtocol)); + expect(badCheck.pass).toBe(false); + expect(badCheck.code).toBe(87014); + }); });