|
@@ -2576,58 +2576,102 @@ log.info("已添加template_id列到tasks表");
|
|
|
|
|
|
|
|
private void migrateHealthDimensionsTable() {
|
|
private void migrateHealthDimensionsTable() {
|
|
|
try {
|
|
try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_dimension_score` (\n" +
|
|
|
|
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_dimension_score` TO `health_dimension_score_old`");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("health_dimension_score rename skipped: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_dimension_scores` (\n" +
|
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
" `member_id` bigint NOT NULL COMMENT '家庭成员ID',\n" +
|
|
" `member_id` bigint NOT NULL COMMENT '家庭成员ID',\n" +
|
|
|
- " `dimension` varchar(50) NOT NULL COMMENT '维度: body/fitness/cardio/blood/sleep/psyche/diet',\n" +
|
|
|
|
|
- " `score` decimal(5,2) NOT NULL COMMENT '得分 0-100',\n" +
|
|
|
|
|
- " `data_source` varchar(50) NOT NULL COMMENT '数据来源: manual/assessment/report/device',\n" +
|
|
|
|
|
- " `record_date` date DEFAULT NULL COMMENT '记录日期',\n" +
|
|
|
|
|
- " `source_id` bigint DEFAULT NULL COMMENT '来源记录ID(体检报告等)',\n" +
|
|
|
|
|
- " `is_latest` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否最新',\n" +
|
|
|
|
|
|
|
+ " `dimension` varchar(20) NOT NULL COMMENT '维度: growth/sleep/vision/immunity/nutrition/gut/exercise',\n" +
|
|
|
|
|
+ " `score` int NOT NULL COMMENT '0-100分',\n" +
|
|
|
|
|
+ " `percentile` int DEFAULT NULL COMMENT '人群百分位 0-100',\n" +
|
|
|
|
|
+ " `data_source` varchar(20) NOT NULL COMMENT '数据源: REPORT/PHOTO/VOICE/MANUAL',\n" +
|
|
|
|
|
+ " `tier` tinyint NOT NULL DEFAULT 3 COMMENT '数据层级: 1自动/2语音/3手动',\n" +
|
|
|
|
|
+ " `raw_data` json DEFAULT NULL COMMENT '原始数据快照',\n" +
|
|
|
|
|
+ " `assess_date` date NOT NULL COMMENT '评估日期',\n" +
|
|
|
|
|
+ " `expire_date` date DEFAULT NULL COMMENT '数据过期日',\n" +
|
|
|
" `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
" `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
|
|
+ " `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n" +
|
|
|
" PRIMARY KEY (`id`),\n" +
|
|
" PRIMARY KEY (`id`),\n" +
|
|
|
- " KEY `idx_member_dimension` (`member_id`,`dimension`,`is_latest`),\n" +
|
|
|
|
|
- " KEY `idx_record_date` (`record_date`)\n" +
|
|
|
|
|
|
|
+ " UNIQUE KEY `uk_member_dim_date` (`member_id`, `dimension`, `assess_date`)\n" +
|
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康维度评分表'");
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康维度评分表'");
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.warn("health_dimension_score 表创建失败: {}", e.getMessage());
|
|
|
|
|
|
|
+ log.warn("health_dimension_scores 表创建失败: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_data_source_record` (\n" +
|
|
|
|
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_data_source_record` TO `health_data_source_record_old`");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("health_data_source_record rename skipped: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_data_sources` (\n" +
|
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
- " `member_id` bigint NOT NULL COMMENT '家庭成员ID',\n" +
|
|
|
|
|
- " `data_source` varchar(50) NOT NULL COMMENT '数据源: manual/assessment/report/device',\n" +
|
|
|
|
|
- " `source_id` bigint NOT NULL COMMENT '来源记录ID',\n" +
|
|
|
|
|
- " `dimension` varchar(50) DEFAULT NULL COMMENT '关联维度',\n" +
|
|
|
|
|
- " `record_time` datetime NOT NULL COMMENT '记录时间',\n" +
|
|
|
|
|
- " `is_latest` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否最新',\n" +
|
|
|
|
|
|
|
+ " `member_id` bigint NOT NULL,\n" +
|
|
|
|
|
+ " `source_type` varchar(20) NOT NULL COMMENT 'PDF/PHOTO/VOICE/MANUAL/WEIXIN_STEP',\n" +
|
|
|
|
|
+ " `dimension_codes` json NOT NULL COMMENT '影响的维度列表',\n" +
|
|
|
|
|
+ " `parsed_result` json DEFAULT NULL COMMENT '解析结果全量',\n" +
|
|
|
|
|
+ " `file_url` varchar(500) DEFAULT NULL COMMENT '原始文件URL',\n" +
|
|
|
|
|
+ " `confidence` decimal(3,2) DEFAULT NULL COMMENT 'AI解析置信度',\n" +
|
|
|
" `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
" `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
" PRIMARY KEY (`id`),\n" +
|
|
" PRIMARY KEY (`id`),\n" +
|
|
|
- " KEY `idx_member_source` (`member_id`,`data_source`,`is_latest`)\n" +
|
|
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康数据来源记录表'");
|
|
|
|
|
|
|
+ " KEY `idx_member_source` (`member_id`, `source_type`)\n" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康数据源记录表'");
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.warn("health_data_source_record 表创建失败: {}", e.getMessage());
|
|
|
|
|
|
|
+ log.warn("health_data_sources 表创建失败: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_norm_reference` TO `health_norm_reference_old`");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("health_norm_reference rename skipped: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
try {
|
|
try {
|
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_norm_reference` (\n" +
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_norm_reference` (\n" +
|
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
" `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
- " `dimension` varchar(50) NOT NULL COMMENT '维度',\n" +
|
|
|
|
|
- " `indicator` varchar(100) NOT NULL COMMENT '指标名',\n" +
|
|
|
|
|
- " `min_value` decimal(10,3) DEFAULT NULL COMMENT '正常最小值',\n" +
|
|
|
|
|
- " `max_value` decimal(10,3) DEFAULT NULL COMMENT '正常最大值',\n" +
|
|
|
|
|
- " `unit` varchar(20) DEFAULT NULL COMMENT '单位',\n" +
|
|
|
|
|
- " `age_group` varchar(20) DEFAULT NULL COMMENT '年龄段: child/adolescent/adult',\n" +
|
|
|
|
|
- " `gender` varchar(10) DEFAULT NULL COMMENT '性别: male/female/unisex',\n" +
|
|
|
|
|
- " `source` varchar(100) DEFAULT NULL COMMENT '参考标准来源',\n" +
|
|
|
|
|
- " `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n" +
|
|
|
|
|
|
|
+ " `dimension` varchar(20) NOT NULL COMMENT '维度',\n" +
|
|
|
|
|
+ " `gender` varchar(10) NOT NULL COMMENT 'male/female/all',\n" +
|
|
|
|
|
+ " `age_min` int NOT NULL COMMENT '年龄下限(月)',\n" +
|
|
|
|
|
+ " `age_max` int NOT NULL COMMENT '年龄上限(月)',\n" +
|
|
|
|
|
+ " `percentile_5` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `percentile_15` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `percentile_25` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `percentile_50` int DEFAULT NULL COMMENT '中位值',\n" +
|
|
|
|
|
+ " `percentile_75` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `percentile_85` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `percentile_95` int DEFAULT NULL,\n" +
|
|
|
|
|
+ " `source` varchar(50) DEFAULT NULL COMMENT '常模来源',\n" +
|
|
|
|
|
+ " `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
" PRIMARY KEY (`id`),\n" +
|
|
" PRIMARY KEY (`id`),\n" +
|
|
|
- " KEY `idx_dimension_indicator` (`dimension`,`indicator`)\n" +
|
|
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康指标参考标准表'");
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.warn("health_norm_reference 表创建失败: {}", e.getMessage());
|
|
|
|
|
|
|
+ " UNIQUE KEY `uk_dim_gender_age` (`dimension`, `gender`, `age_min`, `age_max`)\n" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康常模对照表'");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("health_norm_reference 表重建失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("INSERT IGNORE INTO health_dimension_scores (member_id, dimension, score, data_source, tier, assess_date, created_at)\n" +
|
|
|
|
|
+ " SELECT member_id,\n" +
|
|
|
|
|
+ " CASE dimension\n" +
|
|
|
|
|
+ " WHEN 'body' THEN 'growth'\n" +
|
|
|
|
|
+ " WHEN 'fitness' THEN 'exercise'\n" +
|
|
|
|
|
+ " WHEN 'cardio' THEN 'exercise'\n" +
|
|
|
|
|
+ " WHEN 'blood' THEN 'immunity'\n" +
|
|
|
|
|
+ " WHEN 'sleep' THEN 'sleep'\n" +
|
|
|
|
|
+ " WHEN 'psyche' THEN 'growth'\n" +
|
|
|
|
|
+ " WHEN 'diet' THEN 'nutrition'\n" +
|
|
|
|
|
+ " ELSE dimension\n" +
|
|
|
|
|
+ " END,\n" +
|
|
|
|
|
+ " score,\n" +
|
|
|
|
|
+ " UPPER(data_source),\n" +
|
|
|
|
|
+ " 3,\n" +
|
|
|
|
|
+ " record_date,\n" +
|
|
|
|
|
+ " NOW()\n" +
|
|
|
|
|
+ " FROM health_dimension_score_old WHERE is_latest = 1");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("旧维度评分数据迁移跳过: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2635,23 +2679,31 @@ log.info("已添加template_id列到tasks表");
|
|
|
try {
|
|
try {
|
|
|
int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM health_norm_reference", Integer.class);
|
|
int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM health_norm_reference", Integer.class);
|
|
|
if (count == 0) {
|
|
if (count == 0) {
|
|
|
- jdbcTemplate.execute("INSERT INTO `health_norm_reference` (`dimension`, `indicator`, `min_value`, `max_value`, `unit`, `age_group`, `gender`, `source`) VALUES\n" +
|
|
|
|
|
- "('body', 'bmi', 18.5, 24.0, 'kg/m²', 'child', 'unisex', '中国儿童青少年BMI标准'),\n" +
|
|
|
|
|
- "('body', 'bmi', 18.5, 24.0, 'kg/m²', 'adolescent', 'unisex', '中国儿童青少年BMI标准'),\n" +
|
|
|
|
|
- "('body', 'bmi', 18.5, 24.0, 'kg/m²', 'adult', 'unisex', '中国成人BMI标准'),\n" +
|
|
|
|
|
- "('sleep', 'duration', 8.0, 12.0, '小时', 'child', 'unisex', '中国儿童睡眠指南'),\n" +
|
|
|
|
|
- "('sleep', 'duration', 9.0, 11.0, '小时', 'adolescent', 'unisex', '中国儿童青少年睡眠指南'),\n" +
|
|
|
|
|
- "('sleep', 'duration', 7.0, 9.0, '小时', 'adult', 'unisex', '中国成人睡眠指南'),\n" +
|
|
|
|
|
- "('fitness', 'situp', 30.0, NULL, '次', 'child', 'male', '国家学生体质健康标准'),\n" +
|
|
|
|
|
- "('fitness', 'situp', 25.0, NULL, '次', 'child', 'female', '国家学生体质健康标准'),\n" +
|
|
|
|
|
- "('fitness', 'situp', 40.0, NULL, '次', 'adolescent', 'male', '国家学生体质健康标准'),\n" +
|
|
|
|
|
- "('fitness', 'situp', 35.0, NULL, '次', 'adolescent', 'female', '国家学生体质健康标准')");
|
|
|
|
|
- log.info("health_norm_reference 种子数据已加载 (10条)");
|
|
|
|
|
|
|
+ jdbcTemplate.execute("INSERT INTO health_norm_reference (dimension, gender, age_min, age_max, percentile_5, percentile_15, percentile_25, percentile_50, percentile_75, percentile_85, percentile_95, source) VALUES\n" +
|
|
|
|
|
+ "('growth', 'male', 72, 84, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('growth', 'female', 72, 84, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('growth', 'male', 84, 96, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('growth', 'female', 84, 96, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('growth', 'male', 96, 120, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('growth', 'female', 96, 120, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
|
|
+ "('sleep', 'all', 0, 12, 40, 50, 60, 70, 80, 88, 95, 'NSF婴幼儿睡眠→百分制'),\n" +
|
|
|
|
|
+ "('sleep', 'all', 12, 36, 40, 50, 60, 70, 80, 88, 95, 'NSF幼儿睡眠→百分制'),\n" +
|
|
|
|
|
+ "('sleep', 'all', 36, 72, 40, 50, 60, 70, 80, 88, 95, 'NSF学龄前睡眠→百分制'),\n" +
|
|
|
|
|
+ "('sleep', 'all', 72, 156, 40, 50, 60, 70, 80, 88, 95, 'NSF学龄儿童睡眠→百分制'),\n" +
|
|
|
|
|
+ "('vision', 'male', 72, 144, 45, 55, 65, 75, 85, 90, 95, '视力发育标准→百分制'),\n" +
|
|
|
|
|
+ "('vision', 'female', 72, 144, 45, 55, 65, 75, 85, 90, 95, '视力发育标准→百分制'),\n" +
|
|
|
|
|
+ "('immunity', 'all', 0, 72, 15, 30, 45, 65, 80, 90, 95, '年患病次数→百分制'),\n" +
|
|
|
|
|
+ "('immunity', 'all', 72, 192, 15, 35, 50, 70, 85, 92, 98, '年患病次数→百分制'),\n" +
|
|
|
|
|
+ "('nutrition', 'all', 36, 192, 20, 30, 40, 50, 60, 70, 80, '膳食多样性→百分制'),\n" +
|
|
|
|
|
+ "('gut', 'all', 0, 192, 40, 50, 60, 70, 80, 85, 95, '菌群人群分布(平台自建)'),\n" +
|
|
|
|
|
+ "('exercise', 'male', 72, 144, 30, 40, 50, 60, 70, 80, 90, '国家学生体质健康标准'),\n" +
|
|
|
|
|
+ "('exercise', 'female', 72, 144, 25, 35, 45, 55, 65, 75, 85, '国家学生体质健康标准')");
|
|
|
|
|
+ log.info("health_norm_reference 常模种子数据已加载 (18条, 统一0-100百分制)");
|
|
|
} else {
|
|
} else {
|
|
|
- log.info("health_norm_reference 种子数据已存在 ({}条), 跳过", count);
|
|
|
|
|
|
|
+ log.info("health_norm_reference 常模数据已存在 ({}条), 跳过", count);
|
|
|
}
|
|
}
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.warn("health_norm_reference 种子数据初始化失败: {}", e.getMessage());
|
|
|
|
|
|
|
+ log.warn("health_norm_reference 常模种子数据初始化失败: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|