多人协作的需求验收管理系统 — 面向当前生产环境(Cloudflare Pages + D1)的架构说明
验收管家是一个面向业务验收场景的多人协作系统。产品经理(管理员)创建验收需求、维护用例,业务验收人各自独立填写验收结果,云端实时同步,管理员侧一键汇总看板与导出报表。
graph TB
subgraph Browser["浏览器(验收人/管理员)"]
HTML["uat.html
单文件前端(零构建)"]
LS["LocalStorage
离线缓存 + 身份持久化"]
end
subgraph CF["Cloudflare Pages"]
PF["Pages Functions
后端 API(14+ 端点)"]
D1["D1 (SQLite)
生产数据库"]
end
HTML -- "fetch /api/*" --> PF
PF -- "SQL 查询" --> D1
HTML -- "读写缓存" --> LS
LS -. "离线/弱网兜底" .-> HTML
技术栈:Cloudflare Pages + Pages Functions + D1 (SQLite),前端单文件 HTML(零构建),使用 SheetJS 导出 Excel。
erDiagram
projects ||--o{ testers : "1:N 按项目隔离"
projects ||--o{ test_cases : "1:N 用例模板"
projects ||--o{ shares : "1:N 分享链接"
testers ||--o{ results : "1:N 验收结果"
projects {
TEXT id PK "项目唯一标识"
TEXT name "项目名称"
TEXT description "描述"
TEXT owner "负责人"
TEXT status "active/archived"
}
testers {
INTEGER id PK "自增主键"
TEXT project_id FK "所属项目"
TEXT name "验收人姓名"
TEXT emp_id "工号"
TEXT org2 "二级机构"
TEXT org3 "三级机构"
}
results {
INTEGER id PK "自增主键"
TEXT project_id "所属项目"
TEXT tester_name "验收人姓名"
TEXT case_id "用例标识"
TEXT status "pass/fail/partial"
TEXT remark "备注(含截图)"
TEXT sync_time "同步时间"
}
test_cases {
INTEGER id PK "自增主键"
TEXT project_id FK "所属项目"
TEXT module "功能模块"
TEXT sub_module "二级模块"
TEXT scenario "验证场景"
TEXT expected "预期结果"
INTEGER sort_order "排序"
}
shares {
TEXT id PK "8位短链ID"
TEXT project_id "所属项目"
TEXT cases_data "用例JSON"
TEXT expires_at "过期时间"
}
| 表名 | 字段 | 类型 | 约束 | 用途 |
|---|---|---|---|---|
| projects | id | TEXT | PK | 项目唯一标识(如 conlian-uat-2026) |
| name | TEXT | NOT NULL | 项目名称 | |
| description | TEXT | DEFAULT '' | 项目描述 | |
| owner | TEXT | DEFAULT '' | 项目负责人 | |
| status | TEXT | DEFAULT 'active' | 项目状态 | |
| updated_at | TEXT | 自动更新 | 最后更新时间 | |
| testers | id | INTEGER | PK AUTO | 自增主键 |
| project_id | TEXT | FK, UNIQUE(project,name) | 所属项目 | |
| name | TEXT | NOT NULL | 验收人姓名 | |
| emp_id | TEXT | DEFAULT '' | 工号 | |
| org2 | TEXT | DEFAULT '' | 二级机构 | |
| org3 | TEXT | DEFAULT '' | 三级机构 | |
| updated_at | TEXT | 自动更新 | 信息更新时间 | |
| results | id | INTEGER | PK AUTO | 自增主键 |
| project_id | TEXT | UNIQUE(p,t,c) | 所属项目 | |
| tester_name | TEXT | UNIQUE(p,t,c) | 验收人姓名 | |
| case_id | TEXT | UNIQUE(p,t,c) | 用例标识(索引号) | |
| status | TEXT | DEFAULT '' | pass/fail/partial/空 | |
| remark | TEXT | DEFAULT '' | 备注内容 | |
| sync_time | TEXT | 自动 | 最近同步时间 | |
| test_cases | id | INTEGER | PK AUTO | 自增主键 |
| project_id | TEXT | FK | 所属项目 | |
| module | TEXT | DEFAULT '' | 功能模块 | |
| sub_module | TEXT | DEFAULT '' | 二级模块 | |
| scenario | TEXT | NOT NULL | 验证场景描述 | |
| expected | TEXT | DEFAULT '' | 预期效果 | |
| sort_order | INTEGER | DEFAULT 0 | 排序序号 | |
| shares | id | TEXT | PK | 8位短链ID |
| project_id | TEXT | NOT NULL | 所属项目 | |
| cases_data | TEXT | NOT NULL | 用例模板JSON数据 | |
| created_at | TEXT | 自动 | 创建时间 | |
| expires_at | TEXT | NOT NULL | 过期时间(30天) |
| 索引名 | 表 | 字段 | 用途 |
|---|---|---|---|
idx_results_project_tester | results | project_id, tester_name | 按项目+验收人查询结果 |
idx_results_case | results | project_id, case_id | 按项目+用例查询 |
idx_testers_project | testers | project_id | 按项目列出验收人 |
idx_test_cases_project | test_cases | project_id | 按项目列出用例 |
idx_shares_expires | shares | expires_at | 过期清理 |
sequenceDiagram
participant U as 用户浏览器
participant API as Pages Functions
participant D1 as D1 数据库
U->>API: POST /api/login {name, empId}
API->>API: 匹配 ADMIN_WHITELIST
(姓名或工号)
alt 命中白名单
API->>API: HMAC-SHA256 签发 token
(12h 有效期)
API-->>U: {isAdmin: true, token, name}
U->>U: 存储 token + testerInfo 到 LocalStorage
else 普通验收人
API-->>U: {isAdmin: false, name}
U->>U: 仅存储 testerInfo
end
sequenceDiagram
participant U as 用户浏览器
participant LS as LocalStorage
participant API as Pages Functions
participant D1 as D1 数据库
Note over U,D1: 上传(防抖 30s)
U->>LS: saveResults() 写入本地
U->>API: POST /api/data {results, testerInfo, testData}
API->>D1: INSERT OR REPLACE results(批量)
API-->>U: {success: true}
Note over U,D1: 加载(页面初始化)
U->>API: GET /api/data?project=X&tester=Y
API->>D1: SELECT FROM results
D1-->>API: rows
API-->>U: {results, testerInfo, syncTime}
U->>LS: 缓存到本地
Note over U,D1: 定时轮询(每 5 分钟)
U->>API: GET /api/meta?project=X&tester=Y
API-->>U: {syncTime, resultCount}
U->>U: 比较 syncTime 判断是否有更新
sequenceDiagram
participant Admin as 管理员
participant UI as 前端
participant API as Pages Functions
participant D1 as D1 数据库
Admin->>UI: 概览页点击"详情"
UI->>API: GET /api/data?project=X&tester=张三
API->>D1: SELECT FROM results WHERE tester_name='张三'
D1-->>API: rows
API-->>UI: {results, testerInfo}
UI->>UI: 保存 _originalResults
UI->>UI: 设置 _viewingOtherMode=true
UI->>UI: 替换 results 为张三的数据
UI->>UI: renderTable()(只读模式)
Note over UI: 返回列表时 resetViewingOtherMode() 恢复
sequenceDiagram
participant Admin as 管理员
participant API as Pages Functions
participant D1 as D1 数据库
participant User as 被分享人
Admin->>API: POST /api/share {cases: [...]}
API->>API: 生成 8 位 shareId
API->>D1: INSERT INTO shares (id, cases_data, expires_at)
API-->>Admin: {shareId: "abc12345"}
Admin->>Admin: 拼接分享 URL
Note over User,D1: 被分享人访问
User->>API: GET /api/share/abc12345
API->>D1: SELECT FROM shares WHERE id='abc12345'
D1-->>API: row
API->>API: 检查 expires_at 是否过期
API-->>User: {cases: [...], createdAt}
User->>User: 加载用例模板到页面
| 路径 | 方法 | 鉴权 | 用途 |
|---|---|---|---|
/api/login | POST | 公开 | 身份解析,命中白名单签发管理员 token |
/api/data | GET | 公开 | 读取指定验收人的验收数据 |
/api/data | POST | 公开 | 写入验收数据(按 tester 隔离) |
/api/meta | GET | 公开 | 项目元信息 / 单验收人状态查询 |
/api/aggregate | GET | 管理员 | 聚合所有验收人数据 + 用例模板 |
/api/testers | GET | 管理员 | 获取验收人列表 |
/api/user/update | POST | 管理员 | 更新用户信息 |
/api/user/delete | POST | 管理员 | 删除用户及其数据 |
/api/share | POST | 管理员 | 创建用例分享链接(30 天有效) |
/api/share/:id | GET | 公开 | 读取分享链接内容 |
/api/projects | GET | 管理员 | 项目列表 |
/api/project/update | POST | 管理员 | 创建/更新项目 |
/api/project/delete | POST | 管理员 | 删除项目及全部关联数据 |
/api/ai/generate-cases | POST | 管理员 | AI 辅助生成验收用例 |
所有接口返回 JSON 格式,通用结构:
{ success: true, ...data } 或直接返回数据字段{ error: "错误描述" } + 对应 HTTP 状态码(400/401/404/500)body.signature 格式的 tokenX-Admin-Token管理员身份由服务端环境变量 ADMIN_WHITELIST 控制,配置在 wrangler.toml 中:
ADMIN_WHITELIST = "张三,100234,吴美玲,1010001596"
| 功能 | 验收人 | 管理员 |
|---|---|---|
| 提交/查看自己的验收结果 | OK | OK |
| 查看数据汇总看板(概览页) | - | OK |
| 查看所有验收人明细 | - | OK |
| 新增/删除/导入用例 | - | OK |
| 导出汇总 Excel/JSON | - | OK |
| 用户管理 | 仅自己 | 全部用户 |
| 查看他人验收详情 | - | OK(只读) |
管理员在概览页点击某验收人的"详情"时,前端进入 _viewingOtherMode:
viewTesterDetails(name) 加载该验收人数据,保存原始 results 到 _originalResultsresetViewingOtherMode(),恢复原始数据和编辑权限| 页面 ID | 名称 | URL Hash | 说明 |
|---|---|---|---|
pageCase | 验收列表 | #list | 需求项目列表,展示所有项目入口 |
pageDetail | 验收详情 | #detail | 用例表格 + 逐条填写验收结果(主工作区) |
pageOverview | 需求概览 | #overview | 验收进度看板 + 用例管理(Tab 切换) |
pageCaseManage | 用例管理 | #cases | 用例增删改 + 排序(仅管理员) |
pageAccount | 用户管理 | #account | 验收人增删改(管理员管全部,验收人管自己) |
左侧固定侧边栏(可折叠),包含两个菜单项:
底部显示当前用户角色(管理员/验收人),点击可切换账号。
| 变量 | 类型 | 用途 |
|---|---|---|
results | Object | 当前验收人的所有验收结果 {caseId: {status, remark}} |
TEST_DATA | Array | 用例模板数据(从 share 链接或云端加载) |
_isAdminMode | Boolean | 当前是否为管理员模式 |
_viewingOtherMode | Boolean | 是否正在查看他人数据 |
_viewingOtherName | String | 正在查看的验收人姓名 |
PROJECT_ID | String | 当前项目 ID(从 URL ?project= 参数获取) |
API_BASE | String | API 基础地址(默认当前域名,可通过 ?api= 覆盖) |