Files
lingo/docs/04-architecture.md
T
ilaandClaude Opus 4.8 b9bcbf7e6b docs: 补全 AI 入口/API/路由/现状文档并对齐一致性
新增(Codex 起草,本次纳入并校对):
- AGENTS.md:仓库级 AI 强约束入口
- docs/00-ai-start-here.md:AI 开发入口与导航
- docs/api.md:账号/生词本/进度 API 合约草案
- docs/routes.md:go-app 页面路由与组件归属
- docs/current-state.md:当前实现状态

补充与对齐:
- 验证命令补 bash(WSL/Linux 为主,PowerShell 为备):00-ai-start-here、AGENTS
- 04-architecture:新增第六节"项目结构(包布局)"+ 3.2 建表 SQL 草案
- docs/README 导航补 06-tasks;CLAUDE 目录树补 AGENTS/scripts/design_mockups
- 00-ai-start-here 去除已过时的"需要补齐"段
- 06-tasks:Phase 2/3/4/5 交叉引用 api.md / routes.md / 包布局

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:30:14 +08:00

8.2 KiB
Raw Blame History

架构设计

本文讲"怎么把技术栈搭起来":系统结构、前后端职责、数据模型、技术难点、开发顺序。 具体用了哪些框架 / 库 / 数据库 / 部署方式,见 技术栈。

一、系统结构

┐
│  用户浏览器 (大屏手机 / 平板)
│    │
│    ▼  安装为 PWA,离线缓存
│  前端 go-app (app.wasm)
│    - 课程列表 / 播放器 / 字幕同步 / 生词本 UI
│    - Service Worker 缓存音频与文稿
│    │
│    ▼  REST API (JSON over HTTPS)
│  后端 Go net/http
│    - 账号鉴权 / 进度 / 生词本
│    │
│    ▼
│  SQLite 数据库
┘

二、前后端职责划分

前端(go-app / wasm)

  • 课程列表、音频播放器
  • 字幕同步高亮 + 点击跳转(核心,自写)
  • 生词本界面
  • PWA manifest + Service Worker

后端(net/http)

  • 用户注册 / 登录 / 鉴权(httpOnly Session Cookie)
  • 生词本增删查
  • 学习进度读写
  • (静态音频与文稿可由 go-app Handler 或 CDN 提供)

三、数据模型

数据分两类:内容数据(静态、只读) 与 用户数据(动态、需后端)。

3.1 内容数据 — 静态 episodes.json(前端直接读,不入库)

实际素材已存在:family-album-usa/episodes.json(约 700KB)+ family-album-usa/audio/*.mp3。 结构为 episode(集)→ act(幕)→ line(句) 三级(以下为真实结构):

{
  "meta": { "version": "1.0.0", "title": "走遍美国 Family Album U.S.A.",
            "totalEpisodes": 26, "totalActs": 78, "audioActs": 49, "lastUpdated": "2026-05-29" },
  "episodes": [
    {
      "id": 1, "title": "46 Linden Street", "titleCn": "林登大街46号",
      "acts": [
        {
          "act": 1, "label": "Act I",
          "audio": "/family-album/audio/u0101.mp3",
          "hasAudio": true, "lineCount": 86,
          "lines": [
            { "t": 34.18, "en": "Excuse me. My name is Richard Stewart." }
          ]
        }
      ]
    }
  ]
}

要点(以实际数据为准,勿臆测字段):

  • 集 episode:id / title / titleCn(26 集全部有中文标题)/ acts[]
  • 幕 act:act 序号 / label / audio(服务路径 /family-album/audio/u{集}{幕}.mp3)/ hasAudio / lineCount / lines[]
  • 句 line:只有 { t, en } 两个字段。t 为音频时间戳(秒),en 为英文原句。无 zh 字段——行级中文翻译目前不存在,未来要补需扩展 schema
  • 规模:26 集 / 78 个 act 条目,其中 49 个 hasAudio=true;audio/ 目录共 50 个 mp3,合计 6335 句
  • ⚠️ 路径映射:JSON 里的 /family-album/audio/... 是对外服务路径,对应磁盘目录 family-album-usa/audio/,后端路由需做这层映射
  • ⚠️ audio/episode12_act2.mp3 命名不符合 u####.mp3 规则且未被 JSON 引用,属游离文件,接入时需核对

多教材扩展:未来接入新概念英语等,可沿用「教材 → 单元 → 句子」三级抽象——「走遍美国」即一个教材。新教材未必每句有时间戳,line 的 t 允许缺省。第一版无需为此提前建库,前端按 JSON 渲染即可。

3.2 用户数据 — SQLite(需账号,后端读写)

仅存「用户态」,内容数据不入库:

users — id, email, password_hash, created_at

progress — user_id, course_id, episode_id, act, last_position(秒), completed, updated_at

vocab — id, user_id, course_id, word, episode_id, act, context_sentence, created_at

course_id 是前向兼容的预留列:MVP 只有「走遍美国」一个教材,建表时即带上此列、固定默认 "family-album"。 目的是避免将来接入新概念英语等时,对已产生的用户进度/生词数据做痛苦的 schema 迁移(用户数据不可重生,内容数据可重生)。 ⚠️ MVP 阶段仅留列、填默认值,不写任何多教材逻辑(不建 courses 表、不改内容 JSON、不做按教材过滤)——多教材接入是 V2+,见 任务看板 待办池。

建表草案(SQLite,T-301 实现时以此为基准,可按需微调约束):

CREATE TABLE users (
  id            INTEGER PRIMARY KEY,
  email         TEXT UNIQUE NOT NULL,
  password_hash TEXT NOT NULL,
  created_at    TEXT NOT NULL
);

CREATE TABLE progress (
  user_id       INTEGER NOT NULL,
  course_id     TEXT NOT NULL DEFAULT 'family-album',
  episode_id    INTEGER NOT NULL,
  act           INTEGER NOT NULL,
  last_position REAL NOT NULL DEFAULT 0,   -- 秒
  completed     INTEGER NOT NULL DEFAULT 0, -- 0/1
  updated_at    TEXT NOT NULL,
  PRIMARY KEY (user_id, course_id, episode_id, act)
);

CREATE TABLE vocab (
  id               INTEGER PRIMARY KEY,
  user_id          INTEGER NOT NULL,
  course_id        TEXT NOT NULL DEFAULT 'family-album',
  word             TEXT NOT NULL,
  episode_id       INTEGER NOT NULL,
  act              INTEGER NOT NULL,
  context_sentence TEXT,
  created_at       TEXT NOT NULL
);

功能模块化:字幕同步、生词本、播放器做成可复用组件,不同教材按需启用(走遍美国用字幕同步;新概念可能更重单词表与语法)。

四、关键技术难点

难点 说明 应对
字幕同步 音频 currentTime 匹配 t,逐句高亮 + 点句跳转 先出最小原型验证
Audio API 互操 go-app 需通过 syscall/js 调浏览器 封装成可复用组件
wasm 首屏体积 Go wasm 最小约 2MB,首屏较慢 离线缓存后续访问快
离线音频 大体积音频缓存策略 Service Worker 按需缓存

五、推荐开发顺序

  1. 先写「音频 + 时间戳字幕同步」最小原型(验证最难一环)
  2. 搭课程列表 + 播放页骨架
  3. 后端账号体系 + 鉴权
  4. 生词本(前端 UI + 后端 API)
  5. 进度同步
  6. PWA manifest + Service Worker 离线化
  7. (V2)复读 / 测验 / 打卡

原则:先验证最难的字幕同步,跑通再往上搭架子,避免先搭一堆架构最后卡在核心功能。

六、项目结构(建议包布局)

go-app 同一份代码两处运行:编译成 wasm 在浏览器跑(app.RunWhenOnBrowser()),编译成普通二进制在服务器跑(起 http.Server + app.Handler)。因此前端组件包必须能进 wasm,而后端包(net/http、SQLite、文件 IO)不能进 wasm——用构建标签 //go:build !wasm(或单独的非 js 包,只被 server 入口引用)隔离。

建议布局(T-001 起逐步建立,不必一次建全):

lingo/
├── go.mod
├── main.go              # 装配入口:注册页面组件 + RunWhenOnBrowser;server 侧起 http + app.Handler
├── pages/               # go-app 前端组件(进 wasm)
│   ├── list.go          # 课程列表页(EpisodeList)
│   ├── play.go          # 播放页(TranscriptView)
│   ├── player.go        # AudioPlayer:syscall/js 封装浏览器 Audio API
│   ├── vocab.go         # 生词本页
│   └── auth.go          # 登录/注册(AuthForm)
├── content/             # episodes.json 加载与类型定义(T-003,前后端共用,纯数据可进 wasm)
│   └── content.go
├── server/              # 仅服务器端,//go:build !wasm 隔离,不进 wasm
│   ├── api.go           # /api/*(账号 / 生词 / 进度),合约见 docs/api.md
│   ├── auth.go          # httpOnly Session Cookie
│   ├── db.go            # SQLite 建表与读写(见 3.2 建表草案)
│   └── static.go        # /family-album/* → 磁盘 family-album-usa/ 的路径映射
└── web/                 # 静态资源
    ├── app.css          # 纯 CSS(可迁移自 design_mockups/ 的 :root + 组件样式)
    ├── manifest.webmanifest
    └── sw.js            # Service Worker(Phase 6)
  • 页面路由与组件归属详见 routes.md;后端接口形状详见 api.md。
  • content/ 只放数据结构与加载,保持可进 wasm(前端列表/播放页要用),不要在其中引入 server-only 依赖。