193 lines
7.5 KiB
JSON
193 lines
7.5 KiB
JSON
{
|
||
"schema_version": 1,
|
||
"diagram_type": "architecture",
|
||
"meta": {
|
||
"title": "TallyNote 本地优先报销账本架构",
|
||
"locale": "zh-CN",
|
||
"output": "tallynote-architecture.html",
|
||
"quality_profile": "showcase",
|
||
"viewBox": [1360, 800],
|
||
"views": [
|
||
{
|
||
"id": "request-path",
|
||
"label": "主请求路径",
|
||
"focus": ["operator", "web", "api", "security", "expense", "db"],
|
||
"note": "从管理员在浏览器快速记账,到安全会话和 SQLite 持久化。"
|
||
},
|
||
{
|
||
"id": "attachment-consistency",
|
||
"label": "附件一致性",
|
||
"focus": ["api", "expense", "attachment", "filesystem", "db", "audit"],
|
||
"note": "查看 staging、内容校验、原子晋级与事务回滚如何保持记录和文件一致。"
|
||
},
|
||
{
|
||
"id": "export-recovery",
|
||
"label": "导出与恢复",
|
||
"focus": ["api", "export", "db", "filesystem", "janitor", "audit"],
|
||
"note": "跟踪筛选快照、异步 ZIP 构建、会话绑定下载和重启恢复。"
|
||
},
|
||
{
|
||
"id": "admin-governance",
|
||
"label": "管理员治理",
|
||
"focus": ["operator", "web", "api", "admin", "security", "audit"],
|
||
"note": "查看管理员初始化、停用、重置密码和全局审计边界。"
|
||
}
|
||
]
|
||
},
|
||
"components": [
|
||
{
|
||
"id": "operator",
|
||
"type": "external",
|
||
"label": "使用者 / 管理员",
|
||
"sublabel": "Chrome 浏览器",
|
||
"pos": [30, 250],
|
||
"size": [150, 80]
|
||
},
|
||
{
|
||
"id": "web",
|
||
"type": "frontend",
|
||
"label": "React 工作台",
|
||
"sublabel": "React 19 + Vite",
|
||
"tag": "桌面优先",
|
||
"pos": [220, 250],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "api",
|
||
"type": "backend",
|
||
"label": "Fastify REST API",
|
||
"sublabel": "/api/* + 静态托管",
|
||
"tag": "单进程入口",
|
||
"pos": [450, 250],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "security",
|
||
"type": "security",
|
||
"label": "认证与请求防护",
|
||
"sublabel": "Argon2id · Cookie · CSRF",
|
||
"tag": "HttpOnly / Origin",
|
||
"pos": [450, 50],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "expense",
|
||
"type": "backend",
|
||
"label": "账目业务服务",
|
||
"sublabel": "筛选 · 状态 · 乐观锁",
|
||
"tag": "至少一张凭证",
|
||
"pos": [680, 250],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "db",
|
||
"type": "database",
|
||
"label": "SQLite + Drizzle",
|
||
"sublabel": "WAL · 外键 · 事务",
|
||
"tag": "同机持久化",
|
||
"pos": [910, 250],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "attachment",
|
||
"type": "backend",
|
||
"label": "附件流水线",
|
||
"sublabel": "staging · 内容识别 · 晋级",
|
||
"tag": "20 MB / 文件",
|
||
"pos": [680, 430],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "filesystem",
|
||
"type": "cloud",
|
||
"label": "本地数据文件系统",
|
||
"sublabel": "files / staging / exports",
|
||
"tag": "同一卷 · UUID 路径",
|
||
"pos": [910, 430],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "export",
|
||
"type": "backend",
|
||
"label": "异步导出构建器",
|
||
"sublabel": "Excel · ZIP · manifest",
|
||
"tag": "会话绑定任务",
|
||
"pos": [910, 50],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "audit",
|
||
"type": "backend",
|
||
"label": "审计子系统",
|
||
"sublabel": "追加写入 · 前后值 · 请求 ID",
|
||
"tag": "只读查询",
|
||
"pos": [680, 50],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "admin",
|
||
"type": "backend",
|
||
"label": "管理员管理",
|
||
"sublabel": "初始化 · 停用 · 重置",
|
||
"tag": "最后管理员保护",
|
||
"pos": [450, 590],
|
||
"size": [190, 80]
|
||
},
|
||
{
|
||
"id": "janitor",
|
||
"type": "backend",
|
||
"label": "启动清理与恢复",
|
||
"sublabel": "孤儿文件 · 过期任务 · 会话",
|
||
"tag": "每 60 秒 + 重启",
|
||
"pos": [30, 590],
|
||
"size": [150, 80]
|
||
}
|
||
],
|
||
"boundaries": [
|
||
{
|
||
"kind": "region",
|
||
"label": "TallyNote 单实例本地运行时(localhost / Docker)",
|
||
"wraps": ["web", "api", "security", "expense", "db", "attachment", "filesystem", "export", "audit", "admin", "janitor"],
|
||
"pad": 28
|
||
},
|
||
{
|
||
"kind": "security-group",
|
||
"label": "受保护 API 边界",
|
||
"wraps": ["api", "security", "admin", "expense", "attachment", "export", "audit"],
|
||
"pad": 18
|
||
}
|
||
],
|
||
"connections": [
|
||
{ "id": "operator-web", "from": "operator", "to": "web", "label": "浏览器交互", "variant": "emphasis", "fromSide": "right", "toSide": "left", "labelAt": [200, 210] },
|
||
{ "id": "web-api", "from": "web", "to": "api", "label": "HTTP / REST", "variant": "emphasis", "fromSide": "right", "toSide": "left", "labelAt": [430, 210] },
|
||
{ "id": "security-api", "from": "security", "to": "api", "label": "Cookie + CSRF / Origin", "variant": "security", "fromSide": "bottom", "toSide": "top", "labelAt": [900, 195] },
|
||
{ "id": "api-expense", "from": "api", "to": "expense", "label": "账目路由", "fromSide": "right", "toSide": "left", "labelAt": [700, 210] },
|
||
{ "id": "expense-db", "from": "expense", "to": "db", "label": "SQLite transaction", "variant": "emphasis", "fromSide": "right", "toSide": "left", "labelAt": [890, 210] },
|
||
{ "id": "expense-attachment", "from": "expense", "to": "attachment", "label": "multipart 上传", "fromSide": "bottom", "toSide": "top", "labelAt": [880, 380] },
|
||
{ "id": "attachment-files", "from": "attachment", "to": "filesystem", "label": "staging → atomic promote", "variant": "emphasis", "fromSide": "right", "toSide": "left", "labelAt": [890, 405] },
|
||
{ "id": "api-export", "from": "api", "to": "export", "label": "snapshot → async job", "variant": "dashed", "fromSide": "right", "toSide": "left", "via": [[660, 290], [660, 25], [890, 25], [890, 90]], "labelAt": [775, 12] },
|
||
{ "id": "export-db", "from": "export", "to": "db", "label": "快照读取", "fromSide": "bottom", "toSide": "top", "labelAt": [1070, 195] },
|
||
{ "id": "export-files", "from": "export", "to": "filesystem", "label": "ZIP / Excel / manifest", "variant": "dashed", "fromSide": "right", "toSide": "bottom", "via": [[1280, 90], [1280, 700], [1005, 700]], "labelAt": [1170, 720] },
|
||
{ "id": "expense-audit", "from": "expense", "to": "audit", "label": "操作审计", "variant": "dashed", "fromSide": "top", "toSide": "bottom", "labelAt": [620, 195] },
|
||
{ "id": "admin-api", "from": "admin", "to": "api", "label": "管理员管理", "fromSide": "top", "toSide": "bottom", "labelAt": [700, 530] },
|
||
{ "id": "janitor-files", "from": "janitor", "to": "filesystem", "label": "孤儿文件 / 过期任务", "variant": "dashed", "fromSide": "bottom", "toSide": "bottom", "via": [[105, 735], [1005, 735]], "labelAt": [555, 715] }
|
||
],
|
||
"cards": [
|
||
{
|
||
"dot": "cyan",
|
||
"title": "请求与数据",
|
||
"items": ["前端通过统一 API 客户端携带会话 Cookie 和 CSRF 令牌", "账目写入、附件元数据和审计在 SQLite 事务内保持一致"]
|
||
},
|
||
{
|
||
"dot": "emerald",
|
||
"title": "文件可靠性",
|
||
"items": ["附件先落 staging,完成格式、大小和内容校验后原子晋级", "失败路径删除已晋级字节,启动时继续清理孤儿文件"]
|
||
},
|
||
{
|
||
"dot": "amber",
|
||
"title": "导出与治理",
|
||
"items": ["导出冻结不可变快照,后台生成 Excel、ZIP 和可选 manifest", "管理员变更、记录操作、附件和导出都进入只读审计日志"]
|
||
}
|
||
]
|
||
}
|