6.5 KiB
6.5 KiB
评分标准JSON结构化重构方案
问题分析
当前设计缺陷:
- UI层:使用结构化对象
[{min_score, max_score, description}] - 数据库层:使用
\n分隔的字符串存储在instructions字段 - 数据丢失:
min_score和max_score等结构化信息在保存时完全丢失
解决方案
方案一:使用现有 scoring_criteria 字段(推荐)
1. 数据库结构调整
-- 第一步:将 scoring_criteria 字段改为 JSONB 类型(如果不是的话)
ALTER TABLE public.ak_training_projects
ALTER COLUMN scoring_criteria TYPE JSONB USING scoring_criteria::jsonb;
2. 新的JSON数据结构
{
"criteria": [
{
"min_score": 90,
"max_score": 100,
"description": "优秀:动作标准,节奏控制完美,表现卓越"
},
{
"min_score": 80,
"max_score": 89,
"description": "良好:动作较为标准,节奏控制良好"
},
{
"min_score": 70,
"max_score": 79,
"description": "及格:基本掌握动作要领,需要改进"
},
{
"min_score": 0,
"max_score": 69,
"description": "不及格:动作不标准,需要重新练习"
}
],
"scoring_method": "comprehensive", // 综合评分方式
"weight_distribution": {
"technique": 0.4, // 技术动作权重 40%
"effort": 0.3, // 努力程度权重 30%
"improvement": 0.3 // 进步幅度权重 30%
}
}
3. 前端代码重构
加载数据时:
// 修改 loadProjectData 函数
function loadProjectData() {
// ...现有代码...
// 处理评分标准 - 使用新的JSON结构
let scoringCriteria: Array<UTSJSONObject> = []
const criteriaData = safeGet(projectData, 'scoring_criteria', null)
if (criteriaData && typeof criteriaData === 'object') {
// 新的JSON格式
const criteria = safeGet(criteriaData, 'criteria', [])
if (criteria instanceof Array) {
scoringCriteria = criteria.map((item: any) => ({
min_score: item.min_score?.toString() || '',
max_score: item.max_score?.toString() || '',
description: item.description || ''
} as UTSJSONObject))
}
} else if (typeof criteriaData === 'string') {
// 兼容旧的 \n 分隔格式
const instructionSteps = criteriaData.split('\n').filter((step: string) => step.trim().length > 0)
scoringCriteria = instructionSteps.map((step: string, index: number) => ({
min_score: (index * 20).toString(),
max_score: ((index + 1) * 20).toString(),
description: step.trim()
} as UTSJSONObject))
}
if (scoringCriteria.length === 0) {
scoringCriteria = [{ min_score: '', max_score: '', description: '' } as UTSJSONObject]
}
// ...其他代码...
}
保存数据时:
// 修改 updateProject 和 saveDraft 函数
function updateProject() {
// ...现有代码...
const scoringCriteria = getScoringCriteria()
const scoringCriteriaJson = {
criteria: scoringCriteria.map((criteria: UTSJSONObject) => ({
min_score: parseInt(safeGet(criteria, 'min_score', '0')),
max_score: parseInt(safeGet(criteria, 'max_score', '100')),
description: safeGet(criteria, 'description', '')
})).filter(item => item.description.trim().length > 0),
scoring_method: "comprehensive",
weight_distribution: {
technique: 0.4,
effort: 0.3,
improvement: 0.3
}
}
const { data, error } = await supaClient
.from('ak_training_projects')
.update({
// ...其他字段...
scoring_criteria: scoringCriteriaJson,
instructions: '', // 清空旧的instructions字段,避免混淆
updated_at: new Date().toISOString()
})
.eq('id', projectId.value)
.execute()
// ...其他代码...
}
方案二:添加新字段(备选方案)
如果不想修改现有字段,可以添加新字段:
-- 添加新的JSONB字段
ALTER TABLE public.ak_training_projects
ADD COLUMN scoring_criteria_json JSONB;
-- 创建索引以提高查询性能
CREATE INDEX idx_ak_training_projects_scoring_criteria
ON public.ak_training_projects USING GIN (scoring_criteria_json);
数据迁移脚本
第一步:备份现有数据
-- 创建备份表
CREATE TABLE ak_training_projects_backup AS
SELECT * FROM ak_training_projects;
第二步:迁移现有数据
-- 将现有的 instructions 数据转换为 JSON 格式
UPDATE ak_training_projects
SET scoring_criteria = jsonb_build_object(
'criteria',
ARRAY(
SELECT jsonb_build_object(
'min_score', (ROW_NUMBER() OVER () - 1) * 20,
'max_score', ROW_NUMBER() OVER () * 20,
'description', trim(instruction)
)
FROM unnest(string_to_array(instructions, E'\n')) AS instruction
WHERE trim(instruction) != ''
),
'scoring_method', 'comprehensive',
'weight_distribution', jsonb_build_object(
'technique', 0.4,
'effort', 0.3,
'improvement', 0.3
)
)
WHERE instructions IS NOT NULL AND instructions != '';
第三步:验证迁移结果
-- 验证JSON结构
SELECT
id,
title,
scoring_criteria->'criteria' as criteria_array,
jsonb_array_length(scoring_criteria->'criteria') as criteria_count
FROM ak_training_projects
WHERE scoring_criteria IS NOT NULL
LIMIT 5;
优势对比
| 特性 | 旧方案 (\n分隔) | 新方案 (JSON) |
|---|---|---|
| 数据结构 | 平铺字符串 | 结构化对象 |
| 分数范围 | ❌ 丢失 | ✅ 保留 |
| 查询能力 | ❌ 有限 | ✅ 强大 |
| 扩展性 | ❌ 困难 | ✅ 容易 |
| 数据完整性 | ❌ 差 | ✅ 优秀 |
| AI分析支持 | ❌ 不支持 | ✅ 原生支持 |
实施建议
- 测试环境先行:在测试环境完成迁移和测试
- 渐进式部署:保持向后兼容,支持新旧两种格式
- 数据校验:迁移后进行全面的数据完整性检查
- 性能优化:为JSONB字段创建合适的索引
长期收益
- 更好的数据查询:可以直接查询特定分数范围的标准
- AI集成友好:结构化数据更容易被AI系统理解和处理
- 扩展性强:未来可以轻松添加新的评分维度
- 数据一致性:避免了UI和数据库层的数据结构不匹配问题