|
|
@@ -2963,6 +2963,8 @@ log.info("已添加template_id列到tasks表");
|
|
|
seedHealthNorms();
|
|
|
runLateMigrations();
|
|
|
migration102();
|
|
|
+ // 迁移226-247: 原seedFullRegionData内的DDL迁移,确保不因省市区数据已存在而跳过
|
|
|
+ runFullRegionDataMigrations();
|
|
|
seedFullRegionData();
|
|
|
|
|
|
}
|
|
|
@@ -3918,14 +3920,6 @@ try {
|
|
|
|
|
|
ensureColumn("health_reports", "subject_id", "BIGINT COMMENT '报告主体用户ID(关联users表,为null表示未绑定)'");
|
|
|
|
|
|
- // ---------- 健康指标表 新增symptoms字段 ----------
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_indicators ADD COLUMN symptoms TEXT COMMENT '过量/缺乏相关症状描述'");
|
|
|
- log.info("已为health_indicators表添加symptoms字段");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("为health_indicators表添加symptoms字段可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
log.info("成长规划师模块、财富维度及健康检测报告数据库表初始化完成");
|
|
|
|
|
|
// 健康打卡表
|
|
|
@@ -3949,31 +3943,6 @@ try {
|
|
|
}
|
|
|
|
|
|
// 迁移31: 创建联系人表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS contacts (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "name VARCHAR(100) NOT NULL COMMENT '姓名', " +
|
|
|
- "phone VARCHAR(20) COMMENT '电话', " +
|
|
|
- "avatar VARCHAR(500) COMMENT '头像', " +
|
|
|
- "relationship_type VARCHAR(20) COMMENT '关系类型: family/friend/partner/colleague/other', " +
|
|
|
- "known_since DATETIME COMMENT '认识时间', " +
|
|
|
- "bio VARCHAR(500) COMMENT '简介', " +
|
|
|
- "birthday DATE COMMENT '生日', " +
|
|
|
- "intimacy_level INT DEFAULT 0 COMMENT '亲密度(0-100)', " +
|
|
|
- "contact_source VARCHAR(20) DEFAULT 'manual' COMMENT '来源: phone/manual', " +
|
|
|
- "tags VARCHAR(500) COMMENT '标签(逗号分隔)', " +
|
|
|
- "last_contact_at DATETIME COMMENT '最近联系时间', " +
|
|
|
- "contact_count INT DEFAULT 0 COMMENT '联系次数', " +
|
|
|
- "notes TEXT COMMENT '备注', " +
|
|
|
- "created_at DATETIME, " +
|
|
|
- "updated_at DATETIME, " +
|
|
|
- "INDEX idx_user_id (user_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='联系人表'");
|
|
|
- log.info("已创建contacts联系表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建contacts表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
|
|
|
// 创建文章阅读记录表
|
|
|
try {
|
|
|
@@ -3993,110 +3962,7 @@ try {
|
|
|
log.warn("创建article_reading_records表可能已存在: {}", e.getMessage());
|
|
|
}
|
|
|
|
|
|
- // 迁移30: activities表添加visibility和visible_to字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visibility VARCHAR(20) DEFAULT 'all' COMMENT '可见性: all/member'");
|
|
|
- log.info("已添加visibility列到activities表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visible_to VARCHAR(255) COMMENT '可见角色列表(逗号分隔)'");
|
|
|
- log.info("已添加visible_to列到activities表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 创建维度配置表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS product_dimension_config (" +
|
|
|
- "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
- "dimension_code VARCHAR(50) NOT NULL UNIQUE COMMENT '维度代码: body/mind/wisdom/action/wealth', " +
|
|
|
- "dimension_name VARCHAR(100) NOT NULL COMMENT '维度名称', " +
|
|
|
- "dimension_type VARCHAR(50) NOT NULL DEFAULT 'five' COMMENT '类型: five(五维)/action(行动)/other', " +
|
|
|
- "parent_id BIGINT COMMENT '父级ID,无父级为NULL', " +
|
|
|
- "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "enabled TINYINT(1) DEFAULT 1 COMMENT '启用: 0禁用 1启用', " +
|
|
|
- "dify_dataset_id VARCHAR(100) COMMENT 'Dify Dataset ID,不填则不同步', " +
|
|
|
- "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维度配置表'");
|
|
|
- log.info("已创建product_dimension_config表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建product_dimension_config表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 创建知识库表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base (" +
|
|
|
- "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
- "title VARCHAR(200) NOT NULL COMMENT '知识标题', " +
|
|
|
- "content TEXT NOT NULL COMMENT '知识内容', " +
|
|
|
- "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "status TINYINT(1) DEFAULT 1 COMMENT '状态: 0禁用 1启用', " +
|
|
|
- "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库表'");
|
|
|
- log.info("已创建dan_knowledge_base表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建dan_knowledge_base表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 创建标签表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_tag (" +
|
|
|
- "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
- "tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT '标签名', " +
|
|
|
- "tag_type VARCHAR(50) DEFAULT 'custom' COMMENT '标签类型: custom/auto', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签表'");
|
|
|
- log.info("已创建dan_knowledge_tag表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建dan_knowledge_tag表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 创建知识-维度关联表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_dimension (" +
|
|
|
- "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
- "knowledge_id BIGINT NOT NULL COMMENT '知识库ID', " +
|
|
|
- "dimension_id BIGINT NOT NULL COMMENT '维度配置ID', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_knowledge_dimension (knowledge_id, dimension_id) " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-维度关联表'");
|
|
|
- log.info("已创建dan_knowledge_base_dimension表");
|
|
|
-
|
|
|
- // 添加外键约束(单独执行避免CREATE TABLE时依赖表不存在)
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_dimension FOREIGN KEY (dimension_id) REFERENCES product_dimension_config(id) ON DELETE CASCADE");
|
|
|
- log.info("已添加dan_knowledge_base_dimension外键约束");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建dan_knowledge_base_dimension表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 创建知识-标签关联表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_tag (" +
|
|
|
- "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
- "knowledge_id BIGINT NOT NULL, " +
|
|
|
- "tag_id BIGINT NOT NULL, " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_knowledge_tag (knowledge_id, tag_id) " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-标签关联表'");
|
|
|
- log.info("已创建dan_knowledge_base_tag表");
|
|
|
-
|
|
|
- // 添加外键约束
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_tag FOREIGN KEY (tag_id) REFERENCES dan_knowledge_tag(id) ON DELETE CASCADE");
|
|
|
- log.info("已添加dan_knowledge_base_tag外键约束");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建dan_knowledge_base_tag表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移32: products表添加配送/类目/销售字段(商品编辑表单对齐)
|
|
|
+ // 迁移32: products表添加配送/类目/销售字段(商品编辑表单对齐)
|
|
|
ensureColumn("products", "delivery_method", "TINYINT DEFAULT 1 COMMENT '配送方式: 1=快递 2=自提 3=两者皆可'");
|
|
|
ensureColumn("products", "category_id", "BIGINT COMMENT '商品类目ID'");
|
|
|
ensureColumn("products", "distribution_system_id", "BIGINT COMMENT '销售体系ID'");
|
|
|
@@ -4269,11 +4135,6 @@ try {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 迁移: foods表添加营养字段
|
|
|
- ensureColumn("foods", "energy_kj", "DECIMAL(8,2) COMMENT '能量(kJ/100g)'");
|
|
|
- ensureColumn("foods", "starch", "DECIMAL(8,2) COMMENT '淀粉(g/100g)'");
|
|
|
- ensureColumn("foods", "cholesterol", "DECIMAL(8,2) COMMENT '胆固醇(mg/100g)'");
|
|
|
-
|
|
|
// 初始化成长任务种子数据
|
|
|
try {
|
|
|
Integer count = jdbcTemplate.queryForObject(
|
|
|
@@ -4525,20 +4386,6 @@ try {
|
|
|
} catch (Exception e) {
|
|
|
log.warn("创建 tianpan_member_annual_energy 表可能已存在: {}", e.getMessage());
|
|
|
}
|
|
|
-
|
|
|
- // 迁移 81: family_member_attributes 添加西方星座字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_member_attributes ADD COLUMN western_zodiac VARCHAR(20) COMMENT '西方星座代码'");
|
|
|
- log.info("已添加 western_zodiac 列到 family_member_attributes 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("添加 western_zodiac 列可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_member_attributes ADD COLUMN western_zodiac_name VARCHAR(50) COMMENT '西方星座名称'");
|
|
|
- log.info("已添加 western_zodiac_name 列到 family_member_attributes 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("添加 western_zodiac_name 列可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
private void insertSysConfigSeed(String key, String value, String desc) {
|
|
|
@@ -8586,107 +8433,7 @@ private void runMigration100() {
|
|
|
// 迁移225: articles 添加 growth_category 列(成长分类筛选,Article 实体已含字段,生产库缺列导致 Unknown column 'growth_category')
|
|
|
ensureColumn("articles", "growth_category", "VARCHAR(50) DEFAULT NULL COMMENT '成长分类'");
|
|
|
}
|
|
|
- private void seedFullRegionData() {
|
|
|
- try {
|
|
|
- Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM streets WHERE level = 1", Integer.class);
|
|
|
- if (count != null && count >= 20) {
|
|
|
- log.info("全国省市区数据已存在({}个省份),跳过导入", count);
|
|
|
- return;
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查省份数据失败,跳过全量导入: {}", e.getMessage());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:region_data.json");
|
|
|
- if (!resource.exists()) {
|
|
|
- log.warn("region_data.json 不存在,跳过全量省市区数据导入");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- String content = new String(StreamUtils.copyToByteArray(resource.getInputStream()), StandardCharsets.UTF_8);
|
|
|
- com.alibaba.fastjson.JSONArray provinces = com.alibaba.fastjson.JSON.parseArray(content);
|
|
|
-
|
|
|
- for (int pi = 0; pi < provinces.size(); pi++) {
|
|
|
- com.alibaba.fastjson.JSONObject prov = provinces.getJSONObject(pi);
|
|
|
- String provName = prov.getString("name");
|
|
|
- String provCode = prov.getString("code");
|
|
|
-
|
|
|
- java.sql.Connection conn = jdbcTemplate.getDataSource().getConnection();
|
|
|
- java.sql.PreparedStatement ps = conn.prepareStatement(
|
|
|
- "INSERT INTO streets (province, province_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, 1, NULL, ?)",
|
|
|
- java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
- );
|
|
|
- ps.setString(1, provName);
|
|
|
- ps.setString(2, provCode);
|
|
|
- ps.setString(3, provName);
|
|
|
- ps.setInt(4, pi + 1);
|
|
|
- ps.executeUpdate();
|
|
|
- java.sql.ResultSet rs = ps.getGeneratedKeys();
|
|
|
- long provId = 0;
|
|
|
- if (rs.next()) provId = rs.getLong(1);
|
|
|
- rs.close();
|
|
|
- ps.close();
|
|
|
-
|
|
|
- com.alibaba.fastjson.JSONArray cities = prov.getJSONArray("cities");
|
|
|
- if (cities == null) continue;
|
|
|
-
|
|
|
- for (int ci = 0; ci < cities.size(); ci++) {
|
|
|
- com.alibaba.fastjson.JSONObject city = cities.getJSONObject(ci);
|
|
|
- String cityName = city.getString("name");
|
|
|
- String cityCode = city.getString("code");
|
|
|
-
|
|
|
- java.sql.PreparedStatement ps2 = conn.prepareStatement(
|
|
|
- "INSERT INTO streets (province, province_code, city, city_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, ?, ?, 2, ?, ?)",
|
|
|
- java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
- );
|
|
|
- ps2.setString(1, provName);
|
|
|
- ps2.setString(2, provCode);
|
|
|
- ps2.setString(3, cityName);
|
|
|
- ps2.setString(4, cityCode);
|
|
|
- ps2.setString(5, provName + cityName);
|
|
|
- ps2.setLong(6, provId);
|
|
|
- ps2.setInt(7, ci + 1);
|
|
|
- ps2.executeUpdate();
|
|
|
- java.sql.ResultSet rs2 = ps2.getGeneratedKeys();
|
|
|
- long cityId = 0;
|
|
|
- if (rs2.next()) cityId = rs2.getLong(1);
|
|
|
- rs2.close();
|
|
|
- ps2.close();
|
|
|
-
|
|
|
- com.alibaba.fastjson.JSONArray districts = city.getJSONArray("districts");
|
|
|
- if (districts == null) continue;
|
|
|
-
|
|
|
- for (int di = 0; di < districts.size(); di++) {
|
|
|
- com.alibaba.fastjson.JSONObject dist = districts.getJSONObject(di);
|
|
|
- String distName = dist.getString("name");
|
|
|
- String distCode = dist.getString("code");
|
|
|
-
|
|
|
- java.sql.PreparedStatement ps3 = conn.prepareStatement(
|
|
|
- "INSERT INTO streets (province, province_code, city, city_code, district, district_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, 3, ?, ?)",
|
|
|
- java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
- );
|
|
|
- ps3.setString(1, provName);
|
|
|
- ps3.setString(2, provCode);
|
|
|
- ps3.setString(3, cityName);
|
|
|
- ps3.setString(4, cityCode);
|
|
|
- ps3.setString(5, distName);
|
|
|
- ps3.setString(6, distCode);
|
|
|
- ps3.setString(7, provName + cityName + distName);
|
|
|
- ps3.setLong(8, cityId);
|
|
|
- ps3.setInt(9, di + 1);
|
|
|
- ps3.executeUpdate();
|
|
|
- ps3.close();
|
|
|
- }
|
|
|
- }
|
|
|
- conn.close();
|
|
|
- }
|
|
|
- log.info("全国省市区数据导入完成:{}省/{}市/{}区", provinces.size(), 0, 0);
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("导入全国省市区数据失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
+ private void runFullRegionDataMigrations() {
|
|
|
// 迁移226: 创建 family_platform_balance 表 + family_platform_balance_log 表 + products.points_multiplier 列
|
|
|
try {
|
|
|
jdbcTemplate.execute(
|
|
|
@@ -9113,6 +8860,225 @@ private void runMigration100() {
|
|
|
ensureColumn("product_skus", "linked_product_id", "BIGINT COMMENT '关联商品ID,null=Legacy SKU'");
|
|
|
ensureColumn("product_skus", "label", "VARCHAR(100) COMMENT '自定义显示标签,null=使用linked_product.name'");
|
|
|
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS contacts (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '姓名', " +
|
|
|
+ "phone VARCHAR(20) COMMENT '电话', " +
|
|
|
+ "avatar VARCHAR(500) COMMENT '头像', " +
|
|
|
+ "relationship_type VARCHAR(20) COMMENT '关系类型: family/friend/partner/colleague/other', " +
|
|
|
+ "known_since DATETIME COMMENT '认识时间', " +
|
|
|
+ "bio VARCHAR(500) COMMENT '简介', " +
|
|
|
+ "birthday DATE COMMENT '生日', " +
|
|
|
+ "intimacy_level INT DEFAULT 0 COMMENT '亲密度(0-100)', " +
|
|
|
+ "contact_source VARCHAR(20) DEFAULT 'manual' COMMENT '来源: phone/manual', " +
|
|
|
+ "tags VARCHAR(500) COMMENT '标签(逗号分隔)', " +
|
|
|
+ "last_contact_at DATETIME COMMENT '最近联系时间', " +
|
|
|
+ "contact_count INT DEFAULT 0 COMMENT '联系次数', " +
|
|
|
+ "notes TEXT COMMENT '备注', " +
|
|
|
+ "created_at DATETIME, " +
|
|
|
+ "updated_at DATETIME, " +
|
|
|
+ "INDEX idx_user_id (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='联系人表'");
|
|
|
+ log.info("已创建contacts联系表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建contacts表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS product_dimension_config (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "dimension_code VARCHAR(50) NOT NULL UNIQUE COMMENT '维度代码: body/mind/wisdom/action/wealth', " +
|
|
|
+ "dimension_name VARCHAR(100) NOT NULL COMMENT '维度名称', " +
|
|
|
+ "dimension_type VARCHAR(50) NOT NULL DEFAULT 'five' COMMENT '类型: five(五维)/action(行动)/other', " +
|
|
|
+ "parent_id BIGINT COMMENT '父级ID,无父级为NULL', " +
|
|
|
+ "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "enabled TINYINT(1) DEFAULT 1 COMMENT '启用: 0禁用 1启用', " +
|
|
|
+ "dify_dataset_id VARCHAR(100) COMMENT 'Dify Dataset ID,不填则不同步', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维度配置表'");
|
|
|
+ log.info("已创建product_dimension_config表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建product_dimension_config表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "title VARCHAR(200) NOT NULL COMMENT '知识标题', " +
|
|
|
+ "content TEXT NOT NULL COMMENT '知识内容', " +
|
|
|
+ "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "status TINYINT(1) DEFAULT 1 COMMENT '状态: 0禁用 1启用', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库表'");
|
|
|
+ log.info("已创建dan_knowledge_base表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_tag (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT '标签名', " +
|
|
|
+ "tag_type VARCHAR(50) DEFAULT 'custom' COMMENT '标签类型: custom/auto', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签表'");
|
|
|
+ log.info("已创建dan_knowledge_tag表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_tag表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_dimension (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "knowledge_id BIGINT NOT NULL COMMENT '知识库ID', " +
|
|
|
+ "dimension_id BIGINT NOT NULL COMMENT '维度配置ID', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_knowledge_dimension (knowledge_id, dimension_id) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-维度关联表'");
|
|
|
+ log.info("已创建dan_knowledge_base_dimension表");
|
|
|
+
|
|
|
+ // 添加外键约束(单独执行避免CREATE TABLE时依赖表不存在)
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_dimension FOREIGN KEY (dimension_id) REFERENCES product_dimension_config(id) ON DELETE CASCADE");
|
|
|
+ log.info("已添加dan_knowledge_base_dimension外键约束");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base_dimension表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_tag (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "knowledge_id BIGINT NOT NULL, " +
|
|
|
+ "tag_id BIGINT NOT NULL, " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_knowledge_tag (knowledge_id, tag_id) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-标签关联表'");
|
|
|
+ log.info("已创建dan_knowledge_base_tag表");
|
|
|
+
|
|
|
+ // 添加外键约束
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_tag FOREIGN KEY (tag_id) REFERENCES dan_knowledge_tag(id) ON DELETE CASCADE");
|
|
|
+ log.info("已添加dan_knowledge_base_tag外键约束");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base_tag表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ // 迁移222b: health_reports 添加 shannon_index, payload_json 列(原迁移222仅测试环境执行、迁移242在菜单种子方法内,生产库均不执行)
|
|
|
+ ensureColumn("health_reports", "shannon_index", "VARCHAR(32) DEFAULT NULL COMMENT '香农指数'");
|
|
|
+ ensureColumn("health_reports", "payload_json", "LONGTEXT COMMENT '报告完整载荷快照'");
|
|
|
+ // 迁移222c: foods 表添加营养字段(原位于initGuideModuleTables仅测试环境执行,foods建表在initFoodTables始终执行)
|
|
|
+ ensureColumn("foods", "energy_kj", "DECIMAL(8,2) COMMENT '能量(kJ/100g)'");
|
|
|
+ ensureColumn("foods", "starch", "DECIMAL(8,2) COMMENT '淀粉(g/100g)'");
|
|
|
+ ensureColumn("foods", "cholesterol", "DECIMAL(8,2) COMMENT '胆固醇(mg/100g)'");
|
|
|
+ // 迁移222d: 原initGuideModuleTables内的生产增量列,schema.sql未覆盖,生产库缺失
|
|
|
+ ensureColumn("health_indicators", "symptoms", "TEXT COMMENT '过量/缺乏相关症状描述'");
|
|
|
+ ensureColumn("activities", "visibility", "VARCHAR(20) DEFAULT 'all' COMMENT '可见性: all/member'");
|
|
|
+ ensureColumn("activities", "visible_to", "VARCHAR(255) COMMENT '可见角色列表(逗号分隔)'");
|
|
|
+ ensureColumn("family_member_attributes", "western_zodiac", "VARCHAR(20) COMMENT '西方星座代码'");
|
|
|
+ ensureColumn("family_member_attributes", "western_zodiac_name", "VARCHAR(50) COMMENT '西方星座名称'");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void seedFullRegionData() {
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM streets WHERE level = 1", Integer.class);
|
|
|
+ if (count != null && count >= 20) {
|
|
|
+ log.info("全国省市区数据已存在({}个省份),跳过导入", count);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查省份数据失败,跳过全量导入: {}", e.getMessage());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:region_data.json");
|
|
|
+ if (!resource.exists()) {
|
|
|
+ log.warn("region_data.json 不存在,跳过全量省市区数据导入");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ String content = new String(StreamUtils.copyToByteArray(resource.getInputStream()), StandardCharsets.UTF_8);
|
|
|
+ com.alibaba.fastjson.JSONArray provinces = com.alibaba.fastjson.JSON.parseArray(content);
|
|
|
+
|
|
|
+ for (int pi = 0; pi < provinces.size(); pi++) {
|
|
|
+ com.alibaba.fastjson.JSONObject prov = provinces.getJSONObject(pi);
|
|
|
+ String provName = prov.getString("name");
|
|
|
+ String provCode = prov.getString("code");
|
|
|
+
|
|
|
+ java.sql.Connection conn = jdbcTemplate.getDataSource().getConnection();
|
|
|
+ java.sql.PreparedStatement ps = conn.prepareStatement(
|
|
|
+ "INSERT INTO streets (province, province_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, 1, NULL, ?)",
|
|
|
+ java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
+ );
|
|
|
+ ps.setString(1, provName);
|
|
|
+ ps.setString(2, provCode);
|
|
|
+ ps.setString(3, provName);
|
|
|
+ ps.setInt(4, pi + 1);
|
|
|
+ ps.executeUpdate();
|
|
|
+ java.sql.ResultSet rs = ps.getGeneratedKeys();
|
|
|
+ long provId = 0;
|
|
|
+ if (rs.next()) provId = rs.getLong(1);
|
|
|
+ rs.close();
|
|
|
+ ps.close();
|
|
|
+
|
|
|
+ com.alibaba.fastjson.JSONArray cities = prov.getJSONArray("cities");
|
|
|
+ if (cities == null) continue;
|
|
|
+
|
|
|
+ for (int ci = 0; ci < cities.size(); ci++) {
|
|
|
+ com.alibaba.fastjson.JSONObject city = cities.getJSONObject(ci);
|
|
|
+ String cityName = city.getString("name");
|
|
|
+ String cityCode = city.getString("code");
|
|
|
+
|
|
|
+ java.sql.PreparedStatement ps2 = conn.prepareStatement(
|
|
|
+ "INSERT INTO streets (province, province_code, city, city_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, ?, ?, 2, ?, ?)",
|
|
|
+ java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
+ );
|
|
|
+ ps2.setString(1, provName);
|
|
|
+ ps2.setString(2, provCode);
|
|
|
+ ps2.setString(3, cityName);
|
|
|
+ ps2.setString(4, cityCode);
|
|
|
+ ps2.setString(5, provName + cityName);
|
|
|
+ ps2.setLong(6, provId);
|
|
|
+ ps2.setInt(7, ci + 1);
|
|
|
+ ps2.executeUpdate();
|
|
|
+ java.sql.ResultSet rs2 = ps2.getGeneratedKeys();
|
|
|
+ long cityId = 0;
|
|
|
+ if (rs2.next()) cityId = rs2.getLong(1);
|
|
|
+ rs2.close();
|
|
|
+ ps2.close();
|
|
|
+
|
|
|
+ com.alibaba.fastjson.JSONArray districts = city.getJSONArray("districts");
|
|
|
+ if (districts == null) continue;
|
|
|
+
|
|
|
+ for (int di = 0; di < districts.size(); di++) {
|
|
|
+ com.alibaba.fastjson.JSONObject dist = districts.getJSONObject(di);
|
|
|
+ String distName = dist.getString("name");
|
|
|
+ String distCode = dist.getString("code");
|
|
|
+
|
|
|
+ java.sql.PreparedStatement ps3 = conn.prepareStatement(
|
|
|
+ "INSERT INTO streets (province, province_code, city, city_code, district, district_code, full_name, level, parent_id, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, 3, ?, ?)",
|
|
|
+ java.sql.Statement.RETURN_GENERATED_KEYS
|
|
|
+ );
|
|
|
+ ps3.setString(1, provName);
|
|
|
+ ps3.setString(2, provCode);
|
|
|
+ ps3.setString(3, cityName);
|
|
|
+ ps3.setString(4, cityCode);
|
|
|
+ ps3.setString(5, distName);
|
|
|
+ ps3.setString(6, distCode);
|
|
|
+ ps3.setString(7, provName + cityName + distName);
|
|
|
+ ps3.setLong(8, cityId);
|
|
|
+ ps3.setInt(9, di + 1);
|
|
|
+ ps3.executeUpdate();
|
|
|
+ ps3.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ conn.close();
|
|
|
+ }
|
|
|
+ log.info("全国省市区数据导入完成:{}省/{}市/{}区", provinces.size(), 0, 0);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("导入全国省市区数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -9257,12 +9223,5 @@ private void runMigration100() {
|
|
|
Long id = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class);
|
|
|
keyToId.put(m[1], id);
|
|
|
}
|
|
|
- // 迁移242: health_reports 表添加 payload_json 字段(报告完整载荷快照)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN payload_json LONGTEXT DEFAULT NULL COMMENT '报告完整载荷快照'");
|
|
|
- log.info("已添加payload_json列到health_reports表");
|
|
|
- } catch (Exception ex) {
|
|
|
- log.warn("添加payload_json列失败: {}", ex.getMessage());
|
|
|
- }
|
|
|
}
|
|
|
}
|