Skip to content

Commit 05f8e22

Browse files
committed
feat(photography): 实现写真与BDSM模块的跨模块联动
本次提交完成了写真NSFW模块与BDSM模块的集成。现在,当两个模块同时启用时,情趣写真拍摄可以与BDSM的叙事线相结合。 主要变更包括: - **状态扩展**: 在写真项目的状态中增加了`涉及BDSM`和`照片把柄`字段,在BDSM关系状态中增加了`照片把柄`字段,用于存储可作为威胁筹码的照片ID。 - **运行时数据提取**: 更新了模块注册逻辑,现在可以从游戏状态中提取BDSM关系阶段和可用的照片把柄信息。 - **提示词构建**: 新增了`构建BDSM联动约束`函数,根据BDSM关系阶段和照片把柄数量动态生成叙事约束,影响AI在写真场景中的行为。 - **测试方案**: 添加了一份全面的写真NSFW模块测试计划文档,涵盖单元、集成、E2E及AI响应测试。
1 parent 2904d04 commit 05f8e22

10 files changed

Lines changed: 427 additions & 25 deletions

File tree

docs/plans/2026-05-06_photography-nsfw-implementation.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,15 @@ import type { 写真NSFW设置, 写真系统扩展 } from './photographyNSFW';
354354

355355
---
356356

357-
### Step 15: 连接BDSM模块
357+
### Step 15: 连接BDSM模块
358358

359359
**修改文件**
360-
- `models/photographyNSFW/states.ts` — 在 `拍摄项目状态` 中添加可选字段 `涉及BDSM?: boolean`
360+
- `models/photographyNSFW/states.ts` — 在 `拍摄项目状态` 中添加可选字段 `涉及BDSM?: boolean``照片把柄?: { 照片Id: string; 是否已用于威胁: boolean }[]`
361361
- `models/campusNSFW/sm.ts` — 在BDSM关系状态中添加可选字段 `照片把柄?: string[]`
362-
- `prompts/runtime/photographyNSFW.ts` — 当两个模块都激活时,添加BDSM感知提示词变体
362+
- `models/photographyNSFW/index.ts` — 在 `写真NSFW设置` 中添加 `涉及BDSM模块: boolean`
363+
- `models/photographyNSFW/normalization.ts` — 规范化 `涉及BDSM模块` 字段
364+
- `prompts/runtime/photographyNSFW.ts` — 当两个模块都激活时,添加BDSM感知提示词变体(`构建BDSM联动约束`
365+
- `modules/contemporary/photographyNSFW/registration.ts` — 运行时数据提取:从 `gameState.校园系统.欲望系统.NPC欲望档案` 读取BDSM关系阶段,从 `进行中的拍摄项目` 收集未使用的照片把柄
363366

364367
跨模块联动:情趣写真可触发BDSM叙事,泄露照片可成为BDSM关系中的把柄。仅使用可选字段——不硬耦合。
365368

@@ -1041,7 +1044,7 @@ export interface 写真NSFW设置 {
10411044
| Phase 3: AI工作流+提示词 | ✅ 完成 | photographyNSFW.ts, shootWorkflow.ts, leakWorkflow.ts |
10421045
| Phase 4: UI设置注册 | ✅ 完成 | tabDefinitions.ts, SettingsPanel.tsx |
10431046
| Phase 4: UI仪表盘 | ✅ 完成 | PhotographyDashboard.tsx, MobilePhotographyDashboard.tsx 已创建并接入App.tsx |
1044-
| Phase 4: BDSM跨模块联动 | ⏸ 待实施 | 可选字段交叉引用(低优先级) |
1047+
| Phase 4: BDSM跨模块联动 | ✅ 已完成 | 可选字段交叉引用 + 运行时数据提取 |
10451048
| 状态标签解析 | ✅ 完成 | `<写真系统状态>` 标签解析已对接 responseCommandProcessor.ts |
10461049

10471050
## 已创建文件清单
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
# 写真 NSFW 模块测试方案
2+
3+
## 概述
4+
5+
为写真 NSFW 模块建立完整的四级测试体系:单元测试(纯函数逻辑)、集成测试(模块注册/提示词构建/XML 解析)、E2E 测试(拍摄流程 UI 交互)、AI 响应测试(真实 API 端到端验证)。项目已有 Vitest + Playwright 基础设施,但尚未有任何写真模块测试用例。
6+
7+
## 测试端点
8+
9+
- **API 地址**: `https://gcli.ggchan.dev/`
10+
- **API 密钥**: `gg-gcli-RALFsIs47kRn7m3HKh98dTj0R48ccM2ln8sIVDc3OSA`
11+
- **环境变量**: `GCLI_API_KEY`(密钥应通过环境变量注入,不硬编码)
12+
13+
---
14+
15+
## Phase 1: 测试工厂函数(基础依赖)
16+
17+
### 1.1 测试用状态工厂 `__tests__/setup/mockStateFactory.ts`
18+
19+
创建一组可复用的工厂函数,用于快速生成测试所需的模特、摄影师、拍摄项目、泄露事件状态。
20+
21+
**核心工厂函数**
22+
- `createMockModel(overrides?)` — 模特核心状态
23+
- `createMockPhotographer(overrides?)` — 摄影师核心状态
24+
- `createMockShootProject(overrides?)` — 拍摄项目状态
25+
- `createMockLeakEvent(overrides?)` — 泄露事件状态
26+
- `createMockSettings(overrides?)` — 写真NSFW设置
27+
28+
**预设场景工厂**
29+
- `createSafeScenario()` — 低风险场景(影棚 + G级 + 业界大佬)
30+
- `createRiskyScenario()` — 高风险场景(酒店 + R级 + 有争议)
31+
- `createExtremeScenario()` — 极端风险场景(个人住所 + XXX + 惯犯)
32+
33+
### 1.2 XML 解析测试辅助 `__tests__/setup/xmlParserTestHelper.ts`
34+
35+
帮助函数构造各种格式的 `<写真系统状态>` XML 标签内容。
36+
37+
---
38+
39+
## Phase 2: 单元测试(Vitest)
40+
41+
### 2.1 规范化函数测试 `__tests__/photographyNSFW/normalization.test.ts`
42+
43+
| 用例 | 输入 | 期望 | 覆盖分支 |
44+
|------|------|------|----------|
45+
| N-01 | 空对象 `{}` | 返回全部默认值 | 默认回退路径 |
46+
| N-02 | 完整合法设置 | 原样返回 | 正常路径 |
47+
| N-03 | `启用写真NSFW系统: 'true'` (字符串) | 回退到默认 `false` | 类型校验 |
48+
| N-04 | `NSFW内容强度: '过度'` | 回退到默认 `'微暗'` | 枚举校验 |
49+
| N-05 | `次要玩法权重: 150` | 限制为 `100` | 范围限制(上界) |
50+
| N-06 | `次要玩法权重: -10` | 限制为 `0` | 范围限制(下界) |
51+
| N-07 | `主要玩法层: '经营管理'` | 正确保留 | 枚举合法值 |
52+
| N-08 | `泄露事件频率: '超高'` | 回退到默认 `'低'` | 枚举校验 |
53+
| N-09 | 部分字段 `null/undefined` | 对应字段使用默认值 | 缺失字段处理 |
54+
| N-10 | `涉及BDSM模块: 1` (数字) | 回退到默认 `false` | 布尔类型校验 |
55+
56+
### 2.2 引擎纯函数测试 `__tests__/photographyNSFW/engine.test.ts`
57+
58+
**风险评估函数**
59+
| 用例 | 场景 | 期望 |
60+
|------|------|------|
61+
| E-01 | 影棚 + G级 + 业界大佬 + 平台担保 | 风险值 <= 20 |
62+
| E-02 | 野外 + XXX + 惯犯 + 拒绝交付 | 风险值 >= 85 |
63+
| E-03 | 酒店 + R级 + 普通摄影师 + 直接交付 | 风险值 30-70 区间 |
64+
| E-06 | 所有场景 | 返回值始终 0-100 |
65+
| E-07 | 权重公式验证 | 场所*0.20 + 尺度*0.30 + 信誉*0.25 + 交付*0.25 |
66+
67+
**尺度递进函数**
68+
| 用例 | 场景 | 期望 |
69+
|------|------|------|
70+
| E-08 | 越界倾向=0 + 极度保护 + G级 | 概率 < 0.15 |
71+
| E-09 | 越界倾向=100 + 开放型 + G级 | 概率 > 0.40 |
72+
| E-13 | `下一尺度('G级')` | 返回 `'PG-13'` |
73+
| E-14 | `下一尺度('XXX')` | 返回 `'XXX'` (已达上限) |
74+
| E-15 | 尺度递进顺序链 | G级 -> PG-13 -> R级 -> NC-17 -> XXX |
75+
76+
**越界行为判定**
77+
| 用例 | 场景 | 期望 |
78+
|------|------|------|
79+
| E-16 | 影棚 + G级 + 纯艺术动机(低越界倾向) | 大概率返回 `null` |
80+
| E-17 | 个人住所 + XXX + 偷拍动机(高越界倾向) | 大概率返回非 null |
81+
| E-18 | 越界行为返回类型 | 必须是 `越界行为类型` 之一 |
82+
83+
**摄影师口碑评分**
84+
| 用例 | 场景 | 期望 |
85+
|------|------|------|
86+
| E-21 | 技术水平=100 + 业界大佬 + 0投诉 + 10回头客 | 评分接近 100 |
87+
| E-22 | 技术水平=0 + 惯犯 + 10投诉 + 0回头客 | 评分接近 0 |
88+
| E-23 | 投诉惩罚 | 每增加 1 投诉,评分 -5 |
89+
| E-25 | 返回值范围 | 始终 0-100 |
90+
91+
**摄影师筛选**
92+
| 用例 | 场景 | 期望 |
93+
|------|------|------|
94+
| E-26 | 极度保护 + [口碑60, 口碑40] | 过滤后只剩口碑60 |
95+
| E-28 | 开放型 + [口碑10, 口碑80] | 不过滤,按口碑降序排列 |
96+
97+
**泄露事件判定**
98+
| 用例 | 场景 | 期望 |
99+
|------|------|------|
100+
| E-31 | 风险值=10 + 频率=低 | 大概率不触发 |
101+
| E-32 | 风险值=90 + 频率=高 | 较高概率触发 |
102+
| E-36 | 频率系数验证 | 低=0.3, 中=0.6, 高=1.0 |
103+
104+
### 2.3 拍摄工作流单元测试 `__tests__/photographyNSFW/shootWorkflow.test.ts`
105+
106+
| 用例 | 场景 | 期望 |
107+
|------|------|------|
108+
| SW-01 | 创建新项目 | id 以 `shoot_` 开头,阶段='未开始' |
109+
| SW-02~05 | 推进第 1~最大回合 | 阶段正确递进 |
110+
| SW-05 | 推进到最大回合(5) | 阶段变为 `'已完成'`,触发 `拍摄完成` 事件 |
111+
| SW-07 | 尺度递进开启 + 模特同意 | 实际尺度+1,记录变更历史 |
112+
| SW-08 | 尺度递进开启 + 模特拒绝 | 信任度-5,记录 `尺度递进被拒` 事件 |
113+
| SW-09 | 越界行为触发 | 记录越界行为,信任度和安全感下降 |
114+
| SW-10 | 越界行为=威胁恐吓 | 信任度-30,安全感-20 |
115+
| SW-13 | 取消拍摄 | 阶段变为 `'已取消'` |
116+
| SW-14 | 投诉拍摄 | 阶段变为 `'已投诉'`,违规记录增加 |
117+
| SW-16 | 不可变性验证 | 原始对象不被修改 |
118+
119+
### 2.4 泄露工作流单元测试 `__tests__/photographyNSFW/leakWorkflow.test.ts`
120+
121+
| 用例 | 场景 | 期望 |
122+
|------|------|------|
123+
| LW-01 | 泄露事件关闭 | 返回 `null` |
124+
| LW-04 | 推进泄露传播 1 回合 | 传播回合+1,各项影响增加 |
125+
| LW-06 | 传播范围升级:小范围 -> 论坛传播 (回合>3) | 传播范围正确升级 |
126+
| LW-07 | 传播范围升级:论坛传播 -> 社交媒体 (回合>5) | 传播范围正确升级 |
127+
| LW-08 | 传播范围升级:社交媒体 -> 全网扩散 (回合>7, 主动泄露) | 传播范围正确升级 |
128+
| LW-09 | 泄露影响<20 | 事件状态变为 `'已平息'` |
129+
| LW-10 | 心理影响>=80 | 事件状态变为 `'已发酵'` |
130+
| LW-11 | 应对策略=法律维权 | 应对效果='有效' |
131+
| LW-12~16 | 不同应对策略组合 | 应对效果正确判定 |
132+
| LW-17 | 不可变性 | 原始事件和模特对象不被修改 |
133+
134+
---
135+
136+
## Phase 3: 集成测试(Vitest)
137+
138+
### 3.1 提示词构建函数测试 `__tests__/photographyNSFW/promptBuilders.test.ts`
139+
140+
| 用例 | 函数 | 场景 | 期望 |
141+
|------|------|------|------|
142+
| PB-01 | `构建玩法层约束` | 灰色地带 + 露骨 | 输出包含"灰色地带"和"露骨" |
143+
| PB-03 | `构建拍摄场景约束` | 酒店 + 私房暧昧 | 输出包含场所风险描述 |
144+
| PB-04 | `构建模特心理约束` | 信任度=90 | 输出包含"高度信任" |
145+
| PB-07 | `构建尺度递进约束` | 微暗 + G级 | 可用尺度只包含 G级和PG-13 |
146+
| PB-08 | `构建尺度递进约束` | 露骨 + G级 | 可用尺度包含全部5级 |
147+
| PB-14 | `构建BDSM联动约束` | 关系阶段=深入 + 2个把柄 | 输出包含"权力动态"和把柄数量 |
148+
| PB-15 | `构建写真NSFW完整叙事约束` | 全参数 | 输出包含所有子组件,用 `\n\n` 分隔 |
149+
| PB-16 | `构建写真NSFW完整叙事约束` | 空参数 | 只输出状态输出要求(最小输出) |
150+
151+
### 3.2 模块注册和参数提取集成测试 `__tests__/photographyNSFW/integration.test.ts`
152+
153+
| 用例 | 测试目标 | 场景 | 期望 |
154+
|------|----------|------|------|
155+
| INT-01 | 模块注册完整性 | 验证模块对象 | 包含 id, name, version, priority, masterToggleKey, defaultSettings 等 |
156+
| INT-02 | 参数提取 | gameState 不含写真系统 | 返回 `null` |
157+
| INT-03 | 参数提取 | 有写真系统但档案全空 | 返回 `null` |
158+
| INT-07 | 参数提取 - BDSM | 项目涉及BDSM=true | 返回包含BDSM关系阶段和把柄 |
159+
| INT-08 | 参数提取 - 多BDSM | 多个项目不同阶段 | 返回最深入的阶段 |
160+
| INT-09 | XML 解析 - 正常 | 合法 JSON | 正确解析 |
161+
| INT-11 | XML 解析 - 无效 | 非法 JSON | 不抛异常,静默失败 |
162+
| INT-12 | XML 解析 - 缺失 | 无标签 | 不做任何修改 |
163+
| INT-16 | 规范化设置集成 | 模块 normalizeSettings | 委托给 `规范化写真NSFW设置` |
164+
165+
---
166+
167+
## Phase 4: E2E 测试(Playwright)
168+
169+
### 4.1 仪表盘 E2E `e2e/photographyNSFW/dashboard.spec.ts`
170+
171+
| 用例 | 场景 | 期望 |
172+
|------|------|------|
173+
| E2E-01 | 桌面端仪表盘渲染 | 显示模特档案区域、拍摄项目追踪、泄露事件区域 |
174+
| E2E-02 | 移动端仪表盘渲染 | 移动视口下显示优化布局 |
175+
| E2E-03 | 空状态显示 | 显示"暂无数据"或空状态提示 |
176+
| E2E-04 | 模特卡片展开 | 点击后展开显示详细信息 |
177+
| E2E-05 | 拍摄项目进度条 | 显示进度条和彩色尺度指示器 |
178+
| E2E-06 | 泄露事件着色 | 根据严重程度显示不同颜色 |
179+
180+
### 4.2 设置面板 E2E `e2e/photographyNSFW/settings.spec.ts`
181+
182+
| 用例 | 场景 | 期望 |
183+
|------|------|------|
184+
| E2E-S01 | 设置面板渲染 | 显示 master toggle 和内容强度选择器 |
185+
| E2E-S02 | Master Toggle 联动 | 关闭后子选项变灰/禁用 |
186+
| E2E-S03 | 开关状态持久化 | 刷新页面后设置保持 |
187+
| E2E-S04 | 玩法层选择→灰色地带 | 道德选择开关变为可用 |
188+
| E2E-S05 | 玩法层选择→经营管理 | 道德选择开关变为禁用 |
189+
190+
---
191+
192+
## Phase 5: AI 响应测试
193+
194+
### 5.1 AI 响应验证 `e2e/photographyNSFW/aiResponse.spec.ts`
195+
196+
使用 `https://gcli.ggchan.dev/` API 端点发送写真上下文提示词,验证 AI 响应。
197+
198+
**API 调用封装**
199+
```typescript
200+
const API_ENDPOINT = 'https://gcli.ggchan.dev/';
201+
const API_KEY = process.env.GCLI_API_KEY || 'gg-gcli-RALFsIs47kRn7m3HKh98dTj0R48ccM2ln8sIVDc3OSA';
202+
203+
async function callAI(prompt: string): Promise<string> {
204+
const response = await fetch(API_ENDPOINT, {
205+
method: 'POST',
206+
headers: {
207+
'Content-Type': 'application/json',
208+
'Authorization': `Bearer ${API_KEY}`,
209+
},
210+
body: JSON.stringify({
211+
model: 'default',
212+
messages: [{ role: 'user', content: prompt }],
213+
}),
214+
});
215+
const data = await response.json();
216+
return data.choices[0].message.content;
217+
}
218+
219+
function hasValidPhotographyStatusTag(response: string): boolean {
220+
const match = response.match(/<写真系统状态>\s*([\s\S]*?)\s*<\/写真系统状态>/);
221+
if (!match) return false;
222+
try { JSON.parse(match[1]); return true; } catch { return false; }
223+
}
224+
```
225+
226+
| 用例 | 场景 | 期望 |
227+
|------|------|------|
228+
| AI-01 | 基本拍摄场景 | 响应包含合法的 `<写真系统状态>` 标签 |
229+
| AI-02 | 尺度递进场景 | 响应包含尺度变更的状态更新 |
230+
| AI-03 | 越界行为场景 | 响应包含越界行为记录和模特状态变化 |
231+
| AI-04 | 泄露事件场景 | 响应包含泄露事件状态更新 |
232+
| AI-05 | 标签 JSON 格式 | JSON 可正常解析 |
233+
| AI-06 | 无状态变化场景 | 响应可能不包含标签(符合规则) |
234+
| AI-07 | 多模块联动 | 写真 + BDSM 联动约束,响应包含跨模块状态更新 |
235+
236+
---
237+
238+
## Phase 6: 覆盖率验证
239+
240+
### 覆盖率目标
241+
242+
| 模块文件 | 目标行覆盖率 | 目标分支覆盖率 |
243+
|----------|-------------|---------------|
244+
| `models/photographyNSFW/normalization.ts` | 100% | 100% |
245+
| `hooks/useGame/photographyNSFWEngine.ts` | 90%+ | 85%+ |
246+
| `hooks/useGame/photographyShootWorkflow.ts` | 85%+ | 80%+ |
247+
| `hooks/useGame/photographyLeakWorkflow.ts` | 85%+ | 80%+ |
248+
| `prompts/runtime/photographyNSFW.ts` | 80%+ | 75%+ |
249+
| `modules/contemporary/photographyNSFW/registration.ts` | 75%+ | 70%+ |
250+
| **模块整体** | **>= 80%** | **>= 75%** |
251+
252+
---
253+
254+
## 风险与缓解
255+
256+
| 风险 | 影响 | 缓解措施 |
257+
|------|------|----------|
258+
| 概率函数测试不稳定 | 单元测试 flaky | 使用 `vi.spyOn(globalThis, 'random').mockReturnValue()` 固定随机种子 |
259+
| E2E 测试依赖完整游戏启动 | 执行缓慢 | `test.beforeAll` 一次性初始化,多个测试共享状态 |
260+
| AI 响应测试受网络波动影响 | flaky | 超时 30 秒,`test.retry(2)` 自动重试 |
261+
| XML 解析逻辑嵌入 responseCommandProcessor | 难以独立测试 | 将解析提取为独立纯函数 |
262+
| 提示词输出很长,断言脆弱 | 测试不稳定 | 使用 `toContain` 断言关键子串,不精确匹配全文 |
263+
| E2E 前置条件复杂 | 难以编写 | 创建 `setupPhotographyGame()` 辅助函数封装完整流程 |
264+
265+
---
266+
267+
## 文件清单
268+
269+
| 阶段 | 文件路径 | 类型 | 估计行数 |
270+
|------|----------|------|----------|
271+
| P1 | `__tests__/setup/mockStateFactory.ts` | 新增 | ~150 |
272+
| P1 | `__tests__/setup/xmlParserTestHelper.ts` | 新增 | ~50 |
273+
| P2 | `__tests__/photographyNSFW/normalization.test.ts` | 新增 | ~120 |
274+
| P2 | `__tests__/photographyNSFW/engine.test.ts` | 新增 | ~350 |
275+
| P2 | `__tests__/photographyNSFW/shootWorkflow.test.ts` | 新增 | ~200 |
276+
| P2 | `__tests__/photographyNSFW/leakWorkflow.test.ts` | 新增 | ~180 |
277+
| P3 | `__tests__/photographyNSFW/promptBuilders.test.ts` | 新增 | ~180 |
278+
| P3 | `__tests__/photographyNSFW/integration.test.ts` | 新增 | ~250 |
279+
| P4 | `e2e/photographyNSFW/dashboard.spec.ts` | 新增 | ~150 |
280+
| P4 | `e2e/photographyNSFW/settings.spec.ts` | 新增 | ~120 |
281+
| P5 | `e2e/photographyNSFW/aiResponse.spec.ts` | 新增 | ~150 |
282+
| - | `vitest.config.ts` | 修改 | +5 |
283+
284+
**总计:11 个新文件,1 个修改文件,约 1,900 行测试代码。**
285+
286+
---
287+
288+
## 实施顺序
289+
290+
1. **Phase 1** — 工厂函数(所有后续阶段的基础)
291+
2. **Phase 2** — normalization + engine(纯函数,零依赖)→ workflows(依赖 engine)
292+
3. **Phase 3** — prompt builders(独立)→ integration(依赖前面的测试工厂)
293+
4. **Phase 4** — E2E(需要项目可运行)
294+
5. **Phase 5** — AI 响应(依赖 API 端点)
295+
6. **Phase 6** — 覆盖率验证(最后验证)
296+
297+
## 成功标准
298+
299+
- [ ] 所有单元测试通过
300+
- [ ] 所有集成测试通过
301+
- [ ] E2E 测试在本地开发服务器上通过
302+
- [ ] AI 响应测试通过(API 端点可达时)
303+
- [ ] 模块整体行覆盖率 >= 80%
304+
- [ ] 所有测试遵循项目约定(中文命名、AAA 模式、描述性测试名)
305+
- [ ]`console.log` 残留在测试代码中

models/campusNSFW/sm.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,7 @@ export interface BDSM关系状态 {
117117
里程碑: BDSM里程碑[];
118118
安全词: string;
119119
底线列表: string[];
120+
121+
// 与写真模块联动(可选)
122+
照片把柄?: string[]; // 写真照片ID列表,可作为威胁把柄
120123
}

models/contemporary/photography/prompts/摄影师提示词.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ export function 生成越界试探提示词(params: {
103103

104104
const 试探话术: Record<string, string[]> = {
105105
'言语': [
106-
'"这个姿势很美,你试试"'",
107-
'"你身材真好,拍私房一定很好看"'",
108-
'"我给你多加钱,拍点大尺度的"'",
109-
'"放心,我拍过很多比你更开放的"'",
106+
'这个姿势很美,你试试',
107+
'你身材真好,拍私房一定很好看',
108+
'我给你多加钱,拍点大尺度的',
109+
'放心,我拍过很多比你更开放的',
110110
],
111111
'行为': [
112112
'以调整姿势为由触碰模特身体',

0 commit comments

Comments
 (0)