feat: 管理员账号体系 + 动态邀请码管理; 各端内置数据固定 uuid 对齐
This commit is contained in:
Binary file not shown.
Binary file not shown.
+119
-52
@@ -127,6 +127,57 @@ function softDelete(d, table, id) {
|
||||
touchOplog(d, table, uuid, 'delete', h);
|
||||
}
|
||||
|
||||
// ---------- 全局固定内置 uuid(各端一致;Rust 端 ensure_builtin 使用同一映射) ----------
|
||||
export const BUILTIN_UUID = {
|
||||
'类型:学业': '018f0000-0000-7000-8000-000000000001',
|
||||
'类型:竞赛': '018f0000-0000-7000-8000-000000000002',
|
||||
'类型:科研': '018f0000-0000-7000-8000-000000000003',
|
||||
'类型:实习实践': '018f0000-0000-7000-8000-000000000004',
|
||||
'类型:学生工作': '018f0000-0000-7000-8000-000000000005',
|
||||
'类型:志愿公益': '018f0000-0000-7000-8000-000000000006',
|
||||
'类型:文体活动': '018f0000-0000-7000-8000-000000000007',
|
||||
'类型:证书技能': '018f0000-0000-7000-8000-000000000008',
|
||||
'类型:荣誉惩处': '018f0000-0000-7000-8000-000000000009',
|
||||
'级别:国际级': '018f0000-0000-7000-8000-000000000011',
|
||||
'级别:国家级': '018f0000-0000-7000-8000-000000000012',
|
||||
'级别:省市级': '018f0000-0000-7000-8000-000000000013',
|
||||
'级别:区县级': '018f0000-0000-7000-8000-000000000014',
|
||||
'级别:校级': '018f0000-0000-7000-8000-000000000015',
|
||||
'级别:院级': '018f0000-0000-7000-8000-000000000016',
|
||||
'角色:主导': '018f0000-0000-7000-8000-000000000017',
|
||||
'角色:核心': '018f0000-0000-7000-8000-000000000018',
|
||||
'角色:参加': '018f0000-0000-7000-8000-000000000019',
|
||||
'维度:学业深造': '018f0000-0000-7000-8000-000000000021',
|
||||
'维度:职业发展': '018f0000-0000-7000-8000-000000000022',
|
||||
'维度:综合素养': '018f0000-0000-7000-8000-000000000023',
|
||||
'维度:兴趣生活': '018f0000-0000-7000-8000-000000000024',
|
||||
'梯度:优秀': '018f0000-0000-7000-8000-000000000031',
|
||||
'梯度:良好': '018f0000-0000-7000-8000-000000000032',
|
||||
'梯度:中等': '018f0000-0000-7000-8000-000000000033',
|
||||
'梯度:及格': '018f0000-0000-7000-8000-000000000034',
|
||||
'梯度:不及格': '018f0000-0000-7000-8000-000000000035',
|
||||
'指标:volunteer_hours': '018f0000-0000-7000-8000-000000000041'
|
||||
};
|
||||
|
||||
// 内置 uuid 按名称对齐(存量库与新库统一;origin=builtin)
|
||||
function alignBuiltinUuids(db) {
|
||||
const fix = (table, key) => {
|
||||
const uuid = BUILTIN_UUID[key];
|
||||
if (!uuid) return;
|
||||
db.prepare('UPDATE ' + table + ' SET uuid=? WHERE name=? AND uuid<>?').run(uuid, key.split(':')[1], uuid);
|
||||
};
|
||||
for (const key of Object.keys(BUILTIN_UUID)) {
|
||||
const [kind, name] = key.split(':');
|
||||
const uuid = BUILTIN_UUID[key];
|
||||
if (kind === '类型') db.prepare('UPDATE footprint_type SET uuid=? WHERE name=? AND uuid<>?').run(uuid, name, uuid);
|
||||
else if (kind === '级别') db.prepare("UPDATE taxonomy SET uuid=? WHERE kind='level' AND value=? AND uuid<>?").run(uuid, name, uuid);
|
||||
else if (kind === '角色') db.prepare("UPDATE taxonomy SET uuid=? WHERE kind='role' AND value=? AND uuid<>?").run(uuid, name, uuid);
|
||||
else if (kind === '维度') db.prepare('UPDATE dimension SET uuid=? WHERE name=? AND uuid<>?').run(uuid, name, uuid);
|
||||
else if (kind === '梯度') db.prepare('UPDATE grade_band SET uuid=? WHERE label=? AND uuid<>?').run(uuid, name, uuid);
|
||||
else if (kind === '指标') db.prepare('UPDATE metric_def SET uuid=? WHERE key=? AND uuid<>?').run(uuid, name, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
const BUILTIN_TYPES = [
|
||||
['学业', 'graduation-cap', '#3056d3', 10, JSON.stringify([
|
||||
{ key: 'semester_label', label: '学期', widget: 'text', placeholder: '如:2024-2025-1' }
|
||||
@@ -179,69 +230,85 @@ const BUILTIN_DIMENSIONS = [
|
||||
];
|
||||
|
||||
function ensureBuiltin(db) {
|
||||
const cnt = db.prepare('SELECT COUNT(*) AS c FROM footprint_type WHERE deleted_at IS NULL').get().c;
|
||||
if (cnt === 0) {
|
||||
const ins = db.prepare(
|
||||
'INSERT INTO footprint_type (name, icon, color, sort, form_template, is_builtin, uuid, hlc, origin) VALUES (?,?,?,?,?,1,?,?,?)');
|
||||
for (const [name, icon, color, sort, tpl] of BUILTIN_TYPES) {
|
||||
const h = hlcNext(db);
|
||||
ins.run(name, icon, color, sort, tpl, uuidv7(), h, getDeviceId(db));
|
||||
const seed = (table, cols, rows) => {
|
||||
if (db.prepare('SELECT COUNT(*) AS c FROM ' + table + ' WHERE deleted_at IS NULL').get().c > 0) return;
|
||||
const ins = db.prepare('INSERT INTO ' + table + ' (' + cols.join(',') + ') VALUES (' + cols.map(() => '?').join(',') + ')');
|
||||
for (const row of rows) {
|
||||
const [uuidKey, ...rest] = row;
|
||||
ins.run(...rest, BUILTIN_UUID[uuidKey], '0:0:builtin', 'builtin');
|
||||
}
|
||||
}
|
||||
const tcnt = db.prepare('SELECT COUNT(*) AS c FROM taxonomy WHERE deleted_at IS NULL').get().c;
|
||||
if (tcnt === 0) {
|
||||
const ins = db.prepare('INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES (?,?,?,?,1,?,?,?)');
|
||||
for (const [v, c, s] of BUILTIN_LEVELS) {
|
||||
const h = hlcNext(db);
|
||||
ins.run('level', v, c, s, uuidv7(), h, getDeviceId(db));
|
||||
}
|
||||
for (const [v, c, s] of BUILTIN_ROLES) {
|
||||
const h = hlcNext(db);
|
||||
ins.run('role', v, c, s, uuidv7(), h, getDeviceId(db));
|
||||
}
|
||||
}
|
||||
const dcnt = db.prepare('SELECT COUNT(*) AS c FROM dimension WHERE deleted_at IS NULL').get().c;
|
||||
if (dcnt === 0) {
|
||||
const ins = db.prepare('INSERT INTO dimension (name, color, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?)');
|
||||
for (const [v, c, s] of BUILTIN_DIMENSIONS) {
|
||||
const h = hlcNext(db);
|
||||
ins.run(v, c, s, uuidv7(), h, getDeviceId(db));
|
||||
}
|
||||
}
|
||||
const mcnt = db.prepare('SELECT COUNT(*) AS c FROM metric_def WHERE deleted_at IS NULL').get().c;
|
||||
if (mcnt === 0) {
|
||||
const h = hlcNext(db);
|
||||
db.prepare('INSERT INTO metric_def (key, label, unit, agg, section, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)')
|
||||
.run('volunteer_hours', '志愿时长', '小时', 'sum', '志愿服务', uuidv7(), h, getDeviceId(db));
|
||||
}
|
||||
const bcnt = db.prepare('SELECT COUNT(*) AS c FROM grade_band WHERE deleted_at IS NULL').get().c;
|
||||
if (bcnt === 0) {
|
||||
const bands = [
|
||||
['优秀', '#16803c', 90, 100, 10],
|
||||
['良好', '#0891b2', 80, 90, 20],
|
||||
['中等', '#d97706', 70, 80, 30],
|
||||
['及格', '#ea580c', 60, 70, 40],
|
||||
['不及格', '#dc2626', 0, 60, 50]
|
||||
];
|
||||
const ins = db.prepare('INSERT INTO grade_band (label, color, min, max, sort, uuid, hlc, origin) VALUES (?,?,?,?,?,?,?,?)');
|
||||
for (const [label, color, min, max, sort] of bands) {
|
||||
const h = hlcNext(db);
|
||||
ins.run(label, color, min, max, sort, uuidv7(), h, getDeviceId(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------- 查询构建 ----------
|
||||
const tpl = (o) => JSON.stringify(o);
|
||||
seed('footprint_type', ['name', 'icon', 'color', 'sort', 'form_template', 'is_builtin', 'uuid', 'hlc', 'origin'], [
|
||||
['类型:学业', '学业', 'graduation-cap', '#3056d3', 10, tpl([{ key: 'semester_label', label: '学期', widget: 'text' }]), 1],
|
||||
['类型:竞赛', '竞赛', 'trophy', '#7c3aed', 20, tpl([{ key: 'my_work', label: '我的分工', widget: 'textarea', required: true }, { key: 'deliverable', label: '最终产品', widget: 'text' }, { key: 'team_members', label: '队伍成员', widget: 'text' }]), 1],
|
||||
['类型:科研', '科研', 'flask-conical', '#0891b2', 30, tpl([{ key: 'authors', label: '作者排序', widget: 'text' }, { key: 'outcome', label: '成果形式', widget: 'select', options: ['论文', '专利', '软件著作权', '研究报告'] }]), 1],
|
||||
['类型:实习实践', '实习实践', 'briefcase', '#0f766e', 40, tpl([{ key: 'position', label: '岗位', widget: 'text' }, { key: 'mentor', label: '带教人', widget: 'text' }]), 1],
|
||||
['类型:学生工作', '学生工作', 'network', '#d97706', 50, tpl([{ key: 'duties', label: '职责', widget: 'textarea' }, { key: 'term', label: '任期', widget: 'text' }]), 1],
|
||||
['类型:志愿公益', '志愿公益', 'heart-handshake', '#16a34a', 60, tpl([{ key: 'hours', label: '志愿时长', widget: 'number', unit: '小时', metric: 'volunteer_hours' }, { key: 'contact', label: '组织负责人', widget: 'text' }]), 1],
|
||||
['类型:文体活动', '文体活动', 'palette', '#db2777', 70, tpl([{ key: 'participation', label: '参与形式', widget: 'text' }]), 1],
|
||||
['类型:证书技能', '证书技能', 'file-badge', '#64748b', 80, tpl([{ key: 'cert_no', label: '证书编号', widget: 'text' }, { key: 'score', label: '成绩', widget: 'text' }]), 1],
|
||||
['类型:荣誉惩处', '荣誉惩处', 'award', '#b45309', 90, tpl([{ key: 'issued_by', label: '授予 / 决定单位', widget: 'text' }]), 1]
|
||||
]);
|
||||
|
||||
seed('taxonomy', ['kind', 'value', 'color', 'sort', 'is_builtin', 'uuid', 'hlc', 'origin'], [
|
||||
['级别:国际级', 'level', '国际级', '#7c3aed', 10, 1],
|
||||
['级别:国家级', 'level', '国家级', '#dc2626', 20, 1],
|
||||
['级别:省市级', 'level', '省市级', '#ea580c', 30, 1],
|
||||
['级别:区县级', 'level', '区县级', '#2563eb', 40, 1],
|
||||
['级别:校级', 'level', '校级', '#16a34a', 50, 1],
|
||||
['级别:院级', 'level', '院级', '#0891b2', 60, 1],
|
||||
['角色:主导', 'role', '主导', '#3056d3', 10, 1],
|
||||
['角色:核心', 'role', '核心', '#0891b2', 20, 1],
|
||||
['角色:参加', 'role', '参加', '#64748b', 30, 1]
|
||||
]); // 注意:seed 行参数顺序见 sort 列映射
|
||||
|
||||
// taxonomy 的 seed 行参数顺序与列序不同,单独处理(kind,value,color,sort)
|
||||
if (db.prepare('SELECT COUNT(*) AS c FROM taxonomy WHERE deleted_at IS NULL').get().c === 0) {
|
||||
const ins2 = db.prepare('INSERT INTO taxonomy (kind, value, color, sort, is_builtin, uuid, hlc, origin) VALUES (?,?,?,?,1,?,?,?)');
|
||||
for (const [uuidKey, kind, value, color, sort] of [
|
||||
['级别:国际级', 'level', '国际级', '#7c3aed', 10],
|
||||
['级别:国家级', 'level', '国家级', '#dc2626', 20],
|
||||
['级别:省市级', 'level', '省市级', '#ea580c', 30],
|
||||
['级别:区县级', 'level', '区县级', '#2563eb', 40],
|
||||
['级别:校级', 'level', '校级', '#16a34a', 50],
|
||||
['级别:院级', 'level', '院级', '#0891b2', 60],
|
||||
['角色:主导', 'role', '主导', '#3056d3', 10],
|
||||
['角色:核心', 'role', '核心', '#0891b2', 20],
|
||||
['角色:参加', 'role', '参加', '#64748b', 30]
|
||||
]) ins2.run(kind, value, color, sort, BUILTIN_UUID[uuidKey], '0:0:builtin', 'builtin');
|
||||
}
|
||||
|
||||
seed('dimension', ['name', 'color', 'sort', 'uuid', 'hlc', 'origin'], [
|
||||
['维度:学业深造', '学业深造', '#3056d3', 10],
|
||||
['维度:职业发展', '职业发展', '#0891b2', 20],
|
||||
['维度:综合素养', '综合素养', '#16a34a', 30],
|
||||
['维度:兴趣生活', '兴趣生活', '#db2777', 40]
|
||||
]);
|
||||
|
||||
seed('grade_band', ['label', 'color', 'min', 'max', 'sort', 'uuid', 'hlc', 'origin'], [
|
||||
['梯度:优秀', '优秀', '#16803c', 90, 100, 10],
|
||||
['梯度:良好', '良好', '#0891b2', 80, 90, 20],
|
||||
['梯度:中等', '中等', '#d97706', 70, 80, 30],
|
||||
['梯度:及格', '及格', '#ea580c', 60, 70, 40],
|
||||
['梯度:不及格', '不及格', '#dc2626', 0, 60, 50]
|
||||
]);
|
||||
|
||||
seed('metric_def', ['key', 'label', 'unit', 'agg', 'section', 'uuid', 'hlc', 'origin'], [
|
||||
['指标:volunteer_hours', 'volunteer_hours', '志愿时长', '小时', 'sum', '志愿服务']
|
||||
]);
|
||||
}
|
||||
|
||||
function openDb(dbPath) {
|
||||
fs.mkdirSync(path.join(path.dirname(dbPath), 'attachments'), { recursive: true });
|
||||
const d = new DatabaseSync(dbPath);
|
||||
d.exec('PRAGMA foreign_keys = ON;');
|
||||
// 先补列(老库无 uuid/hlc 等列时,schema 中的索引会引用失败),再执行 schema,最后回填
|
||||
preMigrateColumns(d);
|
||||
d.exec(fs.readFileSync(path.join(__dirname, '..', 'schema.sql'), 'utf8'));
|
||||
migrateSync(d);
|
||||
ensureBuiltin(d);
|
||||
alignBuiltinUuids(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
+96
-60
@@ -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"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user