From 073072f525c6cf40b71f0b86455716c081c0c80a Mon Sep 17 00:00:00 2001 From: huluxia <2056300012@qq.com> Date: Sat, 8 Aug 2026 18:45:07 +0800 Subject: [PATCH 1/3] update readme --- README.md | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 207 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e44efaf..cc98417 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,208 @@ -# Hulumath-Web-Demo +# 葫芦数学 · Hulu Math -Hulumath Web Demo Version -2026.8.8.15:58 -nimaaaaaaaa \ No newline at end of file +Flask 单体应用,提供网页端 + MathBTI 数学人格测试。一个 `app.py` 包含路由、API、数据库、大模型对接。 + +## 快速开始 + +```bash +# 1. 安装依赖 +py -m pip install -r requirements.txt + +# 2. 启动服务 +py app.py + +# 3. 浏览器访问 +# http://127.0.0.1:5000/ → 自动跳转 /web(桌面端网页) +# http://127.0.0.1:5000/mathbti → MathBTI 数学人格测试 +# http://127.0.0.1:5000/math_teen → 数学少年线独立剧本页 +``` + +## 目录结构 + +``` +Hulumath-Web-Demo/ +├── app.py # Flask 后端主文件(路由 + API + SQLite + DeepSeek 对接) +├── requirements.txt # Python 依赖 +├── vercel.json # Vercel 部署路由配置 +│ +├── templates/ # Jinja2 模板 +│ ├── desktop.html # 网页端(桌面布局,引用 desktop.css + desktop.js) +│ ├── mathbti.html # MathBTI 数学人格测试页(引用 mathbti.css + mathbti.js) +│ └── math_teen.html # 数学少年线独立播放页(自包含,无外部 CSS/JS) +│ +├── static/ # 静态资源 +│ ├── css/ +│ │ ├── desktop.css # 网页端样式 +│ │ └── mathbti.css # MathBTI 页样式 +│ ├── js/ +│ │ ├── desktop.js # 网页端逻辑 +│ │ └── mathbti.js # MathBTI 页逻辑 +│ └── img/ +│ └── mathematicians/ # 16 位数学人物肖像图片(0000~1111.png) +│ +├── data/ # 运行时数据(SQLite + 种子 JSON) +│ ├── miniapp_v3.db # SQLite 数据库(用户、收藏、历史、知识卡片) +│ ├── seed_videos.json # 视频种子数据(54 条) +│ ├── seed_story.json # 葫芦侠行剧本数据(8 章 93 节点) +│ ├── seed_simulator.json # 数学专业模拟器剧本数据 +│ ├── seed_mathbti.json # MathBTI 测试题目 + 16 种结果数据 +│ └── seed_alumni.json # 数学校友访谈数据 +│ +├── docs/ # 内容创作文档(不是运行时依赖) +│ ├── 数学少年线_story.json # 数学少年线剧本数据(被 app.py 直接读取) +│ ├── novel/ # 小说原稿(葫芦侠行 8 章 txt + 设定) +│ ├── 产品文档/ # PRD、需求、开发记录 +│ └── 会议记录/ # 需求评审纪要 +│ +└── scripts/ # 构建/导出脚本 + ├── build_mathbti.py + ├── build_story.py + ├── export_mathbti_doc.py + └── export_mathematicians_docx.py +``` + +## data/ 下的 JSON 文件说明 + +这些是应用运行时通过 API 读取的种子数据。`app.py` 在启动时根据路径加载它们。 + +| 文件 | 对应 API 路由 | 用途 | 数据结构概要 | +|------|-------------|------|-------------| +| `seed_videos.json` | `/api/videos` | 发现页视频列表 | 数组,每条含 id/title/author/discipline/video_url/duration_min 等 | +| `seed_story.json` | `/api/story` | 葫芦侠行互动剧本 | `{title, start_node, nodes: {node_id: {scene, character, choices: [{text, next, effects}]}}}` | +| `seed_simulator.json` | `/api/simulator` | 数学专业模拟器 | 同 story 结构,属性为 gpa/interest/skill | +| `seed_mathbti.json` | `/api/math_personality` | MathBTI 测试题+结果 | `{axes, clans, questions, results: {binary_code: {name, clan, stats5, ...}}}` | +| `seed_alumni.json` | `/api/alumni` | 数学校友访谈 | `{alumni: [{id, name, major, four_years: {nodes}}], meta}` | + +> **注意**:`数学少年线_story.json` 在 `docs/` 目录下,不在 `data/`。`app.py` 第 37 行直接指向 `docs/数学少年线_story.json`。 + +## docs/ 目录说明 + +`docs/` 是内容创作区,**不影响应用运行**(除了数学少年线剧本文件被直接读取)。 + +- `docs/novel/` — 葫芦侠行的小说原稿和设定文档(txt/md/docx) +- `docs/产品文档/` — 产品需求文档、PRD、开发记录 +- `docs/会议记录/` — 需求评审会议纪要 +- `docs/数学少年线_story.json` — 数学少年线互动剧本数据,**被 app.py 直接读取**,API `/api/story/math_teen` 返回此文件内容 + +### 在新项目中复用 docs/ + +如果你想在新项目里使用这些内容: + +1. **互动剧本**:复制 `docs/数学少年线_story.json` 或 `data/seed_story.json` 到新项目,按 `{start_node, nodes: {id: {scene, character, choices}}}` 格式读取即可。前端用 JS 根据 `currentNode` 渲染 `scene` 文字和 `choices` 按钮即可。 +2. **MathBTI 数据**:复制 `data/seed_mathbti.json`,包含 4 轴 12 题 + 16 种结果(含数学家小传、五维能力值、肖像图路径)。 +3. **小说原稿**:`docs/novel/第0X章.txt` 是纯文本,可直接用于任何文本展示场景。 + +## scripts/ 脚本说明 + +这些脚本用于**生成种子 JSON** 或**导出 Word 文档**。在项目根目录运行。 + +### build_mathbti.py — 生成 MathBTI 种子数据 + +将硬编码的题目、4 轴、4 族、16 种结果写入 `data/seed_mathbti.json`。 + +```bash +py scripts/build_mathbti.py +# 输出: data/seed_mathbti.json +# 打印: OK: seed_mathbti.json generated, 16 results, 4 clans +``` + +> 脚本内用 Python dict 写死全部数据,`json.dump` 输出。修改题目/结果后重跑即可刷新 `data/seed_mathbti.json`。 + +### build_story.py — 生成葫芦侠行剧本 + +根据大纲原文构建 8 章 93 节点的互动剧情 JSON。 + +```bash +py scripts/build_story.py +# 输出路径写死在脚本末尾(第 1006 行),需手动改为你的项目路径 +``` + +> 脚本提供 `N(node_id, scene, character, choices)` 和 `C(text, next, shuli, xiayi, xinzhi)` 两个辅助函数来逐节点构建故事。**输出路径硬编码在脚本中**,复制到新项目后需修改 `out_path`。 + +### export_mathbti_doc.py — 导出 Word 设定文档 + +将 16 位数学家设定导出为 Word 文档。 + +```bash +py scripts/export_mathbti_doc.py +# 输出: scripts/万理之城_设定圣经.docx +# 依赖: python-docx +``` + +### export_mathematicians_docx.py — 导出数学家小传 Word + +将 16 位数学家小传 + 六维雷达数据导出为 Word。 + +```bash +py scripts/export_mathematicians_docx.py +# 输出路径硬编码在脚本第 528 行,需手动修改 +# 依赖: python-docx +``` + +> **注意**:两个导出脚本的输出路径是硬编码的绝对路径,复制到新项目后需要修改脚本中的路径。 + +## 在新项目中使用 + +### 最小化移植(只要网页端) + +1. 复制 `app.py`、`templates/desktop.html`、`static/css/desktop.css`、`static/js/desktop.js`、`data/` 目录 +2. `py -m pip install flask requests` +3. `py app.py` + +### 只要互动剧本引擎 + +剧本数据格式是通用的 JSON,任何语言都能读: + +```python +import json +with open('seed_story.json', 'r', encoding='utf-8') as f: + story = json.load(f) + +current_node = story['start_node'] # 例如 'ch1_start' +node = story['nodes'][current_node] +print(node['scene']) # 场景文字 +print(node['character']) # 说话人 +for choice in node.get('choices', []): + print(choice['text']) # 选项文字 + print(choice['next']) # 下一节点 ID + print(choice.get('effects', {})) # 属性变化 +``` + +前端渲染逻辑参考 `static/js/desktop.js` 中的 `loadStory()` 和 `rSN()` 函数。 + +### 只要 MathBTI 测试 + +1. 复制 `templates/mathbti.html`、`static/css/mathbti.css`、`static/js/mathbti.js`、`data/seed_mathbti.json` +2. MathBTI 前端是自包含的,只需能 fetch 到 `seed_mathbti.json` 即可运行 + +## 环境要求 + +- Python 3.12+ +- Flask 3.x +- requests(DeepSeek AI 对话用) +- python-docx(仅 scripts/ 导出 Word 用) + +## API 路由一览 + +| 路由 | 方法 | 说明 | +|------|------|------| +| `/` | GET | 重定向到 `/web` | +| `/web` | GET | 桌面端网页 | +| `/mathbti` | GET | MathBTI 测试页 | +| `/math_teen` | GET | 数学少年线独立剧本页 | +| `/api/videos` | GET | 视频列表 | +| `/api/knowledge` | GET | 知识卡片列表 | +| `/api/knowledge/` | GET | 单个知识卡片详情 | +| `/api/story` | GET | 葫芦侠行剧本数据 | +| `/api/story/math_teen` | GET | 数学少年线剧本数据 | +| `/api/simulator` | GET | 模拟器剧本数据 | +| `/api/alumni` | GET | 校友访谈数据 | +| `/api/math_personality` | GET | MathBTI 题目+结果数据 | +| `/api/math_personality/calculate` | POST | 计算数学人格结果 | +| `/api/chat` | POST | AI 数学助教对话(对接 DeepSeek) | +| `/api/favorites` | GET/POST/DELETE | 收藏管理 | +| `/api/history` | GET/POST | 观看历史 | +| `/api/user/profile` | GET/POST | 用户资料 | +| `/api/user/stats` | GET | 用户统计 | +| `/api/simulator/save` | GET/POST | 模拟器存档 | +| `/data/` | GET | 直接访问 data/ 下的 JSON 文件 | -- 2.54.0 From 0e4fa065a33e30d0438150d1bbc85bc17482b52f Mon Sep 17 00:00:00 2001 From: huluxia <2056300012@qq.com> Date: Sat, 8 Aug 2026 18:48:29 +0800 Subject: [PATCH 2/3] update tutorial --- TUTORIAL.md | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 TUTORIAL.md diff --git a/TUTORIAL.md b/TUTORIAL.md new file mode 100644 index 0000000..d44ca2e --- /dev/null +++ b/TUTORIAL.md @@ -0,0 +1,256 @@ +# 新项目复用教程 + +> 假设你要开一个新仓库,做一个类似的"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=数字` 版本号,改了前端代码后递增这个数字,否则浏览器用缓存。 -- 2.54.0 From 351a6a2a410a7511ea829de253e41e456c0e665c Mon Sep 17 00:00:00 2001 From: SuperJacky6 Date: Sat, 8 Aug 2026 18:51:25 +0800 Subject: [PATCH 3/3] a'a'a aaa --- TUTORIAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TUTORIAL.md b/TUTORIAL.md index d44ca2e..a7e453d 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,6 +1,6 @@ # 新项目复用教程 -> 假设你要开一个新仓库,做一个类似的"Flask + 互动剧本 + 人格测试"项目。本教程告诉你从这个老仓库拿什么、怎么拿、怎么改。 +> 假设你要开一个新仓库,做一个类似的"Python(原Flask)+ 互动剧本 + 人格测试"项目。本教程告诉你从这个老仓库拿什么、怎么拿、怎么改。 --- -- 2.54.0