""" database.py 数据库模块:负责 SQLite 连接、表初始化和基础 CRUD 操作。 使用 Python 内置 sqlite3,无需额外 ORM,保持轻量。 """ import sqlite3 import os from contextlib import contextmanager from datetime import datetime # 数据库文件路径(放在当前目录下,轻量本地运行) DB_PATH = os.path.join(os.path.dirname(__file__), "app.db") # 上传文件根目录(与 uploads 子目录共用) UPLOAD_DIR = os.path.join(os.path.dirname(__file__), "uploads") # 固件公共目录(Next.js public 下,已有实际固件文件) FIRMWARE_PUBLIC_DIR = os.path.join(os.path.dirname(__file__), "..", "public", "uploads", "GD", "firmware") FIRMWARE_PUBLIC_DIR = os.path.normpath(FIRMWARE_PUBLIC_DIR) # 固件类型到子目录映射 FIRMWARE_TYPE_FOLDER = { "采集板": "CJB", "发射板": "FSB", "主协板": "XCL", "主机服务": "", } def get_db_connection() -> sqlite3.Connection: """ 创建并返回一个 SQLite 连接对象。 设置 row_factory 为 sqlite3.Row,使查询结果可以通过列名访问。 """ conn = sqlite3.connect(DB_PATH, check_same_thread=False) conn.row_factory = sqlite3.Row conn.execute("PRAGMA foreign_keys = ON") return conn @contextmanager def get_db(): """ 上下文管理器,用于在 API 接口中安全获取和释放数据库连接。 用法: with get_db() as db: db.execute(...) """ conn = get_db_connection() try: yield conn finally: conn.close() def init_db(): """ 初始化数据库:创建所有表(如果不存在)。 """ # 确保上传目录存在 os.makedirs(UPLOAD_DIR, exist_ok=True) os.makedirs(os.path.join(UPLOAD_DIR, "calibration"), exist_ok=True) os.makedirs(os.path.join(UPLOAD_DIR, "apps"), exist_ok=True) os.makedirs(os.path.join(UPLOAD_DIR, "firmware"), exist_ok=True) with get_db() as db: # 1. 设备表(统一前后端结构) db.execute(""" CREATE TABLE IF NOT EXISTS devices ( id INTEGER PRIMARY KEY AUTOINCREMENT, sn TEXT NOT NULL UNIQUE, model TEXT, type TEXT, status TEXT NOT NULL DEFAULT '待激活', firmware TEXT DEFAULT '', production_date INTEGER, customer TEXT DEFAULT '-', batch TEXT DEFAULT '', activated_at INTEGER, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 2. 校准文件表 db.execute(""" CREATE TABLE IF NOT EXISTS calibration_files ( id INTEGER PRIMARY KEY AUTOINCREMENT, material_sn TEXT NOT NULL, sn TEXT, uid TEXT, file_name TEXT, file_path TEXT, file_size INTEGER, md5 TEXT, result TEXT, operator TEXT, remark TEXT, upload_time INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), channels_count INTEGER DEFAULT 0 ) """) # 3. 校准通道数据表 db.execute(""" CREATE TABLE IF NOT EXISTS calibration_channels ( id INTEGER PRIMARY KEY AUTOINCREMENT, calibration_id INTEGER NOT NULL, channel_id INTEGER, factor_80v REAL, offset_80v REAL, factor_2_5v REAL, offset_2_5v REAL, FOREIGN KEY (calibration_id) REFERENCES calibration_files(id) ON DELETE CASCADE ) """) # 4. APP 版本表 db.execute(""" CREATE TABLE IF NOT EXISTS app_versions ( id INTEGER PRIMARY KEY AUTOINCREMENT, app_name TEXT NOT NULL, package_name TEXT, platform_type INTEGER DEFAULT 2, version_name TEXT NOT NULL, major_version INTEGER DEFAULT 0, minor_version INTEGER DEFAULT 0, patch_version INTEGER DEFAULT 0, file_name TEXT, file_path TEXT, file_size INTEGER DEFAULT 0, file_type TEXT, distribution_type TEXT DEFAULT 'direct', primary_url TEXT, os_min_version TEXT, is_force_update INTEGER DEFAULT 0, changelog TEXT, status INTEGER DEFAULT 1, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 5. 配置文件表 db.execute(""" CREATE TABLE IF NOT EXISTS config_files ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, model TEXT NOT NULL, version TEXT NOT NULL DEFAULT 'v1.0', status TEXT NOT NULL DEFAULT '生效', max_tx_voltage TEXT, max_tx_current TEXT, tx_waveform TEXT, tx_pulse_width TEXT, acq_channels TEXT, acq_sample_rate TEXT, acq_voltage_range TEXT, full_waveform_capture TEXT, ssid_prefix TEXT, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 6. 固件版本表 db.execute(""" CREATE TABLE IF NOT EXISTS firmware_versions ( id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT NOT NULL, firmware_type TEXT NOT NULL, board_model TEXT, device_model TEXT, file_name TEXT, file_path TEXT, file_size INTEGER DEFAULT 0, hw_range TEXT, upgrade_type TEXT DEFAULT '可选', signed INTEGER DEFAULT 0, notes TEXT, status TEXT DEFAULT '已发布', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 7. 设备型号表 db.execute(""" CREATE TABLE IF NOT EXISTS device_models ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, code TEXT NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT '在产', description TEXT DEFAULT '', create_date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 8. 装配Checklist模板表 db.execute(""" CREATE TABLE IF NOT EXISTS checklist_templates ( id INTEGER PRIMARY KEY AUTOINCREMENT, model_code TEXT NOT NULL, name TEXT NOT NULL, required INTEGER NOT NULL DEFAULT 1, standard TEXT, sort_order INTEGER NOT NULL DEFAULT 0 ) """) # 9. 板卡类型表 db.execute(""" CREATE TABLE IF NOT EXISTS board_types ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT NOT NULL, device_models TEXT NOT NULL DEFAULT '[]', description TEXT DEFAULT '', status TEXT NOT NULL DEFAULT '启用' ) """) # 10. 板卡版本表 db.execute(""" CREATE TABLE IF NOT EXISTS board_versions ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, version TEXT NOT NULL, status TEXT NOT NULL DEFAULT '在产' ) """) # 11. 物料实例表 db.execute(""" CREATE TABLE IF NOT EXISTS materials ( id INTEGER PRIMARY KEY AUTOINCREMENT, sn TEXT NOT NULL UNIQUE, name TEXT NOT NULL DEFAULT '', category TEXT NOT NULL DEFAULT '', type TEXT NOT NULL, device_model TEXT NOT NULL DEFAULT '', version TEXT NOT NULL, description TEXT DEFAULT '', firmware TEXT DEFAULT '-', status TEXT NOT NULL DEFAULT '在库', device_sn TEXT DEFAULT '-', production_date INTEGER, calib_status TEXT DEFAULT '-', calib_date INTEGER DEFAULT 0 ) """) # 12. BOM模板表 db.execute(""" CREATE TABLE IF NOT EXISTS bom_templates ( id INTEGER PRIMARY KEY AUTOINCREMENT, model_code TEXT NOT NULL, name TEXT NOT NULL, material_name TEXT NOT NULL DEFAULT '', model TEXT NOT NULL, versions TEXT NOT NULL DEFAULT '[]', qty INTEGER NOT NULL DEFAULT 1, required INTEGER NOT NULL DEFAULT 1, need_calibration INTEGER NOT NULL DEFAULT 0, enforce_version_match INTEGER NOT NULL DEFAULT 0 ) """) # 13. 授权表 db.execute(""" CREATE TABLE IF NOT EXISTS licenses ( id INTEGER PRIMARY KEY AUTOINCREMENT, model TEXT NOT NULL, modules TEXT NOT NULL, expiry TEXT DEFAULT '', status TEXT NOT NULL DEFAULT '生效', config_id INTEGER DEFAULT NULL, license_file TEXT DEFAULT '', device_sn TEXT DEFAULT '', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), FOREIGN KEY (config_id) REFERENCES config_files(id) ON DELETE SET NULL ) """) # 14. 维修工单表 db.execute(""" CREATE TABLE IF NOT EXISTS repair_orders ( id TEXT PRIMARY KEY, sn TEXT NOT NULL, fault_type TEXT NOT NULL, status TEXT NOT NULL DEFAULT '待处理', priority TEXT NOT NULL DEFAULT '中', assignee TEXT DEFAULT '', create_date INTEGER NOT NULL, description TEXT DEFAULT '' ) """) # 15. 报废记录表 db.execute(""" CREATE TABLE IF NOT EXISTS scrap_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, sn TEXT NOT NULL, model TEXT NOT NULL, reason TEXT NOT NULL, applicant TEXT DEFAULT '', status TEXT NOT NULL DEFAULT '待审批', order_id TEXT DEFAULT '', date INTEGER NOT NULL, value INTEGER DEFAULT 0, materials TEXT DEFAULT '[]' ) """) # 16. 物料分类表 db.execute(""" CREATE TABLE IF NOT EXISTS material_categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, description TEXT DEFAULT '', has_firmware INTEGER NOT NULL DEFAULT 0, has_calibration INTEGER NOT NULL DEFAULT 0, sort_order INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT '启用' ) """) # 17. 设备装机BOM记录表 db.execute(""" CREATE TABLE IF NOT EXISTS device_bom_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_sn TEXT NOT NULL, name TEXT NOT NULL, material_sn TEXT DEFAULT '', model TEXT DEFAULT '', version TEXT DEFAULT '', calibration TEXT DEFAULT '无需校准' ) """) # 18. 设备操作日志表 db.execute(""" CREATE TABLE IF NOT EXISTS device_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_sn TEXT NOT NULL, action TEXT NOT NULL, operator TEXT DEFAULT '', detail TEXT DEFAULT '', date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 19. 更新日志表 db.execute(""" CREATE TABLE IF NOT EXISTS update_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT DEFAULT '', category TEXT NOT NULL DEFAULT 'feature', version TEXT DEFAULT '', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 20. 授权下载日志表 db.execute(""" CREATE TABLE IF NOT EXISTS license_download_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, license_id INTEGER NOT NULL, device_sn TEXT NOT NULL, download_time INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), ip_address TEXT DEFAULT '', app_version TEXT DEFAULT '' ) """) # 21. 授权模板表 db.execute(""" CREATE TABLE IF NOT EXISTS license_templates ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, model_code TEXT NOT NULL, auth_items TEXT NOT NULL DEFAULT '[]', config_id INTEGER DEFAULT NULL, status TEXT NOT NULL DEFAULT '启用', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 22. 授权项定义表(全局授权模块字典) db.execute(""" CREATE TABLE IF NOT EXISTS auth_items ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT DEFAULT '', category TEXT NOT NULL, sort_order INTEGER DEFAULT 0, status TEXT NOT NULL DEFAULT '启用' ) """) # 23. 设备装配检查记录表 db.execute(""" CREATE TABLE IF NOT EXISTS device_checklist_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_sn TEXT NOT NULL, checklist_name TEXT NOT NULL, passed INTEGER DEFAULT 0, photos TEXT DEFAULT '[]', note TEXT DEFAULT '', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 24. 维修处理记录表 db.execute(""" CREATE TABLE IF NOT EXISTS repair_process_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT NOT NULL, action TEXT NOT NULL, operator TEXT DEFAULT '', date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), note TEXT DEFAULT '' ) """) # 25. 维修板卡更换记录表 db.execute(""" CREATE TABLE IF NOT EXISTS repair_board_replacements ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT NOT NULL, type TEXT NOT NULL, model TEXT DEFAULT '', old_sn TEXT DEFAULT '', new_sn TEXT DEFAULT '', date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 27. 通用字典表(枚举值、选项配置) db.execute(""" CREATE TABLE IF NOT EXISTS reference_values ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, code TEXT NOT NULL, label TEXT NOT NULL, sort_order INTEGER DEFAULT 0, status TEXT NOT NULL DEFAULT '启用', description TEXT DEFAULT '', extra TEXT DEFAULT '{}', UNIQUE(category, code) ) """) # 创建索引优化查询性能 indexes = [ ("idx_devices_status", "devices", "status"), ("idx_devices_model", "devices", "model"), ("idx_calibration_material_sn", "calibration_files", "material_sn"), ("idx_calibration_sn", "calibration_files", "sn"), ("idx_calibration_uid", "calibration_files", "uid"), ("idx_app_name_platform", "app_versions", "app_name, platform_type"), ("idx_app_versions_status", "app_versions", "status"), ("idx_config_model", "config_files", "model"), ("idx_config_status", "config_files", "status"), ("idx_firmware_type_model", "firmware_versions", "firmware_type, device_model, board_model"), ("idx_firmware_status", "firmware_versions", "status"), ("idx_licenses_device_sn", "licenses", "device_sn"), ("idx_licenses_config_id", "licenses", "config_id"), ("idx_materials_device_sn", "materials", "device_sn"), ("idx_materials_category", "materials", "category"), ("idx_repair_status", "repair_orders", "status"), ("idx_scrap_status", "scrap_records", "status"), ("idx_device_bom_sn", "device_bom_records", "device_sn"), ("idx_bom_model", "bom_templates", "model_code"), ("idx_checklist_model", "checklist_templates", "model_code"), ("idx_reference_category", "reference_values", "category, sort_order"), ] for idx_name, table, columns in indexes: db.execute(f"CREATE INDEX IF NOT EXISTS {idx_name} ON {table}({columns})") db.commit() print(f"[DB] 数据库已初始化: {DB_PATH}") def _column_exists(db: sqlite3.Connection, table: str, column: str) -> bool: """检查表中是否已存在指定列""" rows = db.execute(f"PRAGMA table_info({table})").fetchall() return any(r["name"] == column for r in rows) def migrate_db(): """ 增量迁移:为已存在的表添加新列和索引(不删除数据)。 将现有 TEXT 格式的时间数据迁移为 INTEGER Unix 时间戳。 """ with get_db() as db: # 1. config_files 表列迁移 new_columns = [ ("max_tx_voltage", "TEXT"), ("max_tx_current", "TEXT"), ("tx_waveform", "TEXT"), ("tx_pulse_width", "TEXT"), ("acq_channels", "TEXT"), ("acq_sample_rate", "TEXT"), ("acq_voltage_range", "TEXT"), ("full_waveform_capture", "TEXT"), ("ssid_prefix", "TEXT"), ] for col, ctype in new_columns: if not _column_exists(db, "config_files", col): try: db.execute(f"ALTER TABLE config_files ADD COLUMN {col} {ctype}") print(f"[DB MIGRATE] config_files 新增列: {col}") except Exception as e: print(f"[DB MIGRATE] 添加列 {col} 失败: {e}") # 2. 创建索引(对已存在的数据库补充索引) indexes = [ ("idx_devices_status", "devices", "status"), ("idx_devices_model", "devices", "model"), ("idx_calibration_material_sn", "calibration_files", "material_sn"), ("idx_calibration_sn", "calibration_files", "sn"), ("idx_calibration_uid", "calibration_files", "uid"), ("idx_app_name_platform", "app_versions", "app_name, platform_type"), ("idx_app_versions_status", "app_versions", "status"), ("idx_config_model", "config_files", "model"), ("idx_config_status", "config_files", "status"), ("idx_firmware_type_model", "firmware_versions", "firmware_type, device_model, board_model"), ("idx_firmware_status", "firmware_versions", "status"), ("idx_licenses_device_sn", "licenses", "device_sn"), ("idx_licenses_config_id", "licenses", "config_id"), ("idx_materials_device_sn", "materials", "device_sn"), ("idx_materials_category", "materials", "category"), ("idx_repair_status", "repair_orders", "status"), ("idx_scrap_status", "scrap_records", "status"), ("idx_device_bom_sn", "device_bom_records", "device_sn"), ("idx_bom_model", "bom_templates", "model_code"), ("idx_checklist_model", "checklist_templates", "model_code"), ] for idx_name, table, columns in indexes: try: db.execute(f"CREATE INDEX IF NOT EXISTS {idx_name} ON {table}({columns})") except Exception as e: print(f"[DB MIGRATE] 创建索引 {idx_name} 失败: {e}") # 3. 授权项定义表(兼容已有数据库) db.execute(""" CREATE TABLE IF NOT EXISTS auth_items ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT DEFAULT '', category TEXT NOT NULL, sort_order INTEGER DEFAULT 0, status TEXT NOT NULL DEFAULT '启用' ) """) # 4. 设备装配检查记录表(兼容已有数据库) db.execute(""" CREATE TABLE IF NOT EXISTS device_checklist_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_sn TEXT NOT NULL, checklist_name TEXT NOT NULL, passed INTEGER DEFAULT 0, photos TEXT DEFAULT '[]', note TEXT DEFAULT '', created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 5. 维修处理记录表(兼容已有数据库) db.execute(""" CREATE TABLE IF NOT EXISTS repair_process_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT NOT NULL, action TEXT NOT NULL, operator TEXT DEFAULT '', date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), note TEXT DEFAULT '' ) """) # 6. 维修板卡更换记录表(兼容已有数据库) db.execute(""" CREATE TABLE IF NOT EXISTS repair_board_replacements ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT NOT NULL, type TEXT NOT NULL, model TEXT DEFAULT '', old_sn TEXT DEFAULT '', new_sn TEXT DEFAULT '', date INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ) """) # 7. 通用字典表(兼容已有数据库) db.execute(""" CREATE TABLE IF NOT EXISTS reference_values ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, code TEXT NOT NULL, label TEXT NOT NULL, sort_order INTEGER DEFAULT 0, status TEXT NOT NULL DEFAULT '启用', description TEXT DEFAULT '', extra TEXT DEFAULT '{}', UNIQUE(category, code) ) """) # 8. 将现有 TEXT 时间数据迁移为 INTEGER 时间戳(SQLite 动态类型,无需重建表) timestamp_tables = { "devices": ["created_at", "activated_at", "production_date"], "calibration_files": ["upload_time"], "app_versions": ["created_at", "updated_at"], "config_files": ["created_at", "updated_at"], "firmware_versions": ["created_at", "updated_at"], "device_models": ["create_date"], "licenses": ["created_at", "updated_at"], "repair_orders": ["create_date"], "scrap_records": ["date"], "device_logs": ["date"], "update_logs": ["created_at"], "license_download_logs": ["download_time"], "license_templates": ["created_at"], "device_checklist_records": ["created_at", "updated_at"], "repair_process_records": ["date"], "repair_board_replacements": ["date"], "materials": ["production_date", "calib_date"], } for tbl, cols in timestamp_tables.items(): for col in cols: try: db.execute(f""" UPDATE {tbl} SET {col} = CAST(strftime('%s', COALESCE({col}, '1970-01-01 00:00:00')) AS INTEGER) WHERE {col} IS NOT NULL AND typeof({col}) = 'text' AND {col} != '' """) if db.total_changes > 0: print(f"[DB MIGRATE] {tbl}.{col} 已迁移为时间戳") except Exception as e: print(f"[DB MIGRATE] {tbl}.{col} 迁移失败或无需迁移: {e}") # 9. 数据库迁移:为 checklist_templates 表添加 standard 字段 if not _column_exists(db, "checklist_templates", "standard"): try: db.execute("ALTER TABLE checklist_templates ADD COLUMN standard TEXT") print("[DB] 已为 checklist_templates 表添加 standard 字段") except Exception as e: print(f"[DB] 添加 standard 字段失败: {e}") db.commit() print("[DB MIGRATE] 数据库迁移完成") def seed_demo_data(): """ 插入演示数据,方便新手直接体验接口。 如果已存在相同 SN,则忽略(INSERT OR IGNORE)。 """ # 将演示日期转换为时间戳 demo_date_ts = int(datetime.strptime("2026-05-07", "%Y-%m-%d").timestamp()) demo_devices = [ ("GD30-20260507-001", "GD30-2024", "标准型", "待激活", "", demo_date_ts, "华东地质局", "B2026-05"), ("GD30-20260507-002", "GD30-2024", "标准型", "待激活", "", demo_date_ts, "华北勘探院", "B2026-05"), ("MT-20260507-003", "MT-2024", "标准型", "已禁用", "", demo_date_ts, "-", "B2026-05"), ] with get_db() as db: for sn, model, type_, status, firmware, production_date, customer, batch in demo_devices: db.execute( """ INSERT OR IGNORE INTO devices (sn, model, type, status, firmware, production_date, customer, batch) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """, (sn, model, type_, status, firmware, production_date, customer, batch) ) # 授权项定义种子数据 demo_auth_items = [ ('1D', '一维自电/电阻率/激电测试模块', '包含一维自然电位法、电阻率测深、激发极化测深', '一维', 1), ('2D', '二维自电/电阻率/激电测试模块', '包含二维自然电位法、电阻率成像、激发极化成像', '二维', 2), ('3D', '三维自电/电阻率/激电测试模块', '包含三维自然电位法、电阻率成像、激发极化成像', '三维', 3), ('WATER', '水上', '水上电法探测', '水上', 4), ('CROSS', '跨孔', '跨孔电阻率成像', '跨孔', 5), ('CF', '电流场法', '电流场法', '电流场法', 6), ] for id_, name, desc, cat, sort in demo_auth_items: db.execute( "INSERT OR IGNORE INTO auth_items (id, name, description, category, sort_order) VALUES (?, ?, ?, ?, ?)", (id_, name, desc, cat, sort) ) # 固件分类种子数据(确保4种固件类型分类存在) demo_categories = [ ("采集板", "数据采集板固件", 1, 0, 1), ("发射板", "信号发射板固件", 1, 0, 2), ("主协板", "主控协处理板固件", 1, 0, 3), ("主机服务", "主机服务升级包", 1, 0, 4), ] for name, desc, has_fw, has_cal, sort in demo_categories: db.execute( """ INSERT OR IGNORE INTO material_categories (name, description, has_firmware, has_calibration, sort_order, status) VALUES (?, ?, ?, ?, ?, '启用') """, (name, desc, has_fw, has_cal, sort) ) # 通用字典种子数据 demo_refs = [ # 设备状态 ("device_status", "待激活", "待激活", 1), ("device_status", "装配中", "装配中", 2), ("device_status", "测试通过", "测试通过", 3), ("device_status", "测试不通过", "测试不通过", 4), ("device_status", "已出厂", "已出厂", 5), ("device_status", "已激活", "已激活", 6), ("device_status", "已禁用", "已禁用", 7), # 型号状态 ("model_status", "在产", "在产", 1), ("model_status", "停产", "停产", 2), # 物料状态 ("material_status", "在库", "在库", 1), ("material_status", "已装配", "已装配", 2), ("material_status", "故障", "故障", 3), ("material_status", "报废", "报废", 4), # 校准状态 ("calibration_status", "待校准", "待校准", 1), ("calibration_status", "合格", "合格", 2), ("calibration_status", "不合格", "不合格", 3), ("calibration_status", "无需校准", "无需校准", 4), # 维修状态 ("repair_status", "待处理", "待处理", 1), ("repair_status", "处理中", "处理中", 2), ("repair_status", "已处理", "已处理", 3), # 维修优先级 ("repair_priority", "高", "高", 1), ("repair_priority", "中", "中", 2), ("repair_priority", "低", "低", 3), # 维修故障类型 ("repair_fault_type", "板卡故障", "板卡故障", 1), ("repair_fault_type", "固件异常", "固件异常", 2), ("repair_fault_type", "通信故障", "通信故障", 3), ("repair_fault_type", "电源故障", "电源故障", 4), ("repair_fault_type", "传感器故障", "传感器故障", 5), ("repair_fault_type", "其他", "其他", 6), # 维修处理动作 ("repair_action", "更换板卡", "更换板卡", 1), ("repair_action", "固件修复", "固件修复", 2), ("repair_action", "参数重置", "参数重置", 3), ("repair_action", "其他处理", "其他处理", 4), # 报废状态 ("scrap_status", "待审批", "待审批", 1), ("scrap_status", "审批中", "审批中", 2), ("scrap_status", "已审批", "已审批", 3), ("scrap_status", "已驳回", "已驳回", 4), ("scrap_status", "回收中", "回收中", 5), ("scrap_status", "已回收", "已回收", 6), # 报废流程步骤 ("scrap_step", "申请报废", "申请报废", 1), ("scrap_step", "主管审批", "主管审批", 2), ("scrap_step", "物料检测", "物料检测", 3), ("scrap_step", "回收入库", "回收入库", 4), ("scrap_step", "报废完成", "报废完成", 5), # 固件升级类型 ("firmware_upgrade_type", "可选", "可选", 1), ("firmware_upgrade_type", "强制", "强制", 2), # 注册测试状态 ("registration_test_status", "装配中", "装配中", 1), ("registration_test_status", "测试通过", "测试通过", 2), ("registration_test_status", "测试不通过", "测试不通过", 3), # 配置文件电压选项 ("config_voltage", "500V", "500V", 1), ("config_voltage", "800V", "800V", 2), ("config_voltage", "1000V", "1000V", 3), ("config_voltage", "1200V", "1200V", 4), ("config_voltage", "1500V", "1500V", 5), # 配置文件电流选项 ("config_current", "2A", "2A", 1), ("config_current", "5A", "5A", 2), ("config_current", "8A", "8A", 3), ("config_current", "10A", "10A", 4), ("config_current", "15A", "15A", 5), # 配置文件波形选项 ("config_waveform", "0+0-", "0+0-", 1), ("config_waveform", "+0-0", "+0-0", 2), ("config_waveform", "+-", "+-", 3), # 配置文件脉冲宽度选项 ("config_pulse_width", "0.25s/0.5s/1s/2s/4s/8s", "0.25s/0.5s/1s/2s/4s/8s", 1), ("config_pulse_width", "0.25s/0.5s/1s/2s/4s/8s/16s/32s/64s", "0.25s/0.5s/1s/2s/4s/8s/16s/32s/64s", 2), # 配置文件通道数选项 ("config_channels", "1", "1", 1), ("config_channels", "6", "6", 2), ("config_channels", "12", "12", 3), # 配置文件采样率选项 ("config_sample_rate", "50Hz/60Hz", "50Hz/60Hz", 1), ("config_sample_rate", "50Hz/60Hz/100Hz/1000Hz", "50Hz/60Hz/100Hz/1000Hz", 2), # 配置文件电压范围选项 ("config_voltage_range", "±2.5V", "±2.5V", 1), ("config_voltage_range", "±2.5V/±80V", "±2.5V/±80V", 2), ("config_voltage_range", "±80V/±600V", "±80V/±600V", 3), # APP平台类型 ("app_platform", "1", "iOS", 1), ("app_platform", "2", "Android", 2), ("app_platform", "3", "HarmonyOS", 3), ("app_platform", "4", "Windows", 4), ("app_platform", "5", "macOS", 5), ("app_platform", "6", "Linux", 6), ("app_platform", "7", "Web", 7), ] for cat, code, label, sort in demo_refs: db.execute( "INSERT OR IGNORE INTO reference_values (category, code, label, sort_order) VALUES (?, ?, ?, ?)", (cat, code, label, sort) ) db.commit() print("[DB] 演示数据已插入") def scan_firmware_directory(): """ 扫描 FIRMWARE_PUBLIC_DIR 目录,自动发现固件文件。 返回列表,每项包含:type, folder, file_name, file_path, file_size, version """ results = [] base_dir = FIRMWARE_PUBLIC_DIR if not os.path.isdir(base_dir): return results type_map = { "CJB": "采集板", "FSB": "发射板", "XCL": "主协板", } # 扫描子目录 for folder, fw_type in type_map.items(): folder_path = os.path.join(base_dir, folder) if not os.path.isdir(folder_path): continue for fname in os.listdir(folder_path): fpath = os.path.join(folder_path, fname) if not os.path.isfile(fpath): continue size = os.path.getsize(fpath) # 尝试从文件名解析版本,如 CJB_2_8(Checksum=0xc5b1).bin -> 2.8 version = "-" if "_" in fname: parts = fname.split("_") if len(parts) >= 3: try: major = parts[1] minor = parts[2].split("(")[0].split(".")[0] version = f"{major}.{minor}" except Exception: pass results.append({ "type": fw_type, "folder": folder, "file_name": fname, "file_path": fpath, "file_size": size, "version": version, }) # 扫描根目录(主机服务包) for fname in os.listdir(base_dir): fpath = os.path.join(base_dir, fname) if not os.path.isfile(fpath): continue if fname.startswith("package_arm") and fname.endswith(".tar.gz"): size = os.path.getsize(fpath) # 尝试从文件名解析版本,如 package_arm_20260507.tar.gz -> 2026.05.07 version = "-" if fname.startswith("package_arm_"): ver_part = fname[len("package_arm_"):].replace(".tar.gz", "") if len(ver_part) == 8 and ver_part.isdigit(): version = f"{ver_part[:4]}.{ver_part[4:6]}.{ver_part[6:8]}" else: version = ver_part results.append({ "type": "主机服务", "folder": "", "file_name": fname, "file_path": fpath, "file_size": size, "version": version, }) return results