# 基于手机的招聘代理平台系统设计说明书
## 1 引言
### 1.1 项目背景
在互联网招聘场景中,企业普遍面临以下问题:
* 试题缺乏针对性,无法准确考察岗位所需能力;
* 试题缺乏时效性,不能反映最新项目技术栈或业务需求;
* 试题的录入与维护成本高,复用性差,题库质量难以持续优化。
为此,设计并实现一个**招聘试题代理定制服务平台**(下称“本平台”),以“岗位需求 + 项目场景 + 应聘者背景”为驱动,自动生成个性化试题并持续优化题库质量,为多家招聘企业提供 SaaS 级服务。
### 1.2 设计目标
* 提供**多角色协同**的在线招聘试题定制与考试平台;
* 支持**岗位级别的试卷自动生成**与个性化定制;
* 支持**在线答题、自动评分、结果分析**;
* 通过**统计与反馈驱动的算法**持续优化题库质量;
* 面向多租户企业,以云端部署形式提供服务。
## 2 业务概述与角色
### 2.1 业务角色
1. **招聘企业 HR**
* 维护岗位信息与能力要求;
* 配置试题策略(题量、题型比例、难度分布);
* 发布考试、查看统计报表;
* 对试题效果给出评价与反馈。
2. **应聘者 Candidate**
* 注册/登录平台;
* 选择目标岗位,领取并完成在线试卷;
* 查看成绩与解析;
* 对题目给出主观评价(难度、贴合度、清晰度等)。
3. **平台管理员 Admin**
* 审核企业接入与企业题库;
* 管理系统题库、试题标签、难度标注;
* 监控题目质量与淘汰策略执行;
* 管理模型训练与推荐策略。
4. **内部系统模块(逻辑角色)**
* **Question Service**:题库管理、题目 CRUD;
* **Exam Service**:考试流程管理、会话控制;
* **Scoring Service**:自动评分与统计;
* **ML / Recommendation Service**:试题生成与质量评估。
## 3 UML 视图
### 3.1 用例图(Use Case Diagram)
### 3.2 核心业务活动图(Activity Diagram)
### 3.3 时序图(Sequence Diagram)
### 3.4 类图(Class Diagram)
### 3.5 ER 图(实体–关系图)
## 4 数据结构与数据库设计
### 4.1 主要数据表概述
#### 表 `user`
| 字段名 | 类型 | 说明 |
| ------------- | ------------------- | ---------------------------- |
| id (PK) | INT | 用户 ID |
| company_id | INT (FK company.id) | 所属公司(应聘者可为空) |
| name | VARCHAR | 姓名 |
| email | VARCHAR (UNIQUE) | 登录邮箱 |
| role | ENUM | `HR` / `CANDIDATE` / `ADMIN` |
| password_hash | VARCHAR | 加密后的密码 |
| created_at | DATETIME | 注册时间 |
#### 表 `company`
| 字段名 | 类型 | 说明 |
| -------- | ------- | -------- |
| id (PK) | INT | 公司 ID |
| name | VARCHAR | 公司名称 |
| industry | VARCHAR | 所属行业 |
| size | VARCHAR | 规模(人数区间) |
#### 表 `candidate_profile`
| 字段名 | 类型 | 说明 |
| ---------------- | ------------ | ------------ |
| id (PK) | INT | 主键 |
| user_id (FK) | INT | 对应 `user.id` |
| degree | VARCHAR | 学历 |
| years_experience | INT | 工作年限 |
| skill_tags | JSON/VARCHAR | 技能标签集合 |
| resume_url | VARCHAR | 简历文件存储地址 |
#### 表 `job_position`
| 字段名 | 类型 | 说明 |
| --------------- | ------------ | --------- |
| id (PK) | INT | 岗位 ID |
| company_id (FK) | INT | 所属公司 |
| title | VARCHAR | 岗位名称 |
| level | VARCHAR | 岗位级别 |
| required_skills | JSON/VARCHAR | 要求技能标签 |
| description | TEXT | 岗位详细说明 |
| exam_policy_id | VARCHAR(FK) | 对应考试策略 ID |
#### 表 `exam_policy`
| 字段名 | 类型 | 说明 |
| --------------- | ------- | --------------- |
| id (PK) | VARCHAR | 策略 ID |
| total_questions | INT | 总题量 |
| easy_ratio | FLOAT | 容易题比例 |
| medium_ratio | FLOAT | 中等题比例 |
| hard_ratio | FLOAT | 困难题比例 |
| type_ratio | JSON | 题型比例(单选/多选/编程等) |
#### 表 `question`
| 字段名 | 类型 | 说明 |
| ---------- | ------- | ------------ |
| id (PK) | INT | 题目 ID |
| content | TEXT | 题干 |
| type | VARCHAR | 题目类型 |
| skill_tag | VARCHAR | 技能标签 |
| difficulty | INT | 难度等级 1–5 |
| source | VARCHAR | 来源(企业题/通用题等) |
| is_active | BOOLEAN | 是否在用 |
#### 表 `question_stats`
| 字段名 | 类型 | 说明 |
| --------------- | -------- | ---------------- |
| question_id(PK) | INT | 对应 `question.id` |
| used_count | INT | 被使用次数 |
| avg_score | FLOAT | 平均得分 |
| correct_rate | FLOAT | 正确率 |
| discrimination | FLOAT | 区分度 |
| feedback_score | FLOAT | 反馈评分均值 |
| last_used_at | DATETIME | 最近使用时间 |
#### 表 `exam_template`
| 字段名 | 类型 | 说明 |
| -------------- | ------- | --------- |
| id (PK) | INT | 试卷模板 ID |
| job_id (FK) | INT | 对应岗位 |
| policy_id (FK) | VARCHAR | 对应策略 |
| question_slots | JSON | 题目占位或题目列表 |
#### 表 `exam_session`
| 字段名 | 类型 | 说明 |
| ------------ | -------- | ------------------ |
| id (PK) | INT | 考试会话 ID |
| candidate_id | INT | 应聘者(`user.id`) |
| job_id | INT | 岗位 ID |
| template_id | INT | 使用的模板 ID |
| start_time | DATETIME | 开始时间 |
| submit_time | DATETIME | 提交时间 |
| total_score | FLOAT | 总分 |
| status | VARCHAR | `ONGOING` / `DONE` |
#### 表 `answer`
| 字段名 | 类型 | 说明 |
| ----------- | ----- | ------- |
| id (PK) | INT | 答案 ID |
| session_id | INT | 所属考试会话 |
| question_id | INT | 对应题目 |
| content | TEXT | 作答内容 |
| score | FLOAT | 得分 |
| time_taken | FLOAT | 作答耗时(秒) |
#### 表 `question_feedback`
| 字段名 | 类型 | 说明 |
| ------------ | -------- | -------- |
| id (PK) | INT | 反馈 ID |
| question_id | INT | 对应题目 |
| from_user_id | INT | 反馈人用户 ID |
| session_id | INT | 所在考试会话 |
| rating | INT | 评分(1–5) |
| comment | TEXT | 文本评价 |
| created_at | DATETIME | 创建时间 |
## 5 系统架构与部署设计
### 5.1 分层架构说明
1. **客户端层(Clients)**
* HR Web 前端(Vue/React SPA);
* 应聘者 Web/H5/APP;
* 通过 HTTPS 调用后端 REST API。
2. **接入层(Gateway)**
* Nginx 作为统一入口;
* 实现 HTTPS 终止、静态资源分发、反向代理和负载均衡。
3. **应用服务层(Services)**
* `Auth Service`:登录、鉴权、Token 管理;
* `Job & Company Service`:企业与岗位管理;
* `Question Service`:题库管理、统计指标计算;
* `Exam Service`:考试流程、会话管理;
* `Scoring Service`:客观题 & 主观题自动评分;
* `ML / Recommend Service`:试题生成、质量打分、模糊定制;
* `Admin Console`:运营后台接口。
4. **数据层(Data Layer)**
* 关系型数据库(MySQL/PostgreSQL);
* Redis 缓存热点题目、会话状态;
* 对象存储存放题目附件、解析文档;
* 日志分析存储(ELK/ClickHouse 等)。
### 5.2 部署/组件图
## 6 核心算法设计
### 6.1 自适应试题生成算法
#### 6.1.1 目标
在满足岗位需求与考试策略(题量、题型、难度分布)的前提下,为特定岗位与候选人生成一套“技能匹配度高、区分度好、不过时”的个性化试卷。
#### 6.1.2 关键输入
* `job`:岗位信息(`required_skills`、`level` 等)
* `policy`:试题策略(题量、难度比例、题型比例)
* `candidate_profile`:候选人画像(技能标签、经验)
* `question_bank`:题库(含 `QuestionStats` 统计信息)
#### 6.1.3 题目综合评分函数
对题目 `q` 计算综合得分 `score(q)`:
* `skill_match`:技能匹配度;
* `difficulty_fit`:难度适配度;
* `freshness`:新鲜度;
* `discrimination`:区分度;
* `bad_feedback`:负反馈惩罚。
线性组合示意:
```text
score(q) = w1 * skill_match
+ w2 * difficulty_fit
+ w3 * freshness
+ w4 * discrimination
- w5 * bad_feedback
```
#### 6.1.4 伪代码示例
```python
def generate_exam_paper(job, policy, candidate_profile, question_bank):
# 1. 抽取岗位 & 候选人技能标签
job_skills = extract_skill_tags(job.required_skills)
cand_skills = extract_skill_tags(candidate_profile.skill_tags)
# 2. 候选题集合:技能相关 & 在用
candidates = [
q for q in question_bank
if q.is_active and overlap(q.skill_tag, job_skills | cand_skills)
]
scored_questions = []
for q in candidates:
stats = q.stats # QuestionStats
skill_match = jaccard(q.skill_tag, job_skills | cand_skills)
difficulty_fit = 1.0 - abs(q.difficulty - target_difficulty(job)) / 4.0
freshness = time_decay(now() - stats.last_used_at)
discrimination = stats.discrimination
bad_feedback = max(0, 1 - stats.feedback_score)
score = (
0.30 * skill_match +
0.25 * difficulty_fit +
0.15 * freshness +
0.25 * discrimination -
0.05 * bad_feedback
)
scored_questions.append((q, score))
# 3. 按得分排序
scored_questions.sort(key=lambda x: x[1], reverse=True)
# 4. 按策略约束(题型 + 难度 + 数量)筛选
selected = []
counters = init_counters(policy)
for q, s in scored_questions:
if not satisfy_policy(q, counters, policy):
continue
selected.append(q)
update_counters(q, counters)
if len(selected) >= policy.total_questions:
break
# 5. 生成试卷模板对象
return build_exam_paper_template(job.id, policy.id, selected)
```
### 6.2 自动评分算法
拆分为客观题与主观题评分。
#### 6.2.1 客观题评分
* 单选/判断:完全匹配得满分;
* 多选:集合比对,可支持“部分正确部分得分”;
* 填空:字符串归一化后精确匹配。
```python
def score_objective_question(question, candidate_answer):
correct = question.standard_answer
if question.type in ("single_choice", "true_false"):
return question.full_score if candidate_answer == correct else 0.0
if question.type == "multi_choice":
cand_set = set(candidate_answer)
corr_set = set(correct)
if cand_set == corr_set:
return question.full_score
if cand_set.issubset(corr_set):
return question.full_score * len(cand_set) / len(corr_set)
# 选错选项则记 0 分
return 0.0
if question.type == "blank":
return question.full_score if normalize(candidate_answer) == normalize(correct) else 0.0
return 0.0
```
#### 6.2.2 主观题评分(规则 + 关键词覆盖率)
简化实现:基于关键词覆盖率进行线性赋分。
```python
def score_subjective_question(question, candidate_answer):
tokens = tokenize(candidate_answer) # 分词+规范化
key_points = question.key_points # 关键点列表
hit = sum(1 for k in key_points if k in tokens)
coverage = hit / len(key_points) if key_points else 0.0
return question.full_score * coverage
```
#### 6.2.3 会话总分计算
```python
def score_exam_session(session):
total = 0.0
for answer in session.answers:
q = load_question(answer.question_id)
if q.type in ("single_choice", "multi_choice", "true_false", "blank"):
s = score_objective_question(q, answer.content)
else:
s = score_subjective_question(q, answer.content)
answer.score = s
total += s
session.total_score = total
persist_scores(session)
return total
```
### 6.3 试题质量评估与淘汰算法
#### 6.3.1 目标
根据题目使用情况、得分表现与主观反馈,动态计算题目质量分 `quality_score`。当样本量充足且质量分低于阈值时,将题目标记为淘汰候选。
#### 6.3.2 伪代码示例
```python
def update_question_statistics(question_id):
# 1. 从数据库汇总最新统计信息
stats = aggregate_stats_from_answers_and_feedback(question_id)
# 包含:used_count, avg_score, correct_rate,
# discrimination, feedback_score, last_used_at
# 2. 计算质量分
quality = (
0.25 * stats.correct_rate +
0.35 * stats.discrimination +
0.25 * stats.feedback_score +
0.15 * freshness_factor(stats.last_used_at)
)
# 3. 更新统计表
save_question_stats(question_id, stats, quality)
# 4. 判定是否进入淘汰池
MIN_USED = 30
THRESHOLD = 0.4
if stats.used_count >= MIN_USED and quality < THRESHOLD:
mark_question_as_candidate_for_deprecation(question_id)
```
### 6.4 模糊定制题目推荐算法
#### 6.4.1 场景
HR 只输入自然语言岗位描述(如“做高并发订单系统的 Java 后端工程师”),系统自动推荐若干候选题目供其选择。
#### 6.4.2 思路
1. 使用文本向量化(TF-IDF 或预训练 Embedding)对岗位描述和题目文本编码;
2. 计算岗位向量与题目向量间的余弦相似度;
3. 返回相似度最高的 Top-K 题目。
#### 6.4.3 伪代码
```python
def fuzzy_recommend_questions(job_free_text, question_bank, top_k=50):
job_vec = encode_text(job_free_text) # 向量化岗位描述
scored = []
for q in question_bank:
q_text = q.content + " " + q.skill_tag
q_vec = encode_text(q_text)
sim = cosine_similarity(job_vec, q_vec)
scored.append((q, sim))
scored.sort(key=lambda x: x[1], reverse=True)
return [q for q, _ in scored[:top_k]]
```
## 7 总结
本报告从业务角色与流程出发,给出了招聘试题定制平台的完整系统设计方案:
* **业务层面**:明确 HR、应聘者、平台管理员等角色及其用例;
* **模型层面**:通过 UML 用例图、活动图、时序图、类图与 ER 图,刻画系统的结构与行为;
* **数据层面**:设计了围绕“岗位–试卷–试题–作答–反馈”的关系型数据库模型;
* **架构层面**:采用客户端–接入层–服务层–数据层的分层部署方案,便于扩展与运维;
* **算法层面**:给出自适应试题生成、自动评分、试题质量评估与模糊定制等核心算法的伪代码。