Initial commit of akmon project
This commit is contained in:
215
doc_zhipao/SCORING_CRITERIA_JSON_MIGRATION.md
Normal file
215
doc_zhipao/SCORING_CRITERIA_JSON_MIGRATION.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# 评分标准JSON结构化重构方案
|
||||
|
||||
## 问题分析
|
||||
|
||||
**当前设计缺陷:**
|
||||
1. **UI层**:使用结构化对象 `[{min_score, max_score, description}]`
|
||||
2. **数据库层**:使用 `\n` 分隔的字符串存储在 `instructions` 字段
|
||||
3. **数据丢失**:`min_score` 和 `max_score` 等结构化信息在保存时完全丢失
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案一:使用现有 `scoring_criteria` 字段(推荐)
|
||||
|
||||
#### 1. 数据库结构调整
|
||||
```sql
|
||||
-- 第一步:将 scoring_criteria 字段改为 JSONB 类型(如果不是的话)
|
||||
ALTER TABLE public.ak_training_projects
|
||||
ALTER COLUMN scoring_criteria TYPE JSONB USING scoring_criteria::jsonb;
|
||||
```
|
||||
|
||||
#### 2. 新的JSON数据结构
|
||||
```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. 前端代码重构
|
||||
|
||||
**加载数据时:**
|
||||
```typescript
|
||||
// 修改 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]
|
||||
}
|
||||
|
||||
// ...其他代码...
|
||||
}
|
||||
```
|
||||
|
||||
**保存数据时:**
|
||||
```typescript
|
||||
// 修改 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()
|
||||
|
||||
// ...其他代码...
|
||||
}
|
||||
```
|
||||
|
||||
### 方案二:添加新字段(备选方案)
|
||||
|
||||
如果不想修改现有字段,可以添加新字段:
|
||||
|
||||
```sql
|
||||
-- 添加新的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);
|
||||
```
|
||||
|
||||
## 数据迁移脚本
|
||||
|
||||
### 第一步:备份现有数据
|
||||
```sql
|
||||
-- 创建备份表
|
||||
CREATE TABLE ak_training_projects_backup AS
|
||||
SELECT * FROM ak_training_projects;
|
||||
```
|
||||
|
||||
### 第二步:迁移现有数据
|
||||
```sql
|
||||
-- 将现有的 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 != '';
|
||||
```
|
||||
|
||||
### 第三步:验证迁移结果
|
||||
```sql
|
||||
-- 验证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分析支持 | ❌ 不支持 | ✅ 原生支持 |
|
||||
|
||||
## 实施建议
|
||||
|
||||
1. **测试环境先行**:在测试环境完成迁移和测试
|
||||
2. **渐进式部署**:保持向后兼容,支持新旧两种格式
|
||||
3. **数据校验**:迁移后进行全面的数据完整性检查
|
||||
4. **性能优化**:为JSONB字段创建合适的索引
|
||||
|
||||
## 长期收益
|
||||
|
||||
1. **更好的数据查询**:可以直接查询特定分数范围的标准
|
||||
2. **AI集成友好**:结构化数据更容易被AI系统理解和处理
|
||||
3. **扩展性强**:未来可以轻松添加新的评分维度
|
||||
4. **数据一致性**:避免了UI和数据库层的数据结构不匹配问题
|
||||
Reference in New Issue
Block a user