feat: 管理员账号体系 + 动态邀请码管理; 各端内置数据固定 uuid 对齐

This commit is contained in:
root
2026-10-16 15:24:49 +08:00
parent e0866aa690
commit dc520bfac0
7 changed files with 305 additions and 134 deletions
+96 -60
View File
@@ -231,12 +231,55 @@ fn get_row_uuid(conn: &Connection, table: &str, id: i64) -> Option<String> {
conn.query_row(&format!("SELECT uuid FROM {} WHERE id=?1", table), [id], |r| r.get(0)).ok()
}
// 固定内置 uuid(与 Node 端 BUILTIN_UUID 完全一致;各端互通的对齐前提)
const FIXED_BUILTIN: [(&str, &str, &str, &str); 28] = [
("footprint_type", "name", "学业", "018f0000-0000-7000-8000-000000000001"),
("footprint_type", "name", "竞赛", "018f0000-0000-7000-8000-000000000002"),
("footprint_type", "name", "科研", "018f0000-0000-7000-8000-000000000003"),
("footprint_type", "name", "实习实践", "018f0000-0000-7000-8000-000000000004"),
("footprint_type", "name", "学生工作", "018f0000-0000-7000-8000-000000000005"),
("footprint_type", "name", "志愿公益", "018f0000-0000-7000-8000-000000000006"),
("footprint_type", "name", "文体活动", "018f0000-0000-7000-8000-000000000007"),
("footprint_type", "name", "证书技能", "018f0000-0000-7000-8000-000000000008"),
("footprint_type", "name", "荣誉惩处", "018f0000-0000-7000-8000-000000000009"),
("taxonomy", "value", "国际级", "018f0000-0000-7000-8000-000000000011"),
("taxonomy", "value", "国家级", "018f0000-0000-7000-8000-000000000012"),
("taxonomy", "value", "省市级", "018f0000-0000-7000-8000-000000000013"),
("taxonomy", "value", "区县级", "018f0000-0000-7000-8000-000000000014"),
("taxonomy", "value", "校级", "018f0000-0000-7000-8000-000000000015"),
("taxonomy", "value", "院级", "018f0000-0000-7000-8000-000000000016"),
("taxonomy", "value", "主导", "018f0000-0000-7000-8000-000000000017"),
("taxonomy", "value", "核心", "018f0000-0000-7000-8000-000000000018"),
("taxonomy", "value", "参加", "018f0000-0000-7000-8000-000000000019"),
("dimension", "name", "学业深造", "018f0000-0000-7000-8000-000000000021"),
("dimension", "name", "职业发展", "018f0000-0000-7000-8000-000000000022"),
("dimension", "name", "综合素养", "018f0000-0000-7000-8000-000000000023"),
("dimension", "name", "兴趣生活", "018f0000-0000-7000-8000-000000000024"),
("grade_band", "label", "优秀", "018f0000-0000-7000-8000-000000000031"),
("grade_band", "label", "良好", "018f0000-0000-7000-8000-000000000032"),
("grade_band", "label", "中等", "018f0000-0000-7000-8000-000000000033"),
("grade_band", "label", "及格", "018f0000-0000-7000-8000-000000000034"),
("grade_band", "label", "不及格", "018f0000-0000-7000-8000-000000000035"),
("metric_def", "key", "volunteer_hours", "018f0000-0000-7000-8000-000000000041"),
];
fn align_builtin_uuids(conn: &Connection) {
for (table, col, name, uuid) in FIXED_BUILTIN {
let sql = format!("UPDATE {} SET uuid=?1 WHERE {}=?2 AND uuid<>?1", table, col);
let _ = conn.execute(&sql, rusqlite::params![uuid, name]);
}
}
fn ensure_builtin(conn: &Connection) {
align_builtin_uuids(conn);
let cnt: i64 = conn
.query_row("SELECT COUNT(*) FROM footprint_type WHERE deleted_at IS NULL", [], |r| r.get(0))
.unwrap_or(0);
if cnt == 0 {
let types = [
if cnt > 0 {
align_builtin_uuids(conn);
return;
}
let types = [
("学业", "graduation-cap", "#3056d3", 10, r#"[]"#),
("竞赛", "trophy", "#7c3aed", 20, r#"[{"key":"my_work","label":"我的分工","widget":"textarea","required":true},{"key":"deliverable","label":"最终产品","widget":"text"},{"key":"team_members","label":"队伍成员","widget":"text"}]"#),
("科研", "flask-conical", "#0891b2", 30, r#"[{"key":"authors","label":"作者排序","widget":"text"},{"key":"outcome","label":"成果形式","widget":"select","options":["论文","专利","软件著作权","研究报告"]}]"#),
@@ -246,81 +289,74 @@ fn ensure_builtin(conn: &Connection) {
("文体活动", "palette", "#db2777", 70, r#"[{"key":"participation","label":"参与形式","widget":"text"}]"#),
("证书技能", "file-badge", "#64748b", 80, r#"[{"key":"cert_no","label":"证书编号","widget":"text"},{"key":"score","label":"成绩","widget":"text"}]"#),
("荣誉惩处", "award", "#b45309", 90, r#"[{"key":"issued_by","label":"授予 / 决定单位","widget":"text"}]"#),
];
];
if cnt == 0 {
let mut up = String::new();
for (name, icon, color, sort, tpl) in types {
let h = hlc_next(conn);
let _ = conn.execute(
"INSERT INTO footprint_type (name, icon, color, sort, form_template, is_builtin, uuid, hlc, origin) VALUES (?,?,?,?,?,1,?,?,?)",
rusqlite::params![name, icon, color, sort, tpl, uuid_v7(), h, get_device_id(conn)],
);
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "footprint_type" && *c == "name" && *n == name);
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO footprint_type (name, icon, color, sort, form_template, is_builtin, uuid, hlc, origin) VALUES (?,?,?,?,?,1,?,?,?)",
rusqlite::params![name, icon, color, sort, tpl, uuid, "0:0:builtin", "builtin"],
);
}
}
}
let tcnt: i64 = conn
.query_row("SELECT COUNT(*) FROM taxonomy WHERE deleted_at IS NULL", [], |r| r.get(0))
.unwrap_or(0);
if tcnt == 0 {
let levels = [
("国际级", "#7c3aed", 10), ("国家级", "#dc2626", 20), ("省市级", "#ea580c", 30),
("区县级", "#2563eb", 40), ("校级", "#16a34a", 50), ("院级", "#0891b2", 60),
("国际级", "#7c3aed", 10), ("国家级", "#dc2626", 20), ("省市级", "#ea580c", 30),
("区县级", "#2563eb", 40), ("校级", "#16a34a", 50), ("院级", "#0891b2", 60),
];
for (v, c, s) in levels {
let h = hlc_next(conn);
let _ = conn.execute(
"INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES ('level',?,?,?,1,?,?,?)",
rusqlite::params![v, c, s, uuid_v7(), h, get_device_id(conn)],
);
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "taxonomy" && *n == v);
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES ('level',?,?,?,1,?,?,?)",
rusqlite::params![v, c, s, uuid, "0:0:builtin", "builtin"],
);
}
}
let roles = [("主导", "#3056d3", 10), ("核心", "#0891b2", 20), ("参加", "#64748b", 30)];
for (v, c, s) in roles {
let h = hlc_next(conn);
let _ = conn.execute(
"INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES ('role',?,?,?,1,?,?,?)",
rusqlite::params![v, c, s, uuid_v7(), h, get_device_id(conn)],
);
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "taxonomy" && *n == v);
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES ('role',?,?,?,1,?,?,?)",
rusqlite::params![v, c, s, uuid, "0:0:builtin", "builtin"],
);
}
}
}
let dcnt: i64 = conn
.query_row("SELECT COUNT(*) FROM dimension WHERE deleted_at IS NULL", [], |r| r.get(0))
.unwrap_or(0);
if dcnt == 0 {
let dims = [
("学业深造", "#3056d3", 10), ("职业发展", "#0891b2", 20),
("综合素养", "#16a34a", 30), ("兴趣生活", "#db2777", 40),
("学业深造", "#3056d3", 10), ("职业发展", "#0891b2", 20),
("综合素养", "#16a34a", 30), ("兴趣生活", "#db2777", 40),
];
for (v, c, s) in dims {
let h = hlc_next(conn);
let _ = conn.execute(
"INSERT INTO dimension (name, color, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?)",
rusqlite::params![v, c, s, uuid_v7(), h, get_device_id(conn)],
);
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "dimension" && *n == v);
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO dimension (name, color, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?)",
rusqlite::params![v, c, s, uuid, "0:0:builtin", "builtin"],
);
}
}
}
let mcnt: i64 = conn
.query_row("SELECT COUNT(*) FROM metric_def WHERE deleted_at IS NULL", [], |r| r.get(0))
.unwrap_or(0);
if mcnt == 0 {
let h = hlc_next(conn);
let _ = conn.execute(
"INSERT INTO metric_def (key, label, unit, agg, section, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)",
rusqlite::params!["volunteer_hours", "志愿时长", "小时", "sum", "志愿服务", uuid_v7(), h, get_device_id(conn)],
);
}
let bcnt: i64 = conn
.query_row("SELECT COUNT(*) FROM grade_band WHERE deleted_at IS NULL", [], |r| r.get(0))
.unwrap_or(0);
if bcnt == 0 {
let bands = [
("优秀", "#16803c", 90.0, 100.0, 10),
("良好", "#0891b2", 80.0, 90.0, 20),
("中等", "#d97706", 70.0, 80.0, 30),
("及格", "#ea580c", 60.0, 70.0, 40),
("不及格", "#dc2626", 0.0, 60.0, 50),
("优秀", "#16803c", 90.0, 100.0, 10), ("良好", "#0891b2", 80.0, 90.0, 20),
("中等", "#d97706", 70.0, 80.0, 30), ("及格", "#ea580c", 60.0, 70.0, 40),
("不及格", "#dc2626", 0.0, 60.0, 50),
];
for (label, color, min, max, sort) in bands {
let h = hlc_next(conn);
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "grade_band" && *n == label);
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO grade_band (label, color, min, max, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)",
rusqlite::params![label, color, min, max, sort, uuid, "0:0:builtin", "builtin"],
);
}
}
let h = "0:0:builtin";
let fixed = FIXED_BUILTIN.iter().find(|(t, c, n, _)| *t == "metric_def" && *n == "volunteer_hours");
if let Some((_, _, _, uuid)) = fixed {
let _ = conn.execute(
"INSERT INTO grade_band (label, color, min, max, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)",
rusqlite::params![label, color, min, max, sort, uuid_v7(), h, get_device_id(conn)],
"INSERT INTO metric_def (key, label, unit, agg, section, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)",
rusqlite::params!["volunteer_hours", "志愿时长", "小时", "sum", "志愿服务", uuid, h, "builtin"],
);
}
}