1. 20 Aug, 2026 20 commits
    • Data Governance Dev's avatar
      feat(web3): 任务下拉改从实际数据库查 · aa69ab03
      Data Governance Dev authored
      DataQualityView 顶部「任务名称」下拉原来读 mock 的 taskOptionsByType[type],
      改成调后端 listTasks({group}) 按分组名筛真实任务。
      
      **改动(web3/src/views/DataQualityView.vue)**
      - TYPE_TO_GROUP 映射(id-card→身份证 等 6 个 slug → group.name)
      - loadTasksForCurrentType():type 变时自动重拉,默认选第一项;切路由前选中的任务还在列表里就保留
      - watch(type, loadTasksForCurrentType, { immediate: true }) + onMounted 兜底(SSR 兼容冗余)
      - 下拉加 disabled「加载中… / 该分组暂无任务」选项
      - 「查询」按钮 :disabled="!selectedTask";task-info-bar v-if="selectedTask"
      - 删 taskOptionsByType mock 导入
      
      **为什么硬编码映射**:左侧菜单 slug 是英文、DB 分组名是中文,中间没有强对应字段。
      硬编码是最直白方案,避免后端维护冗余 slug 字段(plan §3 决策)。
      
      **API 验证**
        GET /api/tasks?group=身份证 → 4 个任务
        GET /api/tasks?group=手机号 → 0 个(其他分组还没建任务,符合现状)
      
      踩坑(详见 work-logs/2026-08-20.md 主题 30)
      - 选项 key 用 t.id 不是 t.name(同名任务时 Vue 复用 DOM 可能出错)
      - 空态:selectedTask 用 '' 而非 undefined,按钮 :disabled 兜底
      - onMounted + immediate=true 看起来重复,留作 SSR 兼容保险
      aa69ab03
    • Data Governance Dev's avatar
      feat(web3): 通用结果表组件 ResultTable · ce3693a6
      Data Governance Dev authored
      **新增 web3/src/components/ResultTable.vue**
      - props: columns / rows / pageSizes / defaultPageSize / rowKey
      - 自动追加末尾「备注」列(展示该行所有 issues)
      - 错误单元格 el-tooltip 出错误描述(按 col.cn 匹配 issues)
      - 表头「⚠ N规则」+ el-tooltip(col.rules / col.ruleList 驱动)
      - 内置分页 + 每页条数下拉
      - rows 长度变化自动回第 1 页(避免停在空页)
      - 空数据占位行
      - 样式复用 global.css 已定义的 .dq-table / .cell-error / .err-flag / .col-reason / .issue-line
      
      **重构 web3/src/views/DataQualityView.vue**
      - 删:内嵌 <thead><tbody>、isErrorCell/errTip/cellClass/ruleHint 辅助、page/pageSize/pagedRows/rangeStart/rangeEnd 计算、分页按钮块
      - 删:「操作」列(修改/删除按钮按 plan §3 决策拿掉)
      - 换:<ResultTable :columns="fields" :rows="rows" ... />
      - 留:查询区、任务信息条、卡片头部操作按钮(字段配置/导出/导入/批量删除)、FieldConfigDrawer
      
      **API 契约(结果表通用)**
        row[col.key]              显示值
        row.errorCells?: [key]    哪些单元格标红+tooltip
        row.issues?: [{field,desc}] 备注列展开
        col.rules                 表头规则数
        col.ruleList?             表头 tooltip 明细
      
      踩坑(详见 work-logs/2026-08-20.md 主题 29)
      - el-tooltip 默认包 <div> 会破坏 td inline 布局 → 触发元素改 inline-flex span
      - global.css 旧 .cell-error:hover .err-tip 还在,hover 可能出鬼影 tooltip,留待清理
      - 组件内管分页必须 watch rows 长度变 → 回第 1 页,否则批量删除后停在空页
      ce3693a6
    • Data Governance Dev's avatar
      feat(web3): P4 任务字段+校验规则持久化 · 62f0ce1a
      Data Governance Dev authored
      **后端**
      - 新增 models/field.py + models/rule.py(两张 ORM:field/task_id FK + rule/field_id FK + ON DELETE CASCADE)
      - Field.__table_args__ 加 UNIQUE(task_id, field_key)(schema.sql 第 59 行的事实地约束)
      - models/__init__.py 注册新模型;app.py 在 init_db 前 import models(否则 Base.metadata 不知道有这两张表)
      - routers/tasks.py:POST/PUT/copy 接 fields+rules;整存整取(先删后写,DB CASCADE 带走 rule);GET 列表给 fields/rules 计数,详情带 field_list;pydantic Field 改 import as PydField 避与 SQLAlchemy Field 同名撞车;port 出参强转 str 防 conn_json 里 int 致 500
      - db/database.py:新增 _migrate_field_unique()(仿 _migrate_connection_preset_unique 套路:检测 UNIQUE 缺失则 drop+recreate),init_db 里调用
      
      **前端**
      - TaskConfigView.openEdit:拆「同步设 row + 异步 getTask」两步,确保 TaskModal 拿到带 field_list 的详情(列表接口故意不带 field_list 省带宽)
      - TaskModal:watch(open) 编辑场景回填 form + 抽 _fillFieldListFromTask;新加 watch(field_list) 处理 getTask 异步时序;loadColumns 重选表时按 field_key 合并 rules 不覆盖
      - TaskModal:加 connVerified 标志,testConn 成功才置 true;loadTables/loadColumns 未验证直接 return(编辑场景 form.schema 从 task 回填会触发 watch(form.schema) → loadTables → 空密码连 MySQL 报 1045)
      
      **接口契约**
      - POST/PUT /api/tasks body 加 fields: [{key, show_default, rules: [{desc, regex}]}]
      - GET /api/tasks 返回 fields/rules 计数;GET /api/tasks/{id} 多返 field_list
      - POST /api/tasks/{id}/copy 复制时连同 fields+rules 一起克隆,名字加 _副本 自动避重
      - DELETE 走 DB 层 CASCADE(field/rule 一并清)
      
      **端到端**
      - POST 3 fields + 3 rules → 列表/详情计数与字段一致
      - PUT 整体替换(删 1 加 1 改 1 全部生效)
      - POST /copy → field_list 全克隆
      - DELETE → SQLite 直查 0 孤儿 field/rule 记录
      
      踩坑(详见 work-logs/2026-08-20.md 主题 28 + 微调 1/2/3)
      - pydantic Field 被 SQLAlchemy Field 同名遮蔽(import 时必须 as 别名)
      - Base.metadata.create_all 不会改已存在表(UNIQUE 靠 ORM 声明 + 启动时迁移脚本兜底)
      - 验 SQLite UNIQUE 不能只看 sqlite_master type='index',UniqueConstraint 在表内 CREATE TABLE 子句里,要 INSERT 重复值测
      - 编辑场景的 watch(form.schema) 没拦住自动连数据库(connVerified 守卫)
      62f0ce1a
    • Data Governance Dev's avatar
      feat(web3): AI 生成正则 + Python re 测试 + 规则弹窗保存按钮 · 4aed2229
      Data Governance Dev authored
      LLM 接入(P9 plan 阶段)
      - 后端:
        - backend/core/llm.py 从 web/core/llm.py 拷贝,default_config_path() 改读 web3/backend/configs/llm.yaml
        - backend/core/ai_regex.py:强制走 LLM(删规则库),LLM 返回用 re.compile 校验,编译失败回弹修复,最多 5 次重试
        - backend/routers/ai.py:注册 POST /api/ai/regex
        - backend/configs/llm.example.yaml:多 provider 示例模板(真实 llm.yaml 已 gitignore)
        - backend/app.py:注册 ai_router
        - backend/requirements.txt:+anthropic +openai +pyyaml
      - 前端:
        - src/api/ai.js:genRegex(desc)
        - src/components/RuleModal.vue:删前端 mock 改调后端
      
      测试改走 Python re(避免 JS RegExp 方言差异)
      - backend/core/ai_regex.py:+test_regex(regex, value) → {ok, match, error, groups}
      - backend/routers/ai.py:+POST /api/regex/test
      - src/api/ai.js:+testRegex(regex, value)
      - src/components/RuleModal.vue:testRegex() 改调后端,捕获组带回前端展示
      
      规则弹窗保存按钮
      - src/components/RuleModal.vue:emit 'save' + 取消/保存两按钮 + onSave() + hasValidRules
        (必须 ≥1 条规则 + desc/regex 都非空才允许保存)
      - watch 监听 [open, field.key],每次打开从父组件重新同步
      - src/components/TaskModal.vue:onRuleSave 回写 field.rules(徽章数字刷新)
      - TaskModal onSave() payload 加 fields: [{key, en, cn, rules, show_default}](P4 落库用)
      
      端到端验证
      - 身份证/URL/UUID/产品型号 多 desc 都通过 LLM 生成 + Python re 校验
      - 7 例 /api/regex/test 覆盖:真身份证、短串、捕获组日期、分隔符错、空 value、语法错、空 regex
      
      踩坑
      - 规则库'数字'关键词过宽易误命中,按用户意见删整段强制走 LLM
      - curl 中文 JSON body 在 Git Bash 转义破坏,用 --data @file 或 printf
      - python web3/backend/start.py 报 ModuleNotFoundError,要 python -m web3.backend.start
      - Vue3 const 不 hoist,hasValidRules 必须放在 ruleList 声明之后
      4aed2229
    • Data Governance Dev's avatar
      feat(web3): 选择数据表 → 自动加载字段(数据字典)填充校验规则 Tab · 130b8398
      Data Governance Dev authored
      后端:
      - list_columns.{mysql,oracle,dameng}.sql 加 TABLE_NAME 过滤,绑定参数 (schema, table_name)
      - db_adapter.list_columns 签名加 table_name 参数
      - 新增 POST /api/connect/columns 端点 + ListColumnsRequest schema
      
      前端:
      - connections.js 加 listColumns(form, tableName)
      - TaskModal.vue:
        - 删 rebuildFieldList() + fieldsByType/fieldRuleSamples mock 路径
        - 加 loadColumns() + watch(form.table) 触发
        - 校验规则 Tab 三态提示(加载中 / 请选表 / 空表)
        - loadTables() 加「保留旧选中」(编辑场景)
        - 列定义:字段名(英文) / 字段注释(只读) / 数据类型 / 规则设置 / 默认展示
      
      测试:
      - tests/test_load_columns.py 端到端验证(30 列渲染 + /api/connect/columns 调用 1 次)
      - tests/rule_tab_with_columns.png UI 截图
      130b8398
    • Data Governance Dev's avatar
      feat(web3): Schema/选择数据表 改 el-select filterable 模糊搜索 · 90452495
      Data Governance Dev authored
      之前用原生 <select> 无法过滤,schema 列表动辄几十个、数据表几百张,用户找目标很不方便。
      改成 Element Plus <el-select filterable>,内置模糊搜索 + clearable 一键清空。
      
      - Schema:filterable + 动态 placeholder(无数据时显示「请先测试连接」)
      - 选择数据表:filterable + label 带表注释「表名(注释)」格式 + 三态 placeholder
      
      实测:tests/test_filterable_select.py 跑 Playwright,输入 "house" 后下拉只剩匹配项 c_house_data,其他 5 个 schema 被过滤,匹配文字高亮。
      90452495
    • Data Governance Dev's avatar
      fix(web3): 弹窗 form-grid 列收缩 + form-row.full 防止被内容撑宽 · 85dfa83f
      Data Governance Dev authored
      - .form-grid 列 minmax(320px, 1fr) → minmax(0, 1fr):允许列收缩到内容真实需要的大小
      - .form-row.full 加 min-width: 0:跨列行允许收缩,防止被内嵌 flex 子项的 min-content 撑宽
      
      效果:Tab2 连接方式右列(主机地址 / 数据库名 / 密码)和下方数据表右列(选择数据表)x 坐标严格对齐,不再被 modal-body overflow-x: hidden 切掉。
      
      实测:tests/test_modal_overflow.py 跑 Playwright 测距确认两个 grid 同宽 960px,右列 form-control 右边都对齐在 x=1200,无溢出。
      85dfa83f
    • Data Governance Dev's avatar
      fix(web3): 历史连接选中后密码回填 · 81bbb1ba
      Data Governance Dev authored
      - connectionPresets.js presetToForm():password 从 '' 改为 preset.password || ''
      - 预设里明文存的密码(来自 seed.py 的 yaml 灌入)直接回填到 TaskModal 表单
      - 用户无需在历史连接选中后重输密码
      81bbb1ba
    • Data Governance Dev's avatar
      feat(web3): 任务弹窗「历史连接」下拉选择器 · db3184e0
      Data Governance Dev authored
      - schema.sql 加 connection_preset 表(UNIQUE(db_type, name) + ord 索引)
      - ORM models/connection_preset.py 新建;models/__init__.py 导出
      - seed.py 加 seed_connection_presets(13 条来自 web2/configs/db_defaults.yaml;硬编码避免引 pyyaml)
      - database.init_db() 改造:DB 不存在→schema.sql 全量;DB 存在→Base.metadata.create_all 增量建表 + _migrate_connection_preset_unique() 一次性补 UNIQUE
      - routers/connection_presets.py 新增 5 端点:列表(可选 db_type 过滤)/按 id/POST/PUT/DELETE
      - app.py 注册 connection_presets_router
      - src/api/client.js 加 params query string 支持(GET list 用)
      - src/api/connectionPresets.js 新封装 list/get/create/update/delete + presetToForm helper(密码暂不回填)
      - TaskModal.vue Tab2 顶部加「历史连接」section:下拉按当前 dbType 过滤,选中后回填 connName/host/port/db/user 字段,schema/表清空等用户测试连接再拉
      db3184e0
    • Data Governance Dev's avatar
      feat(web3): 实际数据库连接(参考 web2) · 6edeb386
      Data Governance Dev authored
      - 后端 core/ 模块自包含(拷贝 web2 的 db_adapter.py / sql_loader.py / models.py + 8 个 SQL 模板),不依赖 web.core
      - requirements.txt 加 pymysql / oracledb / dmPython
      - routers/db.py 新增 /api/connect/test + /api/connect/schemas + /api/connect/tables 三个端点
      - app.py 注册 db_router
      - 前端 api/connections.js 封装 3 个调用 + 中文 label→小写 db_type 转换
      - TaskModal.vue testConn() / loadSchemas() / loadTables() 改真实后端调用,去掉所有 mock
      6edeb386
    • Data Governance Dev's avatar
      feat(web3): 数据库连接 Tab 缩减 + 弹窗 form-grid 修复 · 11d7089d
      Data Governance Dev authored
      - 任务弹窗「数据连接」Tab 数据库选项 6→3:保留 MySQL / Oracle / 达梦 DM,移除 SQL Server / PostgreSQL / JDBC 自定义
      - global.css .modal 加宽 880→1000;.form-grid 列最小宽度 320px,右半边输入框不再被裁切
      - docs/db-schema.md task 表 db_type 列注释同步
      11d7089d
    • Data Governance Dev's avatar
      test+docs(web3): 任务端到端测试 + DDL 文档 · e2c16e6e
      Data Governance Dev authored
      测试
      - tests/test_task_conn_fields.py
        - 验证 conn_json 全字段解析(conn_name/host/port/db/user/
          jdbc_params/schema)
        - 验证编辑留空密码时后端兜底保留旧值
        - 验证显式新密码能正确覆盖
      - tests/test_task_persistence.py
        - POST 写入 → taskkill /F 关后端 → 重启 → GET 读回
        - 同时直接 sqlite3.connect() 读 DB 文件确认磁盘数据
        - 证明任务 CRUD 真的落到 SQLite,重启不丢
      
      DDL 文档 web3/docs/db-schema.md
      - 启动流程(init_db() → schema.sql + seed)
      - 表关系总览(task_group/task/field/rule/validation_run/violation)
      - 当前已启用 2 张表(task_group + task)的列结构 + 索引 + 种子
      - conn_json 内嵌字段全清单
      - P4 起预留表结构预览
      - CRUD 路由对照
      - 验证脚本清单
      - 踩坑表(含本次状态 UI 移除)
      e2c16e6e
    • Data Governance Dev's avatar
      fix(web3): 弹窗 form-grid 横向溢出 + 标签对齐修复 · 2ef2965e
      Data Governance Dev authored
      用户反馈任务弹窗里:
      1. 任务说明 textarea 撑出了 modal-body 右边界,出现横向滚动条
      2. 任务说明标签和任务分组的标签 x 坐标没对齐
      
      根因
      - form-grid 用 grid-template-columns: 1fr 1fr,默认是
        minmax(auto, 1fr),auto = 子元素最小内容宽度
      - form-row 里的 flex 子元素(form-control → textarea)默认
        min-width: auto,不会被压到比内容更小
      - textarea 内容/padding 撑大 → grid item 溢出列宽 → full-row 也顶宽
        → 超出 modal
      
      修法
      - .form-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
        grid item 允许收缩到 0 宽
      - .form-row { ...; min-width: 0; }
      - .form-control { ...; min-width: 0; }
        flex 子允许收缩到 0
      - .form-control textarea { max-width: 100%; box-sizing: border-box; }
        padding/border 不撑大盒子
      - .modal-body { overflow-x: hidden; }
        兜底防整个页面出横滚
      
      验收
      - Vite 实时刷样式生效
      - 浏览器刷新后横向滚动条消失
      - 任务说明标签与任务名称/任务分组标签同 x 坐标
      2ef2965e
    • Data Governance Dev's avatar
      feat(web3): 任务编辑回填全部连接字段 + 密码兜底 + 移除状态 UI · bd9adc8e
      Data Governance Dev authored
      P3 头部 CRUD 跑通后发现「编辑」流程有 4 个缺口,本次补齐:
      
      后端
      - models/__init__.py: 补 Task 导出(之前只有 TaskGroup)
      - routers/tasks.py::TaskOut 增加 conn_name/host/port/user/jdbc_params/schema
        6 个字段(从 conn_json 解析),供前端编辑回填
      - routers/tasks.py 抽出 _parse_conn_json() helper
      - routers/tasks.py::update_task 加密码兜底:前端编辑时
        conn_json.password 为空 → 自动用旧值替换,避免误清空
      - routers/tasks.py::copy_task 默认 status='停用' 改为沿用源 status
      
      前端
      - TaskModal.vue Tab1 基础信息加「状态」单选(启用/停用)
      - TaskModal.vue 编辑 watch 从 task.* 还原全部 13 个字段
        (修前只回填 3 个,连接信息全丢)
      - TaskModal.vue 密码不回填到前端(安全考虑),后端兜底
      - TaskModal.vue onSave payload 改成 form.status || '启用'
      - TaskConfigView.vue 任务列表删「状态」列,colspan 9 → 8
      
      UI 调整
      - 用户反馈不要启用/停用状态,又把 Tab1 状态单选和列表状态列都删掉
        (DB status 列保留,向后兼容)
      
      验收
      - tests/test_task_conn_fields.py 端到端跑通:6 步全过
      bd9adc8e
    • Data Governance Dev's avatar
      style(web3): 去掉左侧「校验任务」菜单 icon · 2edc6e83
      Data Governance Dev authored
      - 6 个分组项去掉 <el-icon> 渲染
      - NAME_TO_MENU 移除 icon 字段(不再使用)
      - 「任务配置」项的 <el-icon><Setting /></el-icon> 保留(管理分组,视觉区分用)
      2edc6e83
    • Data Governance Dev's avatar
      feat(web3): P3 任务 CRUD(头部) · 04e0282c
      Data Governance Dev authored
      后端:
      - models/task.py:ORM(name/group_id/data_source_path/source_table/status/description/db_type/conn_json/timestamps)
        - to_dict() 含 group 名字 + fields_count/rules_count(占位 0,P4 改 join)
      - routers/tasks.py:6 端点
        - GET /api/tasks?group=&keyword=(keyword 同时匹配 name 和 source_table)
        - POST(name 重名 → 409)、GET /{id}、PUT /{id}、DELETE /{id}(级联走 DB ON DELETE CASCADE)
        - POST /{id}/copy:自动生成不冲突的 xxx_副本 / xxx_副本2 ...
        - group_id 通过 group 名查询
        - conn_json 解析出 db 字段返回
      - app.py:注册 tasks 路由
      
      前端:
      - src/api/tasks.js:listTasks / getTask / createTask / updateTask / deleteTask / copyTask
      - TaskConfigView.vue:
        - 完全去掉 mockData.taskList,onMounted 拉 /api/tasks
        - 新增/编辑/复制/删除全走 API,错误 ElMessage 提示
      - TaskModal.vue:onSave 发 {name, group, db_type, data_source_path, source_table, status, description, conn_json}(P3 不写 fields/rules,留 P4)
      
      验收(经 Vite 代理,10 步链路):
        空列表 → 新建 → 重名 409 → 分组筛选 → 关键字筛选 → 复制 → 更新 → 404 → 删除 → 剩 N 条
      04e0282c
    • Data Governance Dev's avatar
      feat(web3): P2 任务分组码表 + 前端接码表 · 337a9ad3
      Data Governance Dev authored
      后端:
      - db/schema.sql:建 6 张业务表(task_group / task / field / rule / validation_run / violation)
      - db/seed.py:DEFAULT_TASK_GROUPS = 6 个(身份证/手机号/邮箱/地址信息/银行卡号/日期格式)
      - db/database.py:init_db() 首次启动读 schema.sql + seed
      - models/task_group.py:ORM + to_dict()
      - routers/task_groups.py:GET / POST(name 重名 409,按 ord 升序)
      - app.py:注册 task_groups 路由 + 启动 init_db()
      
      前端:
      - src/api/client.js:fetch 封装(get/post/put/delete)
      - src/api/taskGroups.js:listTaskGroups / createTaskGroup
      - MainLayout.vue:
        - onMounted 拉 /api/task-groups,provide('taskGroups') 给子组件
        - NAME_TO_MENU(name → path+icon)只显示码表里有映射的项
      - TaskModal.vue / TaskConfigView.vue:inject('taskGroups'),分组下拉/筛选用同一数据源
      337a9ad3
    • Data Governance Dev's avatar
      feat(web3): 后端骨架 + Vite 代理 + start.bat 同窗口启动 · 4ffe4159
      Data Governance Dev authored
      - 新增 web3/backend/ 骨架(FastAPI + SQLAlchemy 2 + SQLite)
        - start.py / app.py / _logging.py / config.py / requirements.txt
        - db/database.py:engine + SessionLocal + get_session(init_db 占位)
        - 空的 models/__init__.py、routers/__init__.py、db/__init__.py
      - vite.config.js 加 /api 代理 → http://localhost:8767
      - web3/start.bat 修复:cwd 保持在项目根,python -m web3.backend.start 能 import web3 包
        - 后端启动后 curl /api/health 轮询 20×1s,就绪再起前端
        - REM 中文 + 等号改纯文本,ping 替代 timeout,LF→CRLF 转换
      4ffe4159
    • Data Governance Dev's avatar
      chore(web3): start.bat 不再开新窗口 · e8512305
      Data Governance Dev authored
      去掉 start cmd /k 和末尾的 pause —— 直接在当前窗口跑 npm run dev,
      Ctrl+C 即可停 dev server。
      e8512305
    • Data Governance Dev's avatar
      chore: web3 项目起步(前端骨架 + 日报 skill + 后续计划) · 088d3c61
      Data Governance Dev authored
      .gitignore 新增 web3/backend/configs/llm.yaml 和 web3/data/ 排除项。
      CLAUDE.md 改为「现在在 web3 开发」,web 和 web2 都列为参考。
      新增 .claude/skills/write-daily-report skill(按 git log + work-logs 生成当日日报,3-7 句纯描述性中文)。
      新增 web3/ 子项目:Vue 3.5 + Vite 6 + Element Plus 2.9 + vue-router 4,端口 5175。
      含 DataQualityView / TaskConfigView / TaskModal(三 Tab)/ RuleModal / FieldConfigDrawer。
      当前用静态 mock(src/data/mockData.js),未接后端;npm run dev 在 5175 验证通过。
      新增 web3/plan.md:后端 FastAPI + SQLite、进程内 JobManager 异步(不用 Redis/Celery)、
      LLM 从 web1 core/llm.py 直接拷贝自包含;列了 10 个开发阶段。
      088d3c61
  2. 17 Aug, 2026 20 commits
    • Data Governance Dev's avatar
      feat(ui): 标题/菜单字号加大 + 顶部 textarea 简化 · d0f90abe
      Data Governance Dev authored
      需求:左上角「数据治理工具」+ 菜单「身份证合规验证」字体都更大
      
      改动 1 — MainLayout.vue:
      - 标题 font-size 18px → 22px,logo icon 22px → 26px
      - 菜单 font-size 17px → 20px,menu icon 19px → 22px
      - 菜单项 height 52px → 56px(容纳更大字号)
      
      改动 2 — IdCardTool.vue:
      - 删除顶部「自由文本(与下方卡片互不联动)」提示
      - 删除「清空」按钮
      - 同步删 clearRuleText() 函数 + .idcard-page__head-bar/.head-hint 死代码
      - 顶部仅保留一个干净的 textarea
      
      build: 6.66s 通过
      d0f90abe
    • Data Governance Dev's avatar
      feat(idcard-table): 大表分页显示 + 轮询不卡 · ee2976ed
      Data Governance Dev authored
      需求:大表(5000+ 行)一次渲染卡浏览器;轮询时整表重渲染卡顿
      
      改动(IdCardTable.vue):
      - 模板:truncateAt 截断提示 → el-pagination 分页器
        · pageSizes: [25, 50, 100, 250]
        · 仅 rows.length > pageSize 时显示(避免小表无意义 UI)
      - script:
        · currentPage = ref(1), pageSize = ref(25)
        · displayedRows 改成 pageSize 切片(替换 truncateAt 切片)
        · watch(props.rows) → currentPage 重置(防御性)
      - style: 分页器靠右
      
      轮询不卡的原理(三件套):
      1. IdCardTool.vue: renderedTableNames Set 去重 → 已存在表不重复 push
      2. violationsByTable.value.push(t) 只新增不替换 → 现有 IdCardTable 实例不动
      3. :rows="t.bad_rows" 是稳定 prop 引用 → Vue 不重渲染现有表格
      
      导出功能不变:仍导出 props.rows(不受分页影响)
      
      详细 work-log 见 work-logs/2026-08-17.md 主题 19
      ee2976ed
    • Data Governance Dev's avatar
      fix(idcard): Oracle 反查 3 个串联 bug · 0320cbc0
      Data Governance Dev authored
      主题 16 — Oracle 反查 0 行
        - 现象:Oracle 扫描有违规值但反查表格 0 行
        - 根因:data_dict 的 column_name 是 Oracle 原样「大写」,但
          _rows_as_dicts 把行 dict key 规范化成「小写」,
          row.get('TZRSFZH') 永远 None → 现场 validate 误判 NULL 跳过
        - 修复:_fetch_bad_rows_one 加 row.get(cname.lower()) fallback
      
      主题 17 — Oracle 表格其他列也空
        - 现象:DA60_QY_MSXX 70 行违规展开后只有「违规」列,其他列全空
        - 根因:Oracle _rows_as_dicts 把所有列名强制小写,data_dict 大写,
          前端 row[colName](大写)查不到 → formatCell(undefined)='∅'
          主题 16 只修了 cname 单列,没修其他列
        - 用户反馈:「mysql 和 Oracle 难道不是用的同一套接口吗?」
          三个 DB 走同一个 _rows_as_dicts,但驱动 case 行为不同:
            MySQL pymysql: 跟 schema
            达梦 dmPython: 保留原样
            Oracle oracledb: 总是 lowercase
        - 修复:新增 _normalize_row_keys(row, columns),装配 BadTable 时
          用 columns 元数据做大小写权威,把 row 所有 key 改成一致
      
      主题 18 — 0 违规的表不显示
        - 现象:PM_SYSTEMUSER.SFZH 0 违规但表格仍渲染 + 空表 + 「该表无违规行」
        - 根因:装配点不管有没有违规都创建 BadTable
        - 修复:流式推送处 + 最终汇总处都加 if not fixed_rows: continue
      
      详细 work-log 见 work-logs/2026-08-17.md 主题 16/17/18
      0320cbc0
    • Data Governance Dev's avatar
      perf(web2): 去掉表头 checkbox 整列变灰联动,避免大表卡顿 · 09998006
      Data Governance Dev authored
      问题:
      - IdCardTable 数据列 el-table-column 上挂了两个动态 class-name 绑定:
          :class-name="isColSelected(c) ? '' : 'idcard-table__col-unselected'"
          :header-class-name="isColSelected(c) ? '' : 'idcard-table__col-unselected'"
      - 任意一列 checkbox 切换都让 columnSelection 变化 → 每列重算 className
        → el-table 对全表 <td> 重写 class 属性
      - 800+ 行 × 19 列 ≈ 15000+ 次 DOM patch,操作密集导致卡顿
      
      修复:
      - 删掉 el-table-column 上的 :class-name / :header-class-name 绑定
      - 删掉对应的 .idcard-table__col-unselected 样式
      - 保留 checkbox 勾选状态、selectedColumns / exportedDataColumns 响应式数据
        以及工具栏「已勾选 N 列」提示;导出仍按 selectedDataColumns 过滤
      
      效果:勾选/取消 checkbox 不再让整列变灰,表格交互流畅度恢复正常。
      09998006
    • Data Governance Dev's avatar
      feat(web2): 左侧菜单改为「身份证合规验证」+ 顶部独立规则描述面板 · 1cb480b4
      Data Governance Dev authored
      UI/UX 调整:
      - 左侧菜单 + 路由 meta + 面板标题统一改为「身份证合规验证」
      - 左侧菜单字号加大(14→17px)、行高加高(52px)
      
      IdCardTool.vue 顶部新增独立 textarea 面板:
      - 与下方 5 张规则卡片完全分离(互不联动)
      - 默认填充 5 句规则 summary(每行一条),用户可自由编辑
      - 自有 localStorage key(idcard:rule-text-v1)持久化
      - 提供「清空」按钮
      
      5 张规则卡片样式还原 a5990444(图标 + 标题 + ❓,hover tooltip 看详细):
      - 每条规则拆成 summary(一句话)+ detail(详细列表)
      - tooltip 展示 detail 多条 li(恢复 a5990444 详细版本)
      - 顶部 textarea 默认值取 summary
      
      涉及踩坑:
      - 后端 FastAPI 直接托管 web2/dist/(web2/backend/app.py:93),
        源码改动后浏览器不更新,必须 npm run build 重新生成 dist/
        才能让 192.168.101.126:8766 服务拿到新版本
      1cb480b4
    • Data Governance Dev's avatar
      fix(idcard-scan): 反查时现场 validate,避免大小写/collation 漏匹配导致误标「不合规」 · 581b3e16
      Data Governance Dev authored
      - 问题:MySQL DISTINCT/IN 按 collation 比较(utf8mb4_unicode_ci 不区分
        大小写),但 Python dict 区分大小写。如果数据库真实存的是大写 X 但
        distinct 返回小写 x,bad_reasons 里只有小写的 key,反查拿到大写 X
        的行就查不到 key,错误地标 '不合规'。
      - 例子:'42112319760324521X'(合规)+ '42112319760324521x'(CHARSET
        违规)共存时,大写 X 那行被错误标 '不合规',且原因不明确。
      - 修复:去掉 bad_reasons 缓存查询,对每行现场 validate —— 合规行直接
        丢弃,违规行给出准确原因。出现丢弃时打 INFO 日志方便排查。
      581b3e16
    • Data Governance Dev's avatar
      feat(web2): 身份证治理去掉空字符串检测 —— 空白/None/全空格都不算违规 · a5990444
      Data Governance Dev authored
      需求调整:业务上认为「字段没填」≠「填了不合规的值」。
      之前 NULL/空字符串会被打成 EMPTY violation 计入 bad_rows,新逻辑直接合规。
      
      改动:
      - idcard_validator.py
        - validate():value is None 或 strip() 后空字符串 → ok=True, violations=[]
        - VIOLATION_LABELS 移除 EMPTY 条目
        - docstring 顶部去掉「1. EMPTY」校验项,更新设计说明
      - IdCardTool.vue
        - ruleLabels 移除 EMPTY → '空值'
      
      验证(python 直接调 validate):
        None / '' / '   ' / '\t\n  '  →  ok=True, violations=[]  ✓
        '110101199003078888'           →  ok=False, ['CHECK_DIGIT'] ✓
        '12345'                         →  ok=False, ['LENGTH']    ✓
      
      涉及到的语义变化:
      - 统计上:matched_columns 不变(仍然命中的字段),invalid_count 会下降
      - 前端 UI:「空值」chip 不会再出现
      a5990444
    • Data Governance Dev's avatar
      feat(web2): IdCardTable 列选择(表头 checkbox)+ 导出 Excel · cfca7ea3
      Data Governance Dev authored
      列选择(为「导出选列」预埋):
      - 每列表头前加 el-checkbox,可单独控制该列是否参与导出
      - 第 0 列固定全选 checkbox(支持半选 indeterminate)
      - 「违规」列也加 checkbox,内部用 __violation__ 作 key
      - 默认全部勾选;新出现的列自动勾上
      - 未勾选的列**不隐藏**(仍显示),仅加 idcard-table__col-unselected class 灰显
      - 暴露 selectedColumns / selectedDataColumns / selectAll / deselectAll 给父组件
      - emit columns-change 事件
      
      导出 Excel:
      - 表格上方加工具栏:[📥 导出当前表 (N 行)] 已勾选 X/Y 列
      - 文件格式:
        - 第 1 行:字段注释(column_comment)
        - 第 2 行:字段名(column_name,加粗 + 浅灰背景 + 居中)
        - 第 3 行起:数据
        - 含「违规」列时,第1行首列 = 「违规原因」,第2行首列 = _id_card_violation
      - 黑色细边框(4 边 thin + #000000)
      - 冻结前 2 行(Excel 滚动时表头固定)
      - 超过 5000 行弹确认框
      - sheet 名 sanitize(去掉 :\/?*[],最长 31 字符)
      - 文件名 sanitize(去掉 <>:"/\|?*,最长 100 字符)
      
      IdCardTool.vue:给 <IdCardTable> 加 :table-name="t.table_name"
      cfca7ea3
    • Data Governance Dev's avatar
      chore(deps): web2 装 xlsx-js-style 替代 xlsx(支持 cell 样式导出) · c9e539bb
      Data Governance Dev authored
      - 卸载 xlsx(社区版不支持 cell-level s 属性)
      - 装 xlsx-js-style(API 完全兼容 + 支持 s.border / s.font / s.fill)
      - 用于 IdCardTable 导出 Excel 时给有数据区域加黑色细边框
      - bundle size: xlsx 290KB → xlsx-js-style 870KB(gzip 后约 230KB)
      c9e539bb
    • Data Governance Dev's avatar
      feat(web2): DbConnectionPopover 多连接列表 + IdCardTool 适配 · e1a00dc0
      Data Governance Dev authored
      - DbConnectionPopover.vue
        - 触发器显示:「display · host:port」
        - 弹框按 db_type 分组(MySQL / 达梦 / Oracle),每组下显示该 type 的所有连接
        - 每行:连接名 + host:port + 测试/切换按钮
        - 切换按钮独立判断 isCurrent(db_type, id),不再按 db_type 切换
        - 弹框宽度 280 → 320(容纳 host:port 信息)
      - IdCardTool.vue
        - state.currentType 引用全部替换为 currentConnection?.host
        - db_type 从 conn.db_type 取(store 已注入),不再用 state.currentType
      e1a00dc0
    • Data Governance Dev's avatar
      refactor(web2): store + 兜底配置改为列表结构 — 多连接支持 · 934865dd
      Data Governance Dev authored
      - dbPresets.js:DB_PRESETS 改为列表结构(每 db_type 是 Connection[])
      - useDbConnection.js
        - state.currentType: string → state.currentConnection: {db_type, id}
        - presets[db_type] 从单 dict 改为 Connection[]
        - 新增 currentType computed(便捷暴露 db_type)
        - 新增 setCurrentConnection(db_type, id) 替代 setDbType(type)
        - localStorage 新 key web2.dbConnection.current,序列化用 "db_type::id"
        - API 拉回后校准:当前连接丢失则 fallback 到第一个连接
      934865dd
    • Data Governance Dev's avatar
      feat(web2): /api/db-defaults 改读 web2 yaml + 列表结构返回 · 562894a9
      Data Governance Dev authored
      - 路径:web/configs/db_defaults.yaml → web2/configs/db_defaults.yaml
        (web2 与 web1 配置完全分离,2026-08-17 起)
      - 新增 _normalize_defaults():
        - 新格式 {db_type: [conn, ...]} → 直接归一化(自动注入 id + db_type)
        - 旧格式 {db_type: {conn}} → 包成单元素 list(向后兼容)
      - 返回结构:{mysql: [{...}, ...], dameng: [...], oracle: [...]}
        每个连接有 id + db_type(自动注入,避免前端漏字段 → FastAPI 422)
      - _mask_password 加 id 字段打印,日志输出加「总连接数 + 各 type 数」统计
      562894a9
    • Data Governance Dev's avatar
      chore: .gitignore 增加 web2/configs/db_defaults.yaml · ec0d1323
      Data Governance Dev authored
      - web2 现在用独立的 db_defaults.yaml(列表格式,多连接)
      - 与 web1 的 web/configs/db_defaults.yaml 同样不进版本
      - 含真实凭据(Cd@…/HGwxzj2026/GJJ80_Hg41RG)
      ec0d1323
    • Data Governance Dev's avatar
      chore(skills): 新增 kill-port skill — 跨平台杀端口进程 · 637b5147
      Data Governance Dev authored
      - SKILL.md
        - 项目级 skill(.claude/skills/kill-port/)
        - 默认杀 8765 (web) / 8766 (web2) / 5173 / 5174 (Vite dev)
        - 可传自定义端口
        - 退出码 0/1/2 分别表示成功/端口空闲/杀失败
      - scripts/kill-port.sh(Git Bash / Linux / macOS)
        - 自动检测平台:netstat.exe+taskkill.exe (Windows) / lsof+kill -9 (POSIX)
        - 多端口串行处理,独立退出码
        - 杀完二次确认端口已释放
      - scripts/kill-port.bat(Windows cmd 原生版)
        - 同等功能,:kill_one 子函数处理单端口
      637b5147
    • Data Governance Dev's avatar
      feat(web2): IdCardTool UI — 折叠结果面板 + 弃用 SSE 改轮询 + done 分支兜底 · 91d91703
      Data Governance Dev authored
      - 弃用 SSE(EventSource)
        - 原方案 push 大 payload + Vue 响应式触发 1000+ 行 el-table 重渲,主线程卡 2s+ 导致页面「无响应」
        - 改为 setInterval(1000ms) 轮询 + 立即首次触发
      - 删除大进度卡片(3 指标 + el-progress)
        - 改为单行扫描提示:Loading 图标 + 「已发现 X 张问题表 / Y 行违规(Zs)」
      - 结果区改为 el-collapse 折叠面板(每张表一个 collapse-item)
      - 新增 violationsByTable + liveBadRowCount 两个 ref(模板读这两个,不直接读 scanResult)
      - polling 累加 + done 兜底:
        - running 阶段 partial 来的表 push 到 violationsByTable(dedupe by renderedTableNames Set)
        - done 分支兜底:若 violationsByTable 为空,从 data.response.violations_by_table 同步灌入
          (解决扫描太快<1s 时 running partial 没机会被观察到的问题)
      - 错误处理:
        - 404 = scan_id 过期(10 分钟 TTL),友好提示
        - 网络瞬断不停轮询,下次自动恢复
      91d91703
    • Data Governance Dev's avatar
      feat(web2): 新增 IdCardTable 组件 — 违规标签列 + 列拖拽 + 列宽自适应 · f2291cf0
      Data Governance Dev authored
      - 第 1 列固定「违规」摘要(width=200, min-width=160)
        - el-tooltip 展示详细原因(前 N 字符截断 + 悬停看完整)
        - 违规标签: red 圆角徽章 + 警示图标
      - 数据列:身份证列加宽到 220px + 蓝字 + User 图标
        - 其余列 min-width=120,列可拖拽(el-table-column 默认 resizable)
      - 表头 tooltip:字段名 / 字段注释 / 数据类型 三行
      - 单元格:身份证列若值不合规显示红字(粗略校验,真实校验在后端)
      - 防爆:rows > 5000 时显示「仅展示前 5000 行」提示
      - 内部字段(_ 开头)自动从 dataColumns 过滤掉
      f2291cf0
    • Data Governance Dev's avatar
      feat(web2): 身份证扫描 — 行级违规原因 + 字段元数据透传 + 轮询端点 · 2dda2414
      Data Governance Dev authored
      后端:
      - idcard_validator.py
        - 新增 EMPTY 校验项(值空/None 视为不合规)
        - 返回结构增加 reasons: dict[rule_id, str],每条违规带具体原因文本
        - 原因文本含「现场值」+「期望值」,方便前端直接展示
      - routes/idcard_scan.py
        - BadRow 增加 violation 字段,扫描时把 reasons 串成单行文本塞进每行
        - BadTable.rows_meta 把字段注释 + 数据类型带出来给前端表头 tooltip
        - ProgressTracker 加 _partial_tables + partial_snapshot()
        - 新增 GET /api/id-card/scan/{scan_id}/partial 端点(轮询用)
        - 旧 /stream SSE 端点保留但前端不再调用
      2dda2414
    • Data Governance Dev's avatar
      feat(web2): IdCardTool 进度条 UI + 异步扫描轮询 · c7fe08c5
      Data Governance Dev authored
      配合后端三端点改造前端:
      - POST 立即拿 scan_id → 不阻塞 UI
      - setInterval 800ms 轮询 /progress 更新 el-progress
      - status=done → 拉 /result → 渲染命中列清单 + 折叠问题表
      
      UI 改造:
      - 新增「扫描进度」卡片:旋转 Loading 图标 + el-progress 百分比 +
        done/total 计数 + 耗时 + 当前列名
      - 「重置」按钮同时停轮询、清进度状态
      
      onQuery 拆三个函数:
      - onQuery()      启动 + 立即首次轮询
      - pollProgress() 拉进度;status=done/error 自动收尾
      - fetchResult()  拉最终结果
      
      踩坑(production build 不会报 import 缺失):
      之前改 IdCardTool 加 computed() 时漏了 import 'computed',
      dev 模式 Vite 红屏能看出来,production build 只打包不校验,
      运行时 ReferenceError 只出现在浏览器 console,server log 完全没痕迹。
      这次提交补回 import。
      
      踩坑见 work-logs/2026-08-17.md 第七章。
      c7fe08c5
    • Data Governance Dev's avatar
      feat(web2): 身份证合规扫描 — 多线程 + 实时进度接口 · c4ed1b5c
      Data Governance Dev authored
      需求链路(按用户描述):
        1. 按当前数据库连接抽数据字典(复用 web.core.data_dict)
        2. 按「字段名 / 字段注释」关键字过滤命中列
        3. 每个命中列 SELECT DISTINCT → Python 校验(GB 11643-1999)
        4. 按违规值反查完整行(SELECT * WHERE col IN (...))
        5. 前端按表折叠渲染
      
      架构亮点 —— 三端点异步:
        POST   /api/id-card/scan                     → 立即 202 {scan_id}(不阻塞)
        GET    /api/id-card/scan/{id}/progress       → {done, total, current_column, status}
        GET    /api/id-card/scan/{id}/result         → 完整 IdCardScanResponse / 202 / 500
        进度共享一个 in-memory ProgressTracker(thread-safe,10min TTL)
      
      性能:
        ThreadPoolExecutor(max_workers=5) 并发跑每列
        每个 worker 自己开 DBConnection(oracledb/pymysql/dmPython 不能跨线程共享)
        实测 Oracle 84 列从单线程 221s → 多线程 115s(1.9x 加速)
      
      方言适配(按 web2 库三态):
        MySQL  → LIMIT %s,%s 占位符
        达梦   → LIMIT ?,? 占位符
        Oracle → 子查询 ROWNUM <= N,:1 :2 :3 numbered bind(11g 无 LIMIT;? 占位符会 ORA-01036)
      
      文件:
      - web2/backend/idcard_validator.py  新增
          GB 11643-1999 §6 加权和 + mod 11 映射 + 15 位老号升级 18 位
          违规规则:EMPTY / LENGTH / CHARSET / CHECK_DIGIT / ADDRESS_PREFIX /
                   BIRTH_DATE / ORDER_CODE
      - web2/backend/routes/idcard_scan.py 新增
          异步三端点 + ProgressTracker + ThreadPoolExecutor 5 worker
      - web2/src/config/idCardKeywords.js 新增
          默认关键字常量(前端离线兜底,与 web/configs/standards_match.yaml
          的 IND-001-a 保持一致)
          DEFAULT_NAME_KEYWORDS = [id_card, id_card_no, id_number, identity_card]
          DEFAULT_COMMENT_KEYWORDS = [身份证号]
      
      踩坑见 work-logs/2026-08-17.md 第三、五、六章。
      c4ed1b5c
    • Data Governance Dev's avatar
      fix(web2): 修 start.py GBK 启动崩溃 + app.py SPA fallback + LOG_DIR NameError · 97a82545
      Data Governance Dev authored
      背景(按踩坑时间线):
      1. start.py 用 print("✓ 关键依赖齐全"),Windows 默认 GBK console 抛 UnicodeEncodeError,
         整个启动流程在 09:45 死在第一行
      2. app.py main() 函数引用 LOG_DIR 但没 import,跑 main() 直接 NameError;
         之前走 uvicorn 启动不跑 main() 所以没暴露
      3. 浏览器访问 http://localhost:8766/#/id-card 时页面空白 —— FastAPI 对 SPA
         路由(hash 路由 path 是 /,history 路由会触发 404)没有兜底,Vue Router
         没机会接管
      
      改动:
      - start.py:入口处 sys.stdout.reconfigure(encoding='utf-8', errors='replace');
        ✓ → [OK],✗ → [FAIL](Windows GBK 永远安全)
      - app.py:同上加 stdout reconfigure;从 _logging 多 import LOG_DIR;
        加 @app.exception_handler(404) 做 SPA fallback:
          · /api/* 404 → JSON(前端 fetch 期待 JSON)
          · 其他路径 404 → dist/index.html(让 Vue Router 接管)
      
      顺手:
      - CLAUDE.md 加规则:「永远不要使用数据操作语句,本工具只做查询!」
      
      验证:
      - start.py 启动不再因为 ✓ 崩
      - GET /id-card 现在返回 200 + index.html(之前 404)
      - GET /api/nonexistent 仍返回 404 JSON
      
      踩坑见 work-logs/2026-08-17.md 第五、七章。
      97a82545