# 新项目复用教程 > 假设你要开一个新仓库,做一个类似的"Flask + 互动剧本 + 人格测试"项目。本教程告诉你从这个老仓库拿什么、怎么拿、怎么改。 --- ## 一、先搞清楚这个项目有哪几块可复用 | 模块 | 核心文件 | 能干嘛 | |------|---------|--------| | **Flask 后端骨架** | `app.py` | 路由、API、SQLite、gzip 压缩、静态缓存 | | **互动剧本引擎** | `data/seed_story.json` + `desktop.js` 中的 story 相关函数 | 读取 JSON 剧本 → 前端渲染场景文字 + 选项按钮 + 属性变化 | | **MathBTI 人格测试** | `data/seed_mathbti.json` + `templates/mathbti.html` + `mathbti.css/js` | 12 题测出 16 种结果,前端自包含可独立部署 | | **卡片/抽卡系统** | `desktop.js` 中的 card/gacha 相关函数 | 16 张人物卡,读知识卡片攒抽卡次数,保底机制 | | **内容构建脚本** | `scripts/build_*.py` | 用 Python dict 生成种子 JSON | | **Word 导出脚本** | `scripts/export_*.py` | 把人物设定导出为 docx | | **小说/设定原稿** | `docs/novel/*.txt` | 纯文本,可直接喂给任何文本处理流程 | --- ## 二、新项目规划建议 ### 场景 A:做一个新互动剧本项目(最常见) 你只需要:**Flask 骨架 + 剧本引擎 + 剧本数据**。 ``` new-project/ ├── app.py # 从老项目复制,改掉路径和 API key ├── data/ │ └── my_story.json # 你的新剧本(照着老格式写) ├── templates/ │ └── index.html # 从 desktop.html 简化,或自己写 ├── static/ │ ├── css/ │ └── js/ └── requirements.txt ``` ### 场景 B:做一个人格/心理测试项目 你只需要:**MathBTI 前端 + 数据格式**。 ``` new-project/ ├── data/ │ └── seed_mytest.json # 你的测试题+结果(照 mathbti 格式写) ├── templates/ │ └── test.html # 从 mathbti.html 改 ├── static/ │ ├── css/ │ └── js/ └── scripts/ └── build_mytest.py # 从 build_mathbti.py 改 ``` ### 场景 C:纯内容搬运(不要代码) 你只需要 `docs/` 和 `data/*.json` 里的文本和数据,用自己的技术栈重新实现前端。 --- ## 三、逐步操作 ### Step 1 — 拿 Flask 骨架 从老项目复制 `app.py`,然后改这几处: ```python # 1. 改 DeepSeek API Key(第 83 行附近) DEEPSEEK_API_KEY = "你的key" # 2. 改数据路径(第 30~40 行) # 删掉你不需要的路径变量,加上你自己的 DATA_DIR = os.path.join(BASE_DIR, "data").replace("\\", "/") MY_STORY_PATH = os.path.join(DATA_DIR, "my_story.json").replace("\\", "/") # 3. 改路由(第 258 行起) # 删掉不需要的 @app.route,加上你自己的 @app.route("/") def index(): return render_template("index.html") # 4. 改 API 函数 # 照着 api_story() 的写法,load 你的 JSON 返回给前端 ``` ### Step 2 — 拿剧本引擎 **数据格式**(这是最关键的,任何语言都能读): ```json { "title": "我的剧本", "start_node": "ch1_start", "nodes": { "ch1_start": { "scene": "场景描述文字。用 \\n 换行。", "character": "旁白", "choices": [ { "text": "选项A的文字", "next": "ch1_choice_a", "effects": { "str": 5 } }, { "text": "选项B的文字", "next": "ch1_choice_b", "effects": { "str": -3 } } ] }, "ch1_choice_a": { "scene": "你选了A之后的故事...", "character": "NPC", "choices": [...] } } } ``` **前端渲染逻辑**(从 `desktop.js` 提取的核心循环): ```javascript let storyData = null; let currentState = { currentNode: null, stats: {} }; async function loadStory() { const r = await fetch('/api/story'); storyData = (await r.json()).data; const start = storyData.start_node; const node = storyData.nodes[start]; renderNode(start); } function renderNode(nodeId) { const node = storyData.nodes[nodeId]; if (!node) return; currentState.currentNode = nodeId; localStorage.setItem('my_story_save', JSON.stringify(currentState)); // 渲染场景文字 document.getElementById('scene').textContent = node.scene || ''; // 渲染说话人 document.getElementById('character').textContent = node.character || ''; // 渲染选项按钮 const choicesEl = document.getElementById('choices'); choicesEl.innerHTML = ''; for (const choice of (node.choices || [])) { const btn = document.createElement('button'); btn.textContent = choice.text; btn.onclick = () => makeChoice(choice); choicesEl.appendChild(btn); } // 没有选项 = 故事结束 if (!node.choices || node.choices.length === 0) { choicesEl.innerHTML = '
故事结束
'; } } function makeChoice(choice) { // 应用属性变化 if (choice.effects) { for (const key in choice.effects) { currentState.stats[key] = (currentState.stats[key] || 0) + choice.effects[key]; } } // 跳到下一节点 renderNode(choice.next); } ``` **HTML 骨架**: ```html
``` ### Step 3 — 拿剧本构建脚本 从 `scripts/build_story.py` 复制,它提供了两个辅助函数让你不用手写 JSON: ```python # N(node_id, 场景文字, 说话人, 选项列表) # C(选项文字, 下一节点ID, 属性变化...) N("ch1_start", "你站在路口。", "旁白", [ C("往左走", "ch1_left", str=5), C("往右走", "ch1_right", str=-2), ]) ``` 在脚本末尾改输出路径后运行 `py scripts/build_story.py` 就会生成 JSON 文件。 ### Step 4 — 拿 MathBTI 测试 1. 复制 `templates/mathbti.html`、`static/css/mathbti.css`、`static/js/mathbti.js`、`data/seed_mathbti.json` 2. 前端逻辑在 `mathbti.js` 里,读 JSON → 逐题渲染 → 算 4 轴分数 → 映射到 16 种结果(4 位二进制) 3. 如果你要改题,直接改 `data/seed_mathbti.json`,或用 `scripts/build_mathbti.py` 重新生成 ### Step 5 — 拿 Word 导出脚本 `scripts/export_*.py` 用 `python-docx` 把设定文字导出为 Word。复制后改两处: - 脚本内的内容文字(硬编码在 Python dict/string 里) - 输出路径(脚本末尾,硬编码的绝对路径) ```bash py -m pip install python-docx py scripts/export_mathbti_doc.py ``` --- ## 四、内容文件怎么搬 | 你要什么 | 从哪拿 | 怎么用 | |---------|-------|--------| | 葫芦侠行剧本(8 章 93 节点) | `data/seed_story.json` | 直接当 JSON 读,格式见上 | | 数学少年线剧本(85 节点) | `docs/数学少年线_story.json` | 同上格式,但属性名不同(shuli/xiayi/xinzhi) | | MathBTI 全套数据(4 轴 12 题 16 结果) | `data/seed_mathbti.json` | 前端 fetch 后渲染问卷 | | 模拟器剧本 | `data/seed_simulator.json` | 同剧本格式,属性为 gpa/interest/skill | | 校友访谈数据 | `data/seed_alumni.json` | 线性叙事,four_years.nodes 数组 | | 16 位数学家肖像图 | `static/img/mathematicians/` | 文件名是 4 位二进制(0000~1111.png) | | 小说原稿 | `docs/novel/第0X章.txt` | 纯文本,随便用 | | 人物设定文档 | `docs/novel/数学家小传_*.md` | Markdown,随便用 | --- ## 五、新项目清单 开新仓库时按这个清单走: - [ ] 复制 `app.py`,改掉 API key、数据路径、路由 - [ ] 复制 `requirements.txt`,删掉不需要的包 - [ ] 建 `data/` 目录,放你的 JSON 种子数据 - [ ] 建 `templates/` 目录,放你的 HTML 模板 - [ ] 建 `static/css/` 和 `static/js/`,放样式和脚本 - [ ] 如果有剧本,照 Step 2 的格式写 JSON,或用 `build_story.py` 生成 - [ ] 如果有 Word 导出需求,复制 `scripts/export_*.py` 改内容 - [ ] `py app.py` 启动,浏览器验证 --- ## 六、常见坑 1. **旧 Flask 进程占端口**:改完代码后页面不更新?`Get-Process python | Stop-Process -Force` 杀掉旧进程再重启。 2. **剧本 JSON 里的中文**:文件编码必须是 UTF-8,`json.load(f)` 时加 `encoding='utf-8'`。 3. **build_story.py 输出路径**:脚本末尾的 `out_path` 是硬编码绝对路径,换机器必改。 4. **math_teen 故事文件位置**:`数学少年线_story.json` 在 `docs/` 不在 `data/`,`app.py` 第 37 行直接指向 docs 目录。 5. **localStorage 存档**:前端用 localStorage 保存剧本进度,key 名在 JS 里定义(如 `mb_story_save`),换项目时注意别冲突。 6. **静态资源缓存**:HTML 里引用 CSS/JS 带 `?v=数字` 版本号,改了前端代码后递增这个数字,否则浏览器用缓存。