package fixture import ( "database/sql" "time" ) // 本文件是**第三批(org 域)的数据形状**:机构 t_institution + 校区 t_campus。 // // 口径(已拍板): // - **两张表都走 D1**:`t_school.id` → `t_institution.institution_id`、`t_school_campus.id` // → `t_campus.campus_id`,**原值插入**。理由:两者都是 int 自然主键、数据量极小, // 而 institution_id / campus_id 在几十张表里是裸 int,双跑要靠 id 直接对齐; // 走 biz_no 反而要多一跳。 // - **只迁数据用得到的机构**,不全量迁 t_school(61905 行里 61900 行与本项目无关)。 // - `campus_id = 0` **不建行**:0 是"机构级/未分配"的约定值(DDL 里 `campus_id ... DEFAULT 0 // COMMENT '校区ID(0=平台级/机构级)'`),建了会让前端校区下拉框多出一个"未知校区"。 // OrgOptions 是第三批的取样参数。 type OrgOptions struct { // InstitutionIDs 是要迁移的机构 id 集合(D1 原值)。 // // 这是**显式决策**,不是从数据里自动推导的:多迁一个机构 = 多一个孤儿机构; // 少迁一个 = 引用它的一整批数据变成孤儿引用。集合必须由人确认。 InstitutionIDs []int64 // WithAllCampuses 是否迁移所选机构的**全部**校区(true) // 还是只迁数据里出现过的校区(false)。默认 true:机构迁了、校区不迁会让 // 前端校区下拉框与机构对不上,且属于 org 域自身的不一致。 WithAllCampuses bool OrgSourceDB string } // DefaultOrgOptions 是默认取样 = **7 个机构**。 // // ⚠ 初版统计给的是 5 个(`SELECT DISTINCT schoolid FROM t_courses` + campus 1/2 的 school_id=1), // 实测逐来源清点后发现**漏了 2 个**,各自都带孤儿引用: // // 63332(用户注册)← finance_bk.t_user_balance_request 1 行(schoolid=63332, campusid=57) // 63330(弘毅教育)← finance_bk.t_courses_classify 1 行(schoolid=63330) // —— 这张表不在原统计的 7 个来源清单里,是靠"按列名反查"找出来的 // // 只迁 5 个会让这两个来源各留 1 条孤儿机构 + 1 条孤儿校区(实测见报告)。 func DefaultOrgOptions() OrgOptions { return OrgOptions{ InstitutionIDs: []int64{1, 63317, 63323, 63324, 63325, 63330, 63332}, WithAllCampuses: true, OrgSourceDB: "yuansiwei_i", } } // ---------- legacy(yuansiwei_i) ---------- // LegacySchool 对应 `yuansiwei_i.t_school`。 type LegacySchool struct { ID int64 Name string ProvinceID int64 CityID int64 DistrictID int64 GradePeriod string Status int8 // tinyint(1):1=正常 0=停用(业务口径,见 mapping 注释) IsOpenUserLogin int8 } // LegacyCampus 对应 `yuansiwei_i.t_school_campus`。 type LegacyCampus struct { ID int64 Name string SchoolID int64 ProvinceID int64 CityID int64 DistrictID int64 Status int8 Code sql.NullString SelfSupport int8 // bit(1) → 0/1 IsOpenUserLogin int8 } // ---------- target ---------- // TargetInstitution 对应 `t_institution`(16 列里只用得到的那些)。 type TargetInstitution struct { InstitutionID int64 InstitutionName string InstitutionCode sql.NullString // legacy 无对应列 → NULL(uk_institution_code 允许多 NULL) Address sql.NullString // legacy 只有省市区 id,没有地址文本 → NULL Phone sql.NullString // legacy 无 → NULL Status string // active / disabled CreatedAt time.Time // legacy 无时间列 → epoch 哨兵 UpdatedAt time.Time } // TargetCampus 对应 `t_campus`。 type TargetCampus struct { CampusID int64 InstitutionID int64 CampusName string CampusCode sql.NullString // legacy code(varchar(16))→ campus_code Address sql.NullString Phone sql.NullString Status string SelfSupport int8 // bit(1) → TINYINT(1) CreatedAt time.Time UpdatedAt time.Time } // ---------- 引用完整性 ---------- // RefSource 是一个「带 institution_id / campus_id 的数据来源」。 type RefSource struct { Table string // finance_bk 里的表名 InstCol string // 机构列 CampusCol string // 校区列(空 = 该表没有校区列) Note string } // RefStat 是一个来源的引用统计结果。 type RefStat struct { Source string Rows int64 // 该表总行数 DistinctInst int // distinct institution_id DistinctCampus int // distinct campus_id(不含 0) OrphanInst int64 // institution_id 在 target 里找不到的行数 OrphanCampus int64 // campus_id != 0 但在 target 里找不到的行数 CampusZeroRows int64 // campus_id = 0(机构级,不建行)的行数 OrphanInstList []string OrphanCampusList []string }