376 lines
9.1 KiB
Markdown
376 lines
9.1 KiB
Markdown
|
|
## 1. 架构设计
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TD
|
|||
|
|
A[用户浏览器] --> B[React前端应用]
|
|||
|
|
B --> C[Express后端服务]
|
|||
|
|
C --> D[SQLite数据库]
|
|||
|
|
C --> E[文件存储服务]
|
|||
|
|
|
|||
|
|
subgraph "前端层"
|
|||
|
|
B
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "后端服务层"
|
|||
|
|
C
|
|||
|
|
E
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "数据存储层"
|
|||
|
|
D
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. 技术选型
|
|||
|
|
|
|||
|
|
- **前端框架**:React@18 + TypeScript
|
|||
|
|
- **初始化工具**:vite-init
|
|||
|
|
- **UI组件库**:Ant Design@5
|
|||
|
|
- **状态管理**:React Context + useReducer
|
|||
|
|
- **后端框架**:Express@4 + TypeScript
|
|||
|
|
- **数据库**:SQLite3
|
|||
|
|
- **文件处理**:multer + xlsx
|
|||
|
|
- **数据验证**:Joi
|
|||
|
|
- **开发工具**:nodemon + concurrently
|
|||
|
|
|
|||
|
|
## 3. 路由定义
|
|||
|
|
|
|||
|
|
### 前端路由
|
|||
|
|
| 路由 | 用途 |
|
|||
|
|
|------|------|
|
|||
|
|
| / | 用户答题首页 |
|
|||
|
|
| /quiz | 答题页面 |
|
|||
|
|
| /result/:id | 答题结果页面 |
|
|||
|
|
| /admin/login | 管理员登录 |
|
|||
|
|
| /admin/dashboard | 管理后台首页 |
|
|||
|
|
| /admin/questions | 题库管理 |
|
|||
|
|
| /admin/config | 抽题配置 |
|
|||
|
|
| /admin/statistics | 数据统计 |
|
|||
|
|
|
|||
|
|
### 后端API路由
|
|||
|
|
| 路由 | 方法 | 用途 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| /api/users | POST | 创建用户 |
|
|||
|
|
| /api/users/:id | GET | 获取用户信息 |
|
|||
|
|
| /api/questions/import | POST | Excel导入题目 |
|
|||
|
|
| /api/questions | GET | 获取题目列表 |
|
|||
|
|
| /api/questions | POST | 添加单题 |
|
|||
|
|
| /api/questions/:id | PUT | 更新题目 |
|
|||
|
|
| /api/questions/:id | DELETE | 删除题目 |
|
|||
|
|
| /api/quiz/generate | POST | 生成随机试卷 |
|
|||
|
|
| /api/quiz/submit | POST | 提交答题 |
|
|||
|
|
| /api/quiz/results/:userId | GET | 获取用户答题记录 |
|
|||
|
|
| /api/admin/login | POST | 管理员登录 |
|
|||
|
|
| /api/admin/statistics | GET | 获取统计数据 |
|
|||
|
|
| /api/admin/config | GET/PUT | 获取/更新抽题配置 |
|
|||
|
|
|
|||
|
|
## 4. API接口定义
|
|||
|
|
|
|||
|
|
### 4.1 用户相关接口
|
|||
|
|
|
|||
|
|
#### 创建用户
|
|||
|
|
```
|
|||
|
|
POST /api/users
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请求参数:
|
|||
|
|
| 参数名 | 类型 | 必填 | 描述 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| name | string | 是 | 用户姓名,2-20位中英文 |
|
|||
|
|
| phone | string | 是 | 手机号,11位数字,1开头,第二位3-9 |
|
|||
|
|
|
|||
|
|
响应参数:
|
|||
|
|
| 参数名 | 类型 | 描述 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| id | string | 用户唯一标识 |
|
|||
|
|
| name | string | 用户姓名 |
|
|||
|
|
| phone | string | 手机号 |
|
|||
|
|
| createdAt | string | 创建时间 |
|
|||
|
|
|
|||
|
|
请求示例:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"name": "张三",
|
|||
|
|
"phone": "13812345678"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 生成随机试卷
|
|||
|
|
```
|
|||
|
|
POST /api/quiz/generate
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请求参数:
|
|||
|
|
| 参数名 | 类型 | 必填 | 描述 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| userId | string | 是 | 用户ID |
|
|||
|
|
|
|||
|
|
响应参数:
|
|||
|
|
| 参数名 | 类型 | 描述 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| questions | array | 题目数组 |
|
|||
|
|
| totalScore | number | 总分 |
|
|||
|
|
| timeLimit | number | 时间限制(分钟) |
|
|||
|
|
|
|||
|
|
### 4.2 题库管理接口
|
|||
|
|
|
|||
|
|
#### Excel导入题目
|
|||
|
|
```
|
|||
|
|
POST /api/questions/import
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请求参数(form-data):
|
|||
|
|
| 参数名 | 类型 | 必填 | 描述 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| file | file | 是 | Excel文件 |
|
|||
|
|
|
|||
|
|
响应参数:
|
|||
|
|
| 参数名 | 类型 | 描述 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| success | boolean | 导入是否成功 |
|
|||
|
|
| imported | number | 成功导入数量 |
|
|||
|
|
| errors | array | 错误信息数组 |
|
|||
|
|
|
|||
|
|
#### 添加单题
|
|||
|
|
```
|
|||
|
|
POST /api/questions
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请求参数:
|
|||
|
|
| 参数名 | 类型 | 必填 | 描述 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| content | string | 是 | 题目内容 |
|
|||
|
|
| type | string | 是 | 题型:single/multiple/judgment/text |
|
|||
|
|
| options | array | 条件 | 选项数组(单选/多选) |
|
|||
|
|
| answer | string/array | 是 | 标准答案 |
|
|||
|
|
| score | number | 是 | 分值 |
|
|||
|
|
|
|||
|
|
### 4.3 答题相关接口
|
|||
|
|
|
|||
|
|
#### 提交答题
|
|||
|
|
```
|
|||
|
|
POST /api/quiz/submit
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请求参数:
|
|||
|
|
| 参数名 | 类型 | 必填 | 描述 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| userId | string | 是 | 用户ID |
|
|||
|
|
| answers | array | 是 | 答案数组 |
|
|||
|
|
|
|||
|
|
答案对象结构:
|
|||
|
|
| 参数名 | 类型 | 描述 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| questionId | string | 题目ID |
|
|||
|
|
| userAnswer | string/array | 用户答案 |
|
|||
|
|
| score | number | 得分 |
|
|||
|
|
|
|||
|
|
响应参数:
|
|||
|
|
| 参数名 | 类型 | 描述 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| totalScore | number | 总得分 |
|
|||
|
|
| correctCount | number | 正确题数 |
|
|||
|
|
| totalCount | number | 总题数 |
|
|||
|
|
|
|||
|
|
## 5. 服务端架构图
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TD
|
|||
|
|
A[客户端请求] --> B[路由层]
|
|||
|
|
B --> C[中间件层]
|
|||
|
|
C --> D[控制器层]
|
|||
|
|
D --> E[服务层]
|
|||
|
|
E --> F[数据访问层]
|
|||
|
|
F --> G[(SQLite数据库)]
|
|||
|
|
|
|||
|
|
subgraph "Express服务端"
|
|||
|
|
B
|
|||
|
|
C
|
|||
|
|
D
|
|||
|
|
E
|
|||
|
|
F
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.1 分层设计
|
|||
|
|
- **路由层**:定义API端点,处理请求分发
|
|||
|
|
- **中间件层**:身份验证、错误处理、请求日志
|
|||
|
|
- **控制器层**:处理HTTP请求,调用服务层
|
|||
|
|
- **服务层**:业务逻辑处理,数据验证
|
|||
|
|
- **数据访问层**:数据库操作,SQL查询执行
|
|||
|
|
|
|||
|
|
## 6. 数据模型
|
|||
|
|
|
|||
|
|
### 6.1 数据库实体关系图
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
erDiagram
|
|||
|
|
USER ||--o{ QUIZ_RECORD : takes
|
|||
|
|
USER ||--o{ QUIZ_ANSWER : submits
|
|||
|
|
QUESTION ||--o{ QUIZ_ANSWER : answered
|
|||
|
|
QUESTION ||--o{ QUIZ_CONFIG : included
|
|||
|
|
|
|||
|
|
USER {
|
|||
|
|
string id PK
|
|||
|
|
string name
|
|||
|
|
string phone
|
|||
|
|
datetime created_at
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QUESTION {
|
|||
|
|
string id PK
|
|||
|
|
string content
|
|||
|
|
string type
|
|||
|
|
string options
|
|||
|
|
string answer
|
|||
|
|
integer score
|
|||
|
|
datetime created_at
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QUIZ_RECORD {
|
|||
|
|
string id PK
|
|||
|
|
string user_id FK
|
|||
|
|
integer total_score
|
|||
|
|
integer correct_count
|
|||
|
|
datetime created_at
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QUIZ_ANSWER {
|
|||
|
|
string id PK
|
|||
|
|
string record_id FK
|
|||
|
|
string question_id FK
|
|||
|
|
string user_answer
|
|||
|
|
integer score
|
|||
|
|
boolean is_correct
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QUIZ_CONFIG {
|
|||
|
|
string id PK
|
|||
|
|
string config_type
|
|||
|
|
string config_value
|
|||
|
|
datetime updated_at
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 数据定义语言
|
|||
|
|
|
|||
|
|
#### 用户表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE users (
|
|||
|
|
id TEXT PRIMARY KEY,
|
|||
|
|
name TEXT NOT NULL CHECK(length(name) >= 2 AND length(name) <= 20),
|
|||
|
|
phone TEXT UNIQUE NOT NULL CHECK(length(phone) = 11 AND phone LIKE '1%' AND substr(phone, 2, 1) BETWEEN '3' AND '9'),
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_users_phone ON users(phone);
|
|||
|
|
CREATE INDEX idx_users_created_at ON users(created_at);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 题目表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE questions (
|
|||
|
|
id TEXT PRIMARY KEY,
|
|||
|
|
content TEXT NOT NULL,
|
|||
|
|
type TEXT NOT NULL CHECK(type IN ('single', 'multiple', 'judgment', 'text')),
|
|||
|
|
options TEXT, -- JSON格式存储选项
|
|||
|
|
answer TEXT NOT NULL,
|
|||
|
|
score INTEGER NOT NULL CHECK(score > 0),
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_questions_type ON questions(type);
|
|||
|
|
CREATE INDEX idx_questions_score ON questions(score);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 答题记录表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE quiz_records (
|
|||
|
|
id TEXT PRIMARY KEY,
|
|||
|
|
user_id TEXT NOT NULL,
|
|||
|
|
total_score INTEGER NOT NULL,
|
|||
|
|
correct_count INTEGER NOT NULL,
|
|||
|
|
total_count INTEGER NOT NULL,
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_quiz_records_user_id ON quiz_records(user_id);
|
|||
|
|
CREATE INDEX idx_quiz_records_created_at ON quiz_records(created_at);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 答题答案表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE quiz_answers (
|
|||
|
|
id TEXT PRIMARY KEY,
|
|||
|
|
record_id TEXT NOT NULL,
|
|||
|
|
question_id TEXT NOT NULL,
|
|||
|
|
user_answer TEXT NOT NULL,
|
|||
|
|
score INTEGER NOT NULL,
|
|||
|
|
is_correct BOOLEAN NOT NULL,
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
FOREIGN KEY (record_id) REFERENCES quiz_records(id),
|
|||
|
|
FOREIGN KEY (question_id) REFERENCES questions(id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_quiz_answers_record_id ON quiz_answers(record_id);
|
|||
|
|
CREATE INDEX idx_quiz_answers_question_id ON quiz_answers(question_id);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 系统配置表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE system_configs (
|
|||
|
|
id TEXT PRIMARY KEY,
|
|||
|
|
config_type TEXT UNIQUE NOT NULL,
|
|||
|
|
config_value TEXT NOT NULL,
|
|||
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
-- 初始化抽题配置
|
|||
|
|
INSERT INTO system_configs (id, config_type, config_value) VALUES
|
|||
|
|
('1', 'quiz_config', '{"singleRatio":40,"multipleRatio":30,"judgmentRatio":20,"textRatio":10,"totalScore":100}');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.3 索引优化
|
|||
|
|
- 用户手机号索引:加速用户查询和防重复验证
|
|||
|
|
- 题目类型索引:优化按类型筛选题目性能
|
|||
|
|
- 答题记录时间索引:支持按时间范围查询统计
|
|||
|
|
- 外键索引:确保关联查询性能
|
|||
|
|
|
|||
|
|
## 7. 部署配置
|
|||
|
|
|
|||
|
|
### 7.1 环境变量
|
|||
|
|
```env
|
|||
|
|
# 服务器配置
|
|||
|
|
PORT=3000
|
|||
|
|
NODE_ENV=development
|
|||
|
|
|
|||
|
|
# 数据库配置
|
|||
|
|
DB_PATH=./data/survey.db
|
|||
|
|
|
|||
|
|
# 管理员配置
|
|||
|
|
ADMIN_USERNAME=admin
|
|||
|
|
ADMIN_PASSWORD=admin123
|
|||
|
|
|
|||
|
|
# 文件上传配置
|
|||
|
|
UPLOAD_MAX_SIZE=10MB
|
|||
|
|
UPLOAD_DIR=./uploads
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7.2 目录结构
|
|||
|
|
```
|
|||
|
|
survey-system/
|
|||
|
|
├── src/
|
|||
|
|
│ ├── frontend/ # React前端代码
|
|||
|
|
│ ├── backend/ # Express后端代码
|
|||
|
|
│ │ ├── controllers/ # 控制器
|
|||
|
|
│ │ ├── services/ # 服务层
|
|||
|
|
│ │ ├── models/ # 数据模型
|
|||
|
|
│ │ ├── middlewares/ # 中间件
|
|||
|
|
│ │ └── utils/ # 工具函数
|
|||
|
|
│ ├── shared/ # 共享类型定义
|
|||
|
|
│ └── data/ # 数据库文件
|
|||
|
|
├── uploads/ # 文件上传目录
|
|||
|
|
├── public/ # 静态资源
|
|||
|
|
└── dist/ # 构建输出目录
|
|||
|
|
```
|