# excelToDataBase-code **Repository Path**: sen_shan/excel-to-data-base-code ## Basic Information - **Project Name**: excelToDataBase-code - **Description**: 可视化维护「导入配置 + 字段映射 + 数据库连接」,一键或按排程把 Excel 自动导入 MSSQL / MySQL / Oracle 1读取 Excel按目录 + 文件名通配符指定工作表 2结构预检表不存在则自动建表; 3启用控制表时先整份写入并提交 4写业务表每 N 笔提交一次,失败单独记录 5回写控制表「成功/失败」栏位 6落日志看板 + 运行日志文件全程可追溯 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-28 - **Last Updated**: 2026-09-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # excelToDataBase —— Excel 自动导入数据库 Web 管理系统 可视化维护 Excel 导入配置、字段映射与数据库连接信息,一键或按计划(cron)将 Excel 自动导入 **MSSQL / MySQL / Oracle**。系统自身配置统一存储在 **SQLite**(所有自建表以 `sys_` 开头)。 ## 功能 - **F1 设定入口与权限**:点击「设定」进入系统——**注册/登录入口按用户表是否为空动态控制**:用户表为空(系统首次使用)时仅展示「注册」入口、登录隐藏,**第一个注册用户自动成为系统初始用户(超管)**;用户表非空(已初始化)后仅展示「登录」入口、注册隐藏或禁用(注册接口亦拒绝)。注册/修改密码时密码**加盐哈希存储**(兼容旧明文并自动迁移);登录需校验密码;已登录用户可在导航「修改密码」页修改自己密码;仅超级用户可维护用户列表——支持**新增用户**(填写用户名+密码手动创建,超管亦可在后台增补普通用户)与**删除用户**(删除前二次确认;**超级管理员不可删除**),并可重置任意用户密码/调整角色。 - **F2 导入配置**:导入模型 CRUD(文件目录/文件名通配符/工作表/目标表/起始行/起始列/前后处理/备份路径等); **备份路径 / 错误输出路径填的是目录,处理对象是「本次实际处理的那个文件」**:导入成功移到 `<备份路径>\<批处理号>\` 下、有失败行或导入失败移到 `<错误输出路径>\<批处理号>\` 下(按批处理号分目录,不会同名冲突);配置的文件目录不会被搬走;**完全不含状态流转**——没有状态流转入口、没有独立状态流转配置,控制表成功/失败栏位与取值统一在「字段衍射」中维护;**起始行与起始列可不填写**,不填默认从第 1 行、A 列开始;**所有配置均支持行内「编辑」**,编辑校验与新建一致,关键字段(模型名称)变更需二次确认,保存后即时生效并反馈结果;**支持「复制」**——一键新增配置实例,完整继承核心字段、字段衍射(字段映射)与邮件通知配置(模型名自动去重为 `原名-副本`、`原名-副本2`)。 - **F3 字段映射**:Excel 列 → 数据库字段映射,**Excel 列名可空**(空时字段值取默认值/规则,不依赖 Excel);支持**多列名映射(别名)**:同一目标字段可配置多个 Excel 列名,格式支持数组(`[料号, item no, part number]`、`["料号","item no"]`)或分隔符(`料号,item no`),任一列名命中即取值写入该字段;**互斥约束**:同一 Excel 中同一字段的多个列名同时出现则报错、不予汇入; **栏位严格校验**:只要映射里填了 Excel 列名(含别名),文件表头里就必须能匹配到,找不到就**直接报错终止导入**(不会因填了默认值就放行,避免整列被静默填成默认值而掩盖文件换版/栏位改名),多别名命中任一即可,「Excel 列名」留空只填默认值/规则的映射不参与此校验; 判定只看「是否定义了 Excel 列名」,与写入目标无关(写业务表还是控制表都要校验;启用控制表时用**控制表栏位**取值,但只要同时定义了 Excel 列名就仍须存在);匹配时**忽略列名大小写与首尾空格**,提升兼容性;支持「**写入目标表**」选择(目标表 / 控制表,根据导入配置的目标表名与控制表名称下拉选择,列表与编辑页均明示是目标表还是控制表); **成功 / 失败 栏位**:字段衍射中新增 **成功**、**失败** 两个栏位——「数据库字段」填控制表上的栏位名(如 `成功` / `失败`),取值填该栏位要写入的值(如 `300` / `200`,失败可用 `default-error-message` 记录真实错误);导入时业务表处理成功回写**成功**栏位、失败回写**失败**栏位; **数据库字段自动带出**:「数据库字段 *」随「**写入目标表**」自动带出该表栏位供下拉选择——**选控制表带出控制表栏位、选目标表带出业务表栏位**(栏位名 + 类型 + 备注一并展示);**选中后「描述」默认带出数据库中该栏位的备注**(可再手工修改,不覆盖用户已手改的描述);库中无备注或读不到表时降级为手工输入,不影响保存。列表页与编辑页同此规则(`GET /import-configs/{id}/table-columns?table=<表名>` 提供栏位数据,MySQL/MSSQL/Oracle 分别读 `information_schema` / `sys.extended_properties` / `ALL_COL_COMMENTS` 的备注)。 **成功 / 失败 / 默认值 三者的取值方式统一**:**下拉优先,手工输入即取值**——下拉选中固定函数就按固定函数执行;下拉停在「(空)」或「自定义(手工输入)…」时,以输入框里实际填写的值为准(**填什么就等于该值**),**两处都不填就是空**。三个下拉默认均停在「(空)」,避免浏览器默认选中第一个固定函数而把手输值静默吞掉。 特殊值规则: - `auto-generate`:每个文档生成一个值(如批号) - `auto-generate-row`:每一行生成一个值(如行号) - `auto-datetime`:自动填入当前时间 - `default-batch-id`:业务表与控制表**批次 ID 保持一致**(业务数据继承控制数据批次) - `default-error-message`:运行错误信息(控制表失败阶段回写真实错误内容) - `default-data-count`:**业务表资料中笔数**——本行写入成功后的业务表笔数, 逐行递增(第 1 行写 1、第 2 行写 2 …); - `default-data-count-total`:**业务表资料总笔数**——整份资料成功写入业务表**总行数**; 业务表**完全结束后**把「成功」栏位统一回写为该总笔数 (写在第 3 步的「成功」回写里,不再逐行占位回填); - `default-xxx`:固定默认值 - `{Excel列名}`:引用同行其他列值;其它值视为常量直接写入 - **F3.5 控制表状态(无独立配置)**:状态流转功能已**整体取消**(原 `sys_control_flow` 表与 `/import-configs/{id}/flows` 页面/路由均已移除,导入配置页无任何状态流转入口)—— **控制表栏位全部自定义**:系统不认任何固定栏位名,**没有固定的 `status`、也没有固定的 `error_message`**; 写入与校验只针对**字段衍射(字段映射)里配置了的控制表栏位**,未配置的栏位一律不写、不改、也不校验。 - **写入中占位**:已配置「成功」或「失败」取值的控制表栏位,处理业务表前写入占位值(内置默认 `100`); 占位值可用该栏位「默认值/规则」填 `stage:100`(或 `stage:writing`,也接受任意值如 `stage:WIP`)自定义; 未配置任何成功/失败栏位的控制表**一条都不写**(也不会因缺 `status` / `error_message` 报错); - **成功**:业务表**完全结束后**才回写字段衍射配置的「成功」栏位取值;**未配置成功栏位则不修改**; - **失败**:业务表处理过程中出现失败即回写配置的「失败」栏位取值;**未配置失败栏位则不修改** (失败优先:有失败行时不再写成功栏位); - 成功/失败**两个栏位都配置**时:只写选中结果栏位的取值,另一个结果栏位写同值 (两者同值,因此「写入中」阶段两个栏位都保持空,不会出现未选中栏位被写值/被写栏位名的脏数据)。 - **F3.6 控制表与业务表处理流程规范(一个文档一笔控制表)**: **若设定了控制表**(控制表名非空): 0. **写入前结构预检**:控制表/业务表**已存在**时,**字段映射中出现过的栏位**(含「成功」「失败」等回写栏位)必须在表中存在,否则**直接报错终止**,**不自动新增字段**(详见「目标表结构约定」); 1. **先写入控制表,提交**:把本文件资料逐笔插入控制表(已配置成功/失败取值的控制表栏位 = 写入中占位,内置默认 `100`),**整份插完立即提交**,让资料在库中可见; 2. **写入业务表,每 1000 笔提交**; 3. **写入业务表完全结束后,修改控制表「成功」栏位的值**;**若未设定成功栏位,则不修改**; 4. **写入业务表时失败,修改控制表「失败」栏位的值**;**若未设定失败栏位,则不需要修改**。 > 失败优先:只要有任一行失败,该文档即按失败回写「失败」栏位,不再写「成功」栏位。 **若没有设定控制表**:**直接写入业务表**(沿用单表流程,每 1000 笔提交)。 - 控制表的**「成功」「失败」栏位及其取值全部在「字段衍射」中维护**; 控制表栏位**全部自定义**,系统不认任何固定栏位名——**没设成功栏位时不会回退去写 `status`**, 也不会因为表里没有 `status` / `error_message` 而报「缺栏位」;未设定的栏位**一律不改**。 - 一个文档对应一个控制表批次(文档标记):回写时**只命中本文件**的控制表记录, 不会误改其它文档的资料。 - **事务与批量提交**:控制表整份先写入并提交;业务表每 1000 笔提交一次, **尾批(不足 1000 笔)同样有提交点**。**分批进度日志先提交再统计「已提交笔数」**, 并同时给出业务表与控制表的已提交笔数,便于判断资料是否真的落库。 - 某一行出错时只回滚/重做该行,**不会拖累同批已成功的行**; 回写控制表命中 0 行即定位失效,直接记入错误明细(防止控制表悄悄停在「写入中」而没人发现)。 - **F3.7 批处理号(batch_id)**: - **建导入日志时即生成并落库**:看板「批处理号」从导入开始(执行中)就有值,不必等导入结束; - 全流程复用同一个号:**看板批处理号 = 业务表批次栏位值 = 运行日志文件名前缀**,可按批处理号一路追到数据和日志; - 格式:时间戳 + 随机后缀(如 `2026092811123821dace`);导入配置里用 `auto-generate` / `default-batch-id` 写入业务表、控制表的批次栏位时取的就是该值。 - **F3.8 运行日志(导入过程可追溯)**: - 每次导入都会生成**运行日志**:控制台 + `<项目>/data/logs/<批处理号>_<日志ID>_<业务表名>.log` (如 `2026092811123821dace_44_EPI_PO_CTB_CONFIRM.log`),并在返回值 `log` 字段返回全文; - 日志覆盖全链路:读取 Excel → 载入行数/列名 → 建表/结构预检 → 逐批进度(已处理行数 / 已提交笔数 / 累计成功与错误 / 耗时)→ 回填总笔数 → 导入结束汇总;异常时附带堆栈; - **耗时按易读单位展示**:不足 1 秒显示 `ms`,不足 1 分钟显示「秒」,超过 1 分钟显示「**分钟**」(如 `耗时 1.78 分钟(106969 ms)`,括号内保留原始毫秒便于核对);运行日志结束行、看板耗时列、运行日志页统一使用; - **日志里同时输出实际执行的 SQL 与绑定值**(`[SQL] INSERT INTO "业务表" ...` / `[SQL参数] ...`、回写控制表的 `UPDATE ...`),可直接照抄到数据库客户端核对「资料写到了哪张表、条件是什么、为什么查不到」;元数据探测(`PRAGMA`)与进度统计(`SELECT COUNT(*)`)不记录,避免刷屏; - **看板每条日志都有「运行日志」入口**:`/dashboard/logs/{日志ID}` 查看(执行中每 3 秒自动刷新,可实时看进度)、`/dashboard/logs/{日志ID}/file` 下载 `.log`、`/dashboard/logs` 查看全部日志文件; - 均为**匿名只读**,未登录也能查看;日志文件缺失(如进程重启)时自动回退用导入日志记录展示,保证「一定能查」; - **扫不到文件时直接结束但必记录**:目录下未匹配到源文件时,写一条「未找到待导入文件 … 本次不执行导入,直接结束」后收工——**不输出堆栈、不写错误明细、不发失败邮件、不搬文件**,日志状态记为 `skipped`(看板显示「无文件跳过」,不计入失败)。排程语义即「扫到文件就导入,扫不到就结束」。目录本身不存在仍属配置故障,按 `failed` 报「目录不存在」。 - **F4 数据库配置**:MSSQL/MySQL/Oracle 连接信息维护,**密码加密存储**,提供「测试连接」与「编辑」(密码留空不修改)。新增**描述**栏位(可选,如「采购生产库(华东)」),可在邮件通知里用 `{db_desc}` 带出。 - **F5 看板**:导入日志(时间/模型/文件/成功行数/耗时/状态,可筛选)+ **运行日志**(每次导入的过程日志,见 F3.7),错误明细可一键重试。**看板匿名可访问**:首页看板模块点击「看」弹出详情(统计 + 日志),`/dashboard`、`/dashboard/data`、错误明细均无需登录即可只读查看(写操作仍受鉴权保护)。 - **F6 排程**:可视化配置定时导入—— - **每天固定时间**(可勾选周几,多选循环,如每天 18:00 仅周三/周四); - **每月固定日/最后一天**:固定日**可多选**(如 14,15,20 每日循环)或选**每月最后一天**运行; - **每 N 秒/分/时/天**周期重复(可限制周几); - 通用可选:**启动时间点**(到点后才开始)、**可运行时间段**(如 02:00--18:00 表示仅该时段内可运行); - 另保留**高级 Cron** 模式(标准 cron 与 `every:hours:1` 周期规则);可启停、可编辑;旧「每周某几天」配置自动并入「每天固定时间+周几」。 - **F7 邮件服务器配置**:SMTP 邮件服务器维护(支持匿名发送:用户名/密码留空;SSL/TLS 与 STARTTLS 两种方式),提供「测试发送」。 - **F8 导入结果邮件通知**:导入配置二级维护邮件通知——选择邮件服务器、配置成功/失败收信人,主题与内容支持 `{占位符}`;收信人未设定(留空)时对应结果不发送邮件。 - **主旨带批次处理号**:新增 `{batch_id}`;默认主旨为 `[导入成功] {model_name_display} · 批次 {batch_id}` / `[导入失败] {model_name_display} · 批次 {batch_id}`(`{model_name}` 仍保留可用,渲染纯模型名,旧主旨配置不受影响)。 - **模型描述可选择显示**:新增 `{model_name_display}`(推荐)与 `{model_desc}`——模型「描述」有值时渲染为 `描述(模型名)`,未定义时分别退化为模型名 / 空字符串。 - **数据库描述**:新增 `{db_desc}`——取「数据库配置」里维护的**描述**,未定义时为空字符串;便于主旨里区分是哪个库出的问题(如 `[{db_desc}] {model_name_display}`)。 - **耗时按分钟**:推荐用 `{duration}`(`1.78 分钟(106969 ms)`);`{duration_ms}` 保留原始毫秒。 - **失败通知附运行日志**:自动把该批次运行日志作为附件;**超过 1 MB 自动 gzip 压缩**(附件名 `xxx.log.gz`,正文给出「原大小 → 压缩后大小」);压缩失败 / 压缩后超 8 MB / 文件不存在时只跳过附件并记录说明,**不阻断发信**。 - **F9 操作手册**:`/manual`(HTML,**匿名可访问**)——首页(hero 与看板模块)、看板页、顶部导航均有入口。含 **18 章**:…常见错误排查、启动/数据/备份、**稳定性与容错**、**资料保存天数(到期自动清理)**;左侧目录支持关键词搜索过滤与滚动高亮。 - **F11 资料保存天数(到期自动清理)**:导入配置(导入模型)新增「资料保存天数」,默认 **14 天**;该值在建日志时带入 `sys_import_log.retention_days`(**按批次固化**,改配置不追溯历史批次),`<=0` 表示永不清理。每天凌晨 03:00 与启动时各执行一次,对 `finished_at + retention_days <= now` 的批次按序清理:**① 目标库业务表数据**(按 `default-batch-id` 批处理号栏位逐表 `DELETE`)→ **② 备份/错误输出文件**(含**空批次文件夹一并删除**)→ **③ 运行日志 `.log`/`.log.gz`** → **④ 错误明细 + 导入日志记录**。任一步失败即**保留日志记录**并在消息写「资料到期清理未完成:…」,便于排查重试;单批次失败不影响其它批次。 - **F10 稳定性与容错**:任何一次导入失败、任何一个页面报错,都不影响服务本身与其它任务—— - 系统库(SQLite)开启 **WAL + 30s 等锁**:长导入期间看板仍可实时刷新(并发基准实测 **65s → 1.7s**);`init_db()` 加锁幂等,避免多线程首次访问并发建表崩溃; - **启动容错**:建表失败 / 排程规则非法都只记日志并跳过,服务照常起来;上次进程遗留的「执行中」会被回收,不会永远卡住; - **异常隔离**:排程回调与后台导入线程各自捕获异常,一次排程失败不影响后续排程;失败时兜底标记日志状态,不留僵尸「执行中」; - **全局兜底**:未捕获异常统一返回「系统开小差了」页(接口路径返回 JSON),只影响当前请求; - **并发导入不串台**:实时日志回调按线程隔离; - **邮件通知不影响导入**:发信或附件准备失败只写系统日志,导入结果照常落库; - 除按配置的「资料保存天数」到期清理外,运行日志不被动删除;可选 `EXCEL2DB_LOG_RETENTION_DAYS=30` 在启动时做全局兜底清理。 - **F12 简繁体切换(字段映射栏位)**:字段映射新增「简繁体」,可对**单个栏位**指定简繁转换方向,导入时统一转换,避免简繁混写导致库里出现「同义不同形」的重复资料(查询/比对/去重都会踩坑)。 - 方向:`s2t` 简→繁、`t2s` 繁→简、`s2tw`/`tw2s`(中国台湾正体)、`s2hk`/`hk2s`(中国香港繁体)、`s2twp`/`tw2sp`(含惯用词,如 内存→記憶體、软件→軟體)、`t2tw`/`t2hk`;`(空)` = 不转换。 - **只对文本生效**(数字/日期/空值原样不动,绝不改类型);**只在「源列取值」阶段转换**,固定式默认值/规则不受影响;不同栏位可配不同方向。 - 依赖 `opencc-python-reimplemented`,为**可选依赖**:未安装时配置仍保存但按原文导入(运行日志提示),转换异常也退回原文,**绝不影响导入主流程**。 - 转换是静默改值,运行日志会写明「简繁体切换已启用(N 个栏位):栏位(目标表)→ 方向」用于核对。 ### 目标表结构约定(同步时不自动新增字段) - **目标表不存在**:按字段映射自动建表,栏位类型统一 `VARCHAR(4000)`(兼容 MSSQL/MySQL/Oracle)。 - **目标表已存在**:**结构一律不改**。若字段映射中的栏位在表里不存在,导入**直接报错并终止**, **不会自动 `ALTER TABLE` 新增栏位**,本次不写入任何数据。 - 报错信息形如:`目标表 t_xxx 缺少字段映射中的栏位: name, city(不会自动新增栏位,请在表中补齐或修正字段映射)`; - 该错误属于**模型级错误**:日志状态为「失败」,`message` 记录缺列详情,错误明细「路径」显示为「—」; - 控制表流程同样适用:**字段衍射里配置了的**控制表栏位(含「成功」「失败」等回写栏位)缺列时,在写数据前即报错。 - **控制表栏位全部自定义**:只校验字段衍射里配置过的栏位,**不要求表里有 `status` / `error_message`**—— 自定义栏位(如 `写入状态` / `结果状态`)可正常工作。 - 处理方式:先在目标表补齐栏位,或在「字段衍射」中修正栏位名/写入目标表。 ## 安装 ```bash pip install excelToDataBase # 或源码安装 pip install -e . ``` 依赖:Python 3.11+、pandas、openpyxl、pyxlsb、sqlalchemy、fastapi、uvicorn、pymysql、oracledb、pymssql、cryptography、apscheduler。邮件发送基于标准库 `smtplib`,无额外依赖。简繁体切换为**可选依赖** `opencc-python-reimplemented`(未安装时该功能自动降级为「按原文导入」,不影响其它功能)。 支持 **.xlsx / .xlsm / .xlsb** 等格式(`.xlsb` 自动使用 pyxlsb 引擎读取);配置的工作表不存在时自动读取第一个 sheet。 ## 使用 ### 启动 Web 服务 ```bash excel-to-db serve # 或(推荐:读 config.ini,端口占用自动顺延,自动开浏览器) python -m excelToDataBase.launcher # 或直接指定参数 python -m uvicorn excelToDataBase.web:app --host 0.0.0.0 --port 8000 ``` 打开 http://localhost:8000 ,点击「设定」: 1. **首次使用**:用户表为空,页面仅展示「注册」入口,注册的用户名即系统初始用户(**自动成为超级管理员**); 2. **再次进入**:用户表已有用户,页面仅展示「登录」入口(注册已隐藏),登录后进入维护界面;若需增加其他使用者,由超管在「用户管理」中「新增用户」维护用户名与密码; 3. 在「数据库」新增目标数据库配置并「测试连接」; 4. 在「导入配置」新增导入模型,并为其配置「字段映射」; 5. 在「导入配置」列表点「手动导入」,或在「排程」配置 cron 定时导入; 6. 在「看板」查看日志与错误明细,失败记录可「重试」; 7. 如需导入结果邮件通知: - 在「邮件」维护邮件服务器(SMTP 主机/端口/认证,用户名密码留空即匿名发送,可「测试发送」); - 在「导入配置」列表点击某配置的「邮件通知」进行二级维护:选择邮件服务器、填写成功/失败收信人与主题/内容(支持占位符); - 收信人未设定(留空)时,对应结果(成功/失败)不发送邮件。 ### CLI ```bash excel-to-db --help excel-to-db test-connection --db-type mysql --host 127.0.0.1 --port 3306 --username root --database mydb ``` ## 打包为 Windows exe(免安装) 给操作者一个双击即用的目录,**改端口只需用记事本编辑 `config.ini`**。 ### 构建 ```bash .venv\Scripts\python.exe -m pip install pyinstaller .venv\Scripts\python.exe build_exe.py # 预检查 + 打包 + 产物自检 .venv\Scripts\python.exe build_exe.py --clean # 先清理 build/ dist/ 再打包 .venv\Scripts\python.exe build_exe.py --skip-build # 只检查现有产物,不重新打包 python -m PyInstaller build_exe.spec --noconfirm # 绕开封装脚本,直接调 PyInstaller ``` 产物: ``` dist/excelToDataBase/ ├── excelToDataBase.exe ← 双击运行 ├── config.ini ← 首次运行自动生成(★ 操作者改端口的地方) ├── data/ ← 首次运行自动生成(库/密钥/日志/备份) └── _internal/ ← 依赖与模板/静态资源(不要动) ``` 把 **整个 `dist/excelToDataBase/` 目录**拷给操作者即可,目标机器无需安装 Python。 ### 操作者如何改端口 1. 打开程序所在目录,找到 **`config.ini`**; 2. 右键 →「打开方式」→「记事本」; 3. 找到 `[server]` 下的 `port = 8000`,改成想要的端口(如 `port = 18080`); 4. 保存,重新双击 `excelToDataBase.exe`。 `config.ini` 首次运行自动生成,**带 UTF-8 BOM**,因此记事本打开时中文注释不会乱码: ```ini [server] ; 监听端口(最常用的设置)。如 8000、8080、18080 ; 若该端口已被占用且 auto_port = true,程序会自动改用后面的端口 port = 8000 ; 0.0.0.0 = 允许同网络其他电脑访问;127.0.0.1 = 仅本机访问 host = 0.0.0.0 ; 端口被占用时是否自动顺延(true / false) auto_port = true ; 启动后是否自动打开浏览器(服务器上一般设为 false) open_browser = true ``` ### 设计要点 | 事项 | 说明 | |---|---| | **端口占用自动顺延** | `auto_port = true` 时自动往后找可用端口,并把实际端口**回写** `config.ini`,避免「配置写 8000、实际跑 8080」的困惑 | | **回写保留注释** | 只替换 `port = ` 那一行,操作者写的注释与排版不动 | | **坏配置不崩** | 端口写成一万以上、布尔值写成中文乱填、文件被写坏——一律回退默认值并记警告,**绝不因一次手滑编辑导致起不来** | | **数据放在 exe 旁边** | 打包后 `data/`、`config.ini` 落在 **exe 同级目录**(不是临时解压目录),拷走 exe 目录即迁移;模板等只读资源才放 `_internal` | | **无 CMD 黑窗口** | `console=False` 打包:双击后不弹黑窗口,直接弹浏览器。**自动开浏览器与有无控制台无关**(走 `webbrowser.open()` 调系统默认浏览器)。无窗口下启动失败改用**弹窗**提示,`open_browser=false` 时用气泡弹窗告知访问地址,不会「双击后毫无反应」 | | **环境变量优先** | `EXCEL2DB_PORT` / `EXCEL2DB_HOST` / `EXCEL2DB_AUTO_PORT` / `EXCEL2DB_OPEN_BROWSER` / `EXCEL2DB_TRAY` / `EXCEL2DB_LOG_LEVEL` 可覆盖文件值(容器部署友好) | > 打包默认 **onedir**(启动快、易排查)。若要单文件,可把 `build_exe.spec` 里的 `COLLECT` 换成 `EXE(..., onefile)`;但 onefile 每次运行都解压到临时目录、退出即删,`config.ini` 与 `data/` 仍会正确落在 exe 旁边(已由 `excelToDataBase/paths.py` 处理)。 > > **无窗口模式下怎么停服务**:关闭浏览器**不会**停止服务。请**右键任务栏右下角的托盘图标 → 退出**(找不到托盘图标时到**任务管理器**结束 `excelToDataBase.exe`)。想改回带窗口(现场看日志/报错),把 `build_exe.spec` 的 `console=False` 改成 `True` 重新打包即可,源码运行时不受影响。 ## 数据与安全 数据库与密钥统一保存在**项目目录下的 `data/`**: ``` <项目>/data/ ├── db/excel2db.db # SQLite 配置库(sys_* 表) └── keys/secret.key # Fernet 加密密钥(数据库密码加密用,权限 600) ``` - 目录按需自动创建(首次启动),`data/` 已在 `.gitignore` 中忽略(保留 `data/.gitkeep`),不会误提交。 - 环境变量覆盖(测试/容器部署用): - `EXCEL2DB_PROJECT_ROOT`:项目根目录(默认取包目录上一级;**打包后默认取 exe 同级目录**) - `EXCEL2DB_CONFIG`:`config.ini` 的完整路径(默认 `/config.ini`) - `EXCEL2DB_DATA_DIR`:数据根目录,默认 `<项目>/data` - `EXCEL2DB_DB_DIR` / `EXCEL2DB_DB_PATH` / `EXCEL2DB_DB_URL`:数据库文件与连接串 - `EXCEL2DB_KEY_DIR` / `EXCEL2DB_KEY_FILE`:密钥目录与密钥文件 - `EXCEL2DB_PORT` / `EXCEL2DB_HOST` / `EXCEL2DB_AUTO_PORT` / `EXCEL2DB_OPEN_BROWSER` / `EXCEL2DB_LOG_LEVEL`:覆盖 `config.ini` 的服务参数 - 数据库密码使用 `cryptography.Fernet` 加密存储。 - 从旧版本升级:若原先使用 `~/.excel2db/`,可将其中 `excel2db.db` 与 `.secret.key` 分别复制到 `data/db/` 与 `data/keys/secret.key`,或通过上述环境变量指向旧路径。 - **仓库中不保留任何外部 Excel 模板文件**,全部配置通过 Web 页面维护。 ## 开发 ```bash pip install -e ".[dev]" pytest # 单元测试(导入引擎/特殊值规则/测试连接/Web 流程) python -m build # 构建 wheel 与 sdist ``` ## CI / 发布 `.cnb.yml` 使用官方 Python 镜像 `docker.cnb.cool/examples/language/python-3:latest`: - `main.push`:构建并上传到制品库(持续验证); - `tag.push`:推送 tag(如 `v0.1.0`)时正式发布到 `https://pypi.cnb.cool/sen_shan/excelToDataBase/-/packages/simple`; - 认证:`TWINE_USERNAME=cnb` + `TWINE_PASSWORD=$CNB_TOKEN`(内置变量,不硬编码令牌)。 ## 里程碑 | 里程碑 | 内容 | 状态 | |---|---|---| | M1 | 项目骨架 + 用户/权限 + `sys_` 建表 | ✅ | | M2 | 数据库配置 + 测试连接 + 密码加密 | ✅ | | M3 | 导入配置 / 字段映射 CRUD + 特殊值规则 | ✅ | | M4 | 导入引擎 + 日志/错误落库 | ✅ | | M5 | 看板页面 + 手动触发导入 | ✅ | | M6 | 排程 + 打包发布 + 文档 + 测试 | ✅ | | M7 | 邮件服务器配置(匿名/SMTP)+ 导入结果邮件通知(二级维护、占位符) | ✅ | | M8 | 导入配置复制 + 字段衍射固定函数下拉/自定义 + 控制表状态管理(写入中100/成功300/失败200)+ 控制表与业务表处理流程规范 | ✅ | | M9 | 导入配置取消状态流转栏位(统一由字段衍射维护)+ 字段衍射新增「成功/失败」栏位 + db/key 文件归入项目 `data` 文件夹(data/db + data/keys) | ✅ | | M10 | 彻底取消状态流转(移除配置表/页面/入口)+ 字段衍射 成功/失败/默认值 支持手工输入(固定函数优先,不输入即空) | ✅ | | M11 | 修正手输值被下拉默认项吞掉:三个取值下拉默认停在「(空)」,手工输入框始终可见可填(填什么就等于该值) | ✅ | | M12 | 批处理号建日志即生成落库(执行中可见、全链路同号)+ 运行日志按「批处理号+日志ID+业务表名」命名;修复批量插入 `_log` 未定义导致每批都降级逐行 | ✅ | | M13 | 在线操作手册 `/manual`(首页 + 看板 + 导航入口,匿名只读,目录支持搜索过滤) | ✅ | | M14 | 扫不到文件即干净结束:新增 `NoSourceFileError`,日志写「本次不执行导入,直接结束」、状态 `skipped`(无文件跳过),无堆栈/无错误明细/不发失败邮件/不搬文件;备份与错误输出只搬「本次处理的文件」;耗时改分钟(含邮件) | ✅ | | M15 | 资料保存天数(默认 14 天,随日志固化,到期自动清理表资料/备份/运行日志/日志记录,空批次目录一并删除)+ 邮件主旨带批次处理号、模型描述占位符、耗时按分钟、失败邮件自动附运行日志(超 1 MB 自动 gzip) | ✅ | | M16 | 字段映射新增「简繁体」栏位(`opencc-python-reimplemented`,10 个方向,只对文本/源列取值生效,可选依赖自动降级),统一简繁避免库里「同义不同形」的重复资料 | ✅ | | M17 | 数据库配置新增「描述」栏位(列表/新增/编辑三处可见);邮件新增 `{db_desc}` 占位符带出库描述;默认主旨中的 `{model_name}` 改为 `{model_name_display}`(旧占位符保留兼容) | ✅ | | M18 | PyInstaller 打包 Windows 免安装 exe:新增 `config.ini`(UTF-8 BOM,**记事本可直接改端口**,改坏自动回退默认)、桌面启动器(端口占用自动顺延并回写配置、自动开浏览器、中文启动横幅与友好报错)、冻结路径适配(可写数据落 exe 同级、只读资源走 `_MEIPASS`)、`build_exe.spec` + `build_exe.py`(预检查 + 打包 + 产物自检) | ✅ | | M18.1 | 操作手册新增第 19 章「打包为 Windows exe」:把打包命令(`python build_exe.py`)写入文档,含准备/命令/产物/选项/自测/常见问题 | ✅ | | M18.2 | 打包改为**无 CMD 黑窗口**(`console=False`),**浏览器仍自动打开**:启动器全面兼容无控制台(`_has_console` / `_safe_print`),失败改弹窗、`open_browser=false` 时气泡弹窗告知地址;手册新增「19.4 无 CMD 黑窗口」问答表与停止服务说明 | ✅ | | M18.3 | 修复无窗口 exe 真机故障 `Unable to configure formatter 'default'`(`sys.stdout=None` 时 uvicorn 日志初始化崩溃):启动器第一步 `_ensure_safe_streams()` 把空标准流重定向到 `data/logs/exe-console.log`(兼作无窗口诊断日志);横幅「停止服务」提示按有无窗口自动切换 | ✅ | | M18.4 | 修复字段映射「新增/编辑」校验失败回填时的 500(`UndefinedError: 'zh_convert_labels' is undefined`):简繁体三个值与 `describe_schedule` 注册为 **Jinja 全局**(静态常量不再逐点传参,杜绝漏传);抽出 `_mappings_error_response` / `_mapping_edit_error_response` 统一构造上下文;`table_field` 由 `Form(...)` 改为服务端友好校验(留空提示中文而非 422 JSON);新增**模板变量审计测试**(静态扫描全部渲染点,守住「硬用法缺变量→500」与「遍历变量缺失→静默降级」两类问题) | ✅ | | M18.5 | 修复排程「周几多选」只存下最后一天的 bug:7 个同名 checkbox 被 `str = Form("")` 标量绑定截断(勾 周一~周五 只收到 `'5'`),改为 `list[str] = Form([])` + `_join_multi()` 合并;**「每天固定」与「每 N 秒/分/时/天」的周几限制同属该字段,一并修复**。顺带修复新增排程校验失败只回 `{"detail": ...}` **400 JSON** 的问题——改为回显中文提示并保留已填内容(新增 `_ScheduleDraft` 回填草稿),并确认校验失败**不会写坏已有排程** | ✅ | | M18.6 | exe 增加**单实例锁与托盘退出**:① 重复双击 exe 弹窗「已在运行」并自动打开已运行实例页面(Windows 命名互斥体,进程死亡自动释放;实际端口记录在 `data/launcher.json`,读取端用端口连通性二次校验防残留文件误判);② 无窗口模式在任务栏右下角显示**托盘图标**(pystray,左键「打开页面」、右键「退出」优雅关停 uvicorn 并停排程器;服务线程意外死亡时托盘自动收起)。`config.ini` 新增 `tray = true/false`;托盘依赖缺失自动回退旧阻塞行为 | ✅ |