1. 项目背景与核心需求
这个创意方案评选平台的设计初衷源于当前各类创新竞赛、企业内部提案和学术活动中对高效评选工具的需求。传统评选流程往往依赖纸质表格或简单的在线表单,评委之间缺乏实时互动,统计结果耗时费力。我们团队在实际参与多次创新大赛评审后,深刻体会到需要一个能够整合方案展示、多维度评分和实时结果分析的平台。
平台的核心功能定位为:
- 支持参赛者上传多媒体方案(图文、视频、附件)
- 实现多角色权限管理(管理员、评委、参赛者)
- 提供自定义评分模板功能
- 实时生成可视化统计结果
- 支持评审过程留痕与反馈
2. 技术架构设计
2.1 前后端分离架构
采用Vue3作为前端框架,配合TypeScript强化类型检查。后端选择Python的FastAPI框架,主要考虑因素包括:
- FastAPI的异步特性适合处理评选高峰期的并发请求
- 自动生成的交互式API文档便于前后端协作
- 与Python生态系统的深度集成(如数据分析库)
数据库选用PostgreSQL,因其:
- JSONB类型完美支持动态评分模板的存储
- 成熟的事务处理机制保障评分数据一致性
- 强大的全文检索功能方便方案检索
2.2 关键技术选型解析
前端核心组件库采用Element Plus,其表格和表单组件经过二次开发后:
- 实现了动态评分表渲染
- 支持单元格级别的数据验证
- 内置了无障碍访问特性
特别开发的Vue自定义指令:
javascript复制// 评分项自动聚焦指令
Vue.directive('auto-focus', {
inserted(el, binding) {
if (binding.value) el.querySelector('input').focus()
}
})
后端采用分层架构:
- 路由层:处理HTTP请求和响应格式化
- 服务层:业务逻辑和事务管理
- 仓储层:数据库操作抽象
- 模型层:数据验证和关系映射
3. 核心功能实现细节
3.1 动态评分模板系统
采用JSON Schema定义评分模板结构:
python复制class CriteriaTemplate(BaseModel):
name: str
weight: confloat(gt=0, le=1) # 权重0-1
description: Optional[str]
options: List[CriteriaOption]
class ScoringSheet(BaseModel):
template_id: UUID
items: List[ScoringItem]
前端通过递归组件渲染动态表单:
vue复制<template>
<div v-for="(item, index) in criteria" :key="index">
<h3>{{ item.name }}</h3>
<component
:is="getComponent(item.type)"
v-model="scores[index]"
:config="item"
/>
</div>
</template>
3.2 实时统计看板
利用WebSocket实现实时数据推送:
python复制@app.websocket("/ws/dashboard/{contest_id}")
async def dashboard_feed(
websocket: WebSocket,
contest_id: UUID
):
await websocket.accept()
while True:
data = get_contest_stats(contest_id)
await websocket.send_json(data)
await asyncio.sleep(5) # 5秒刷新间隔
前端使用ECharts实现动态可视化:
javascript复制const updateChart = (data) => {
chart.setOption({
radar: {
indicator: data.criteria.map(c => ({
name: c.name,
max: c.maxScore
}))
},
series: [{
data: data.series
}]
})
}
4. 性能优化实践
4.1 数据库查询优化
针对评分统计场景:
python复制# 原始查询(N+1问题)
scores = await Score.filter(contest_id=contest_id).all()
results = [await score.get_criteria() for score in scores]
# 优化后
scores = await Score.filter(contest_id=contest_id).prefetch_related('criteria')
建立复合索引:
sql复制CREATE INDEX idx_score_contest_criteria ON score(contest_id, criteria_id);
4.2 前端性能提升
实施组件级懒加载:
javascript复制const ScoreForm = defineAsyncComponent(() =>
import('./components/ScoreForm.vue')
)
使用虚拟滚动处理大型列表:
vue复制<RecycleScroller
:items="submissions"
:item-size="120"
key-field="id"
>
<template #default="{ item }">
<SubmissionCard :data="item" />
</template>
</RecycleScroller>
5. 安全防护措施
5.1 评分防篡改机制
采用JWT签名+Redis缓存的组合方案:
python复制def generate_score_token(score: Score):
payload = {
"score_id": str(score.id),
"exp": datetime.utcnow() + timedelta(hours=2)
}
token = jwt.encode(payload, SECRET_KEY)
redis.setex(f"score_token:{token}", 7200, str(score.id))
return token
5.2 文件上传防护
严格限制上传文件:
python复制ALLOWED_EXTENSIONS = {'pdf', 'docx', 'pptx', 'jpg', 'png', 'mp4'}
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
ext = file.filename.split('.')[-1].lower()
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(400, "Unsupported file type")
# 扫描文件内容
if await virus_scan(file.file):
raise HTTPException(400, "File contains malware")
6. 部署实践
6.1 Docker容器化部署
后端Dockerfile关键配置:
dockerfile复制FROM python:3.9-slim
RUN apt-get update && apt-get install -y \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
前端部署优化:
nginx复制server {
listen 80;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
gzip_static on;
}
location /api {
proxy_pass http://backend:8000;
}
}
6.2 监控方案
使用Prometheus+Grafana监控关键指标:
python复制@app.on_event("startup")
async def setup_metrics():
metrics = PrometheusMetrics(app)
metrics.info('app_info', 'Application info', version='1.0.0')
@metrics.summary(
'score_submission_duration',
'Time spent processing score submissions'
)
async def score_submission():
pass
7. 典型问题排查
7.1 评分并发冲突
解决方案:采用乐观锁机制
python复制async def update_score(score_id: UUID, new_value: float):
score = await Score.get(id=score_id).select_for_update()
if score.lock_version != client_version:
raise HTTPException(409, "Score has been modified")
score.value = new_value
score.lock_version += 1
await score.save()
7.2 大文件上传中断
前端实现断点续传:
javascript复制const uploadFile = async (file) => {
const chunkSize = 5 * 1024 * 1024; // 5MB
let uploaded = 0;
while (uploaded < file.size) {
const chunk = file.slice(uploaded, uploaded + chunkSize);
await api.uploadChunk(chunk, file.name, uploaded);
uploaded += chunk.size;
}
}
8. 扩展功能设计
8.1 智能匹配评审
基于NLP的评委自动分配:
python复制def match_reviewers(submission, judges):
submission_vec = get_text_embedding(submission.description)
judge_vectors = [get_judge_expertise(j) for j in judges]
similarities = [
cosine_similarity(submission_vec, jv)
for jv in judge_vectors
]
return sorted(zip(judges, similarities), key=lambda x: -x[1])
8.2 方案相似度检测
使用SimHash算法:
python复制def compute_simhash(text):
tokens = jieba.cut(text)
vec = [0] * 64
for token in tokens:
h = hash(token)
for i in range(64):
bit = (h >> i) & 1
vec[i] += 1 if bit else -1
return sum(1 << i for i in range(64) if vec[i] > 0)
在实际部署中,我们特别注意到评委操作习惯的差异。有些评委习惯使用平板设备,为此我们增加了触摸屏优化:
- 放大评分区域的点击热区
- 实现滑动评分控件
- 添加触觉反馈(针对支持设备)
平台上线后处理了超过200场评选活动,峰值时单日处理评分数据超过1.5万条。通过Redis缓存评分统计结果,即使在高峰期,看板数据加载时间也能控制在800ms以内。
