1. 20 Aug, 2026 6 commits
    • 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 22 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
    • Data Governance Dev's avatar
      docs: 简化 CLAUDE.md 移除已迁移到 README 的项目说明 · 8627b77b
      Data Governance Dev authored
      清理内容:
      - 移除项目目标 / 字段对象 / 大致流程等已过时的工作流描述
      - 移除 LLM 依赖矩阵(已分散到 web/README 等具体文件)
      - 移除「下一阶段」段落
      - 新增 web2 与 web 的关系说明(web/ 是参考实现,web2/ 是新开发)
      - 保留 4 条总体要求(记录工作 / 不自动提交 / 写清内容 / 记踩坑)
      8627b77b
    • Data Governance Dev's avatar
      feat(web2): 初始化前端 + 后端完整骨架 + 数据库连接 popover · 26704c67
      Data Governance Dev authored
      前端 (Vue 3.5 + Element Plus 2.9 + Vite 6, 端口 5174):
      - 脚手架: package.json / vite.config.js / index.html / main.js / App.vue
      - 路由 + 布局: router/index.js + layouts/MainLayout.vue + views/IdCardTool.vue
      - 状态: stores/useDbConnection.js (响应式单例 + localStorage 持久化)
      - UI: components/DbConnectionPopover.vue (数据库名 + 真实 logo + 测试/切换)
      - 配置: config/dbPresets.js (后端不通兜底) + config/dbBranding.js (logo/icon/颜色)
      
      后端 (FastAPI, 端口 8766):
      - 入口: app.py + _logging.py (idempotent logger + RotatingFileHandler)
      - API: routes/db.py (GET /api/db-defaults 读 web/configs/db_defaults.yaml,
        POST /api/connect/test 复用 web.core.db_adapter.test_connection)
      - 启动: start.py (依赖检查 + uvicorn) / backend/start.bat
      
      一键启动: web2/start.bat / web2/start.sh 同时拉起前后端两个窗口
      
      数据流:
      - 前端启动 → 异步 GET /api/db-defaults → 覆盖 presets (4s 超时 fallback)
      - 点击测试 → POST /api/connect/test → 后端复用 web.core.db_adapter
      
      安全策略:
      - dbPresets.js 中 password 字段留空(真实密码不入库)
      - 运行时由 GET /api/db-defaults 注入;后端不通时测试连接因密码空失败(预期)
      
      配套改动:
      - .gitignore: 忽略 web2/logs/ (start 脚本的 console 输出目录)
      26704c67
  3. 14 Aug, 2026 2 commits
    • Data Governance Dev's avatar
      refactor(web): 「字段长度检查」按字段类型拆 7 个独立 step + 3 层嵌套 · 53f6205d
      Data Governance Dev authored
      后端:
        - step6_length_check.py: LENGTH_RULES 扁平列表 → 7 个 _BUILTIN_CATEGORIES
          (len_id_card / len_uscc / len_mobile / len_xzqh / len_postal / len_email / len_bank_card)
          每个分类独立 rules + UI 元数据 + expected_length + standard
        - orchestrator.py: 单 length_check step → 7 个 len_* 独立 step
          (工厂函数 _run_length_check_category(cat_id));旧的 length_check 保留为 hidden
        - match_config.py: 新增 get_length_categories_rules(cat_id);旧 get_length_rules() 保留为兜底
        - standards_match.yaml: step6_length.rules → step6_length.categories.<cat_id>.rules
          每分类 1 个 name_pattern + 1 个 comment_keyword(区划留 2 个)
        - routes.py /api/analysis-tree: 加递归 3 层嵌套(group → subgroup → leaf)
          新增 is_subgroup: True 显式标记(避免靠 step_id 缺失推断)
          /api/match-config: 给每个 len_* 单独返回 names/comments 默认值
      
      前端:
        - analysis_tree.json: 「字段长度检查」从 leaf 改为 subgroup
          (key=length_check_group, 含 7 个 len_* 子 step)
        - index.html: flatCheckRows + flatResultRows 支持 3 层(_level 0/1/2)
          toggleGroupCheck / setAllChecks / setAllGroupsExpanded 改为递归处理子组叶子
          flatResultRows fallback 分支抽出 _push_leaf_row(item, level) 助手
        - style.css: 加 .tree-row--subgroup / .subgroup-title / .subgroup-count / .subgroup-description
      
      Bug fix:
        - step_id vs tab_key 混用导致 displayedTabs 过滤掉 7 个 length tab
          section_key='length_len_id_card' / tab_key='length_len_id_card' / step_id='len_id_card' 三者分离
          _wrap() 加 step_id 参数(默认沿用 tab_key,向后兼容旧 length_check)
      
      WORKLOG 记录所有改动 + 5 个踩坑(subgroup 识别 / YAML 关键词精简 / fallback 分支复用 /
      CSS .tree-row--subgroup / LengthRule.__dict__ 转 dict 抛错 / step_id 与 tab_key 混用)
      53f6205d
    • Data Governance Dev's avatar
      feat(standards): 细化违规原因 + 异常值字符位高亮 + tooltip 换行 + 合并 bug · 1bb3ca98
      Data Governance Dev authored
      合并两轮迭代的修改:
      
      1) 违规原因细化(来自「细化所有目前显示的分析对象的错误信息」)
         - 21 个 IND validator + step4/5/6 的 reason 加上:
           · 标准来源编号(GB/T 2260 / GB 11643-1999 / 工信部 / GB 32100-2015 等)
           · 常见错因 ①②③(末位错填 / 出生日期段 / 地址码舍 0 等)
           · 建议核对原始证件 / 用脚本批量重算
         - reason 字符串内含 \n 换行符(前后端一起生效)
      
      2) 异常值字符位高亮(用户截图反馈「异常值要指出哪里错了」)
         - standards/base.py:ValidationResult 新增 bad_positions 字段
         - 11 个 validator populate bad_positions:
           · IND-001/002/003/004/005/006 按字段类型填具体错位
           · IND-016 银行卡 Luhn 错:暴力枚举找出「单字符修正」位
         - web/core/step_impl/step7_standards.py:_build_value_highlighted
           · 区间合并(重叠/相邻)+ HTML escape → <mark class='bad-pos'>...
         - web/static/index.html:
           · .bad-pos CSS(淡红 + 红字 + 下划线 + cursor:help)
           · kind='code' 渲染分支 v-html='row.value_highlighted'
      
      3) tooltip 多行修复(用户截图反馈「[a] [b] [c] 三个子检查挤在一行」)
         - 上一轮 .el-popper { white-space: pre-line !important } 看着对实际不生效
           · Element Plus 2.x show-overflow-tooltip 内部 normalize 文本,外层 CSS 救不回来
         - 本轮改用自渲染路线:放弃 show-overflow-tooltip,用 <el-tooltip> + #content 槽
           · escapeTooltipHtml():HTML escape + \n → <br>
           · cell 内 text-overflow: ellipsis 保持单行布局
           · 没 \n 的列无感升级
      
      4) IND-003 / IND-004 合并步骤 TypeError 修复(用户截图反馈「有报错」)
         - 报错:TypeError: 'NoneType' object is not iterable
         - 根因:_merge_violations_by_tuple 里的死循环
           for p in (r.get('value_highlighted') and [])
           · 当 value_highlighted 为 None/空串时短路返回 None,
             for p in None 抛错
           · IND-003-b / IND-004-b 不填 bad_positions 时必触发
         - 修复:直接删这段死代码(loop 里只有 pass,真正 fallback 在下面)
      
      5) 其他
         - docs/WORKLOG.md:四段工作记录(细化 / 高亮 / tooltip / 合并 bug)
         - scripts/reformat_reasons.py:AST-based 批量加 \n 工具,留作下轮兜底
      
      未提交产物(生成报告 + tmp 脚本,不入库):
        - data_dictionary/standard_fields_violations_report.md
        - scripts/_tmp_build_violations_md.py
      
      踩坑见 WORKLOG:
        - .el-tooltip__popper vs .el-popper(Element Plus 2.14.3 选择器)
        - and [] 短路求值返回 None(应为 or [] 或干脆删除)
        - show-overflow-tooltip 不可控,normalize 文本外层 CSS 救不回来
      1bb3ca98
  4. 13 Aug, 2026 10 commits
    • Data Governance Dev's avatar
      feat(web): 数据库类型切换自动套用预设 + 杀 8765 脚本 · 5db40025
      Data Governance Dev authored
      ## db_defaults 三套化
      
      用户反馈:「@db_defaults.yaml 现在这个文件里面由三套数据库配置,
      现在页面要改成切换数据库类型同时也要切换默认IP,端口,账户密码和数据库」
      
      ### 改动
      - web/configs/db_defaults.yaml —— 旧单 dict → 新三键 dict
        (mysql / dameng / oracle 各一套,含 port 字段;文件已在 .gitignore 安全)
      - web/api/routes.py /api/db-defaults —— 返回三套 JSON;
        兼容旧格式单 dict(用 _flat 包裹,前端识别后只填 mysql)
      - web/static/index.html —— 加 dbDefaults ref + DB_FALLBACK_PRESETS 兜底
        + _applyPreset(t);onDbTypeChange 之前只切 port(保留用户自定义),
        现在无条件套用预设全部 6 个字段(host/port/user/password/database/oracle_client_dir);
        loadDbDefaults 先缓存三套再按当前 db_type 套用
      
      ## scripts/kill_web.{sh,bat}
      
      杀掉 web 服务(默认端口 8765,可自定义):
      - kill_web.sh —— Git Bash + Linux/macOS 通用(netstat.exe / lsof 自动分支)
      - kill_web.bat —— Windows 原生 cmd 兼容
      
      退出码:
        0 杀掉占用进程成功 / 端口空闲
        1 端口已空闲(无需处理)
        2 杀进程失败
      
      ## 验证
      - curl /api/db-defaults 返回三套预设(端口 3306/5236/1521)✓
      - kill_web.sh 空闲端口返 1 / 占用端口返 0(实测杀 18999 PID 23160)✓
      - 静态资源 200 ✓
      - JS 结构平衡 ✓
      
      ## 踩坑
      - API 旧格式兼容:YAML 老结构(单 dict 平铺)→ 检测 value 不是 dict 时用 _flat 包裹
      - oracle_client_dir 仅 Oracle 有效,三套配置统一保留 key 占位
      - password 空串语义:'password' in preset 判定(保持与之前一致)
      5db40025
    • Data Governance Dev's avatar
      refactor(web): 去掉「部分|全量」radio,所有检查都跑全量 · 98604b82
      Data Governance Dev authored
      用户反馈:partial/full 选项不再需要,所有检查(包括用户自定义规则)
      都跑全量扫描。
      
      改动:
      - 删除每条 IND leaf 行的 <el-radio-group class="sample-scope">
      - 删除自定义规则卡片 header 的 <el-radio-group>
      - 删除 sampleScope ref / SAMPLE_PARTIAL_LIMIT / buildSampleLimitsForSubmit()
      - 删除 watch(analysisTree.groups) 里初始化 sampleScope 的循环
      - startJob() payload 不再发 sample_limits 字段
      - 删除 setup return 中的 sampleScope 相关导出
      - 删除 .sample-scope / .sample-scope--leaf / .custom-rule-sample CSS
      - .tree-row--group .group-description max-width 从 280px 放宽到 560px
        (radio 让位消失后,description 可以更长)
      
      后端零改动:sample_limit 参数链(models → job_manager → orchestrator
      → step7/step9)保留。前端不发 sample_limits → 后端默认 None = 全量扫描。
      
      附带 UI 改进:进度区显示「失败 N 步(...)」行 + 进度条部分失败变
      warning + 状态 tag 显示「completed (N 失败)」,避免静默失败误导用户。
      (对应 step6 length_check TypeError 的 bug 修复,wrapper 签名
      修改在 3dfd038c commit 已包含)
      98604b82
    • Data Governance Dev's avatar
      feat(standards): 表注释关键词默认值 + IND-016 去掉 -b 后缀 · 3dfd038c
      Data Governance Dev authored
      本次会话累计改动(5 个文件):
      
      standards_match.yaml — 表注释匹配关键词
        - IND-001-a/b/c/d (身份证)        : comment_keywords 默认 [身份证, 证件号码]
        - IND-002-a/b/c (统一社会信用代码) : comment_keywords 默认 [信用代码]
        - IND-004-a/b (行政区划)          : comment_keywords 默认 [行政区划]
        - IND-013-a (UI 显示 IND-017 单位类型) : 单位类别 → 单位性质
        - IND-013-c (UI 显示 IND-019 行业代码) : 关键词精简到 3 个
                                                (去掉 行业分类 / 国民经济行业)
        - IND-016-b → IND-016             : YAML key 改名
      
      standards/ind_016b_bank_account_format.py — standard_id 同步
        - standard_id 'IND-016-b' → 'IND-016'
        - docstring 同步更新
      
      web/core/orchestrator.py — 描述字典 key 同步
        - 'IND-016-b' → 'IND-016'
      
      web/configs/analysis_tree.json — UI step_id 同步
        - 'std_ind_016_b' → 'std_ind_016'
      
      docs/WORKLOG.md — 记录改动 + 踩坑
        - 单位性质重复问题
        - IND-016 改名后 smoke 验证结果
        - 行业单字被移除风险
      
      注:standards_match.yaml / orchestrator.py 里还有其他未提交的 keyword 精简
      (如 IND-003-a / IND-005-a / IND-006-a / IND-013-b / IND-101 / IND-201 / IND-202
      / IND-401 / IND-402),看着是之前会话留下没提交的,本 commit 一并带走。
      3dfd038c
    • Data Governance Dev's avatar
      feat(web): 自定义规则卡加「开始分析」按钮(与配置卡互为冗余入口) · f0802827
      Data Governance Dev authored
      需求:分析配置卡折叠时不必再点开它启动分析 → 自定义规则卡也加一个同样的按钮。
      
      实现(web/static/index.html 卡 2.5):
      - 复用 startJob(),disabled/loading 逻辑与配置卡完全一致
      - 按钮放在 <div v-show="!customConfigCollapsed"> 外面 → 卡片折叠时按钮也可见
      - 同时附「取消任务」按钮(运行中显示)
      
      CSS(web/static/style.css):
      - 新增 .card-footer-action(margin-top: 18px, flex row, gap 8px)
        视觉对齐配置卡里 <el-form-item> 的默认间距
      f0802827
    • Data Governance Dev's avatar
      docs(worklog): 数据字典浏览器分页改造 + EP 滚动事件 bug 修复记录 · 1398b020
      Data Governance Dev authored
      包含两段工作:
      1. 2026-08-13 数据字典浏览器动态分页 + 后端缓存复用
         - 后端 3 个分页端点
         - 前端状态机重写(删 tableSummary/dataDict 全量 ref)
         - selectAllTables/Invert 改调 all-table-names
         - CSS 隐藏两边表格的全选 checkbox
      2. 2026-08-13 数据字典浏览器滚动到底不增量刷新(bug fix)
         - 现象:滚到底不拉第二页
         - 根因:EP 2.14.3 + height 时内部 <el-scrollbar> 包裹 body,
           scroll 不冒泡到 el-table 根
         - 修复:直接在 .el-scrollbar__wrap 上挂原生 scroll 监听
      1398b020
    • Data Governance Dev's avatar
      feat(web): 数据字典浏览器动态分页(滚动加载 + 筛选重读 + 隐藏全选) · a0b10c06
      Data Governance Dev authored
      状态机重构:
      - 删 tableSummary / dataDict 全量 ref + 4 个前端过滤 computed
      - 新增 tableList/fieldList + page/hasMore/total/loading 共 10 个 ref
      - 新增 fetchTablesPage/fetchFieldsPage(防 stale 响应 + nextTick 后重挂 scroll)
      - 新增 onTablesScroll/onFieldsScroll:EP 2.14.3 + height 时 scroll 不冒泡到 el-table 根,
        改在 .el-scrollbar__wrap 上挂原生 addEventListener('scroll', { passive: true })
      - 新增 rebindDictScroll:每次 fetch + nextTick 后重挂兜底(el-table 重建 wrap 时失效)
      - 筛选 commit 时从后端重读第 1 页(applyTableSearch/applyFieldSearch)
      - watch(form.tables) 触发 fetchFieldsPage(1, true)(勾选表变化自动更新字段表)
      
      跨页勾选同步:
      - onTableSelectionChange 改为 set 合并(visible 名 + 新选中 → add;visible - 未选中 → delete;
        不在 visible 的保留)—— 旧实现会丢跨页勾选
      - 新增 restoreTableSelection:数据刷新后把 el-table 可见勾选与 form.tables 对齐
        (reserve-selection 在 :data 整体替换时失效,必须手动同步)
      
      按钮重写:
      - 全选 / 反选:调 /api/dict/all-table-names 拿全量匹配名字 → 设 form.tables
        → fetchTablesPage(1, true) 把所有匹配表拉回首屏
      - 清空:form.tables = [] + clearSelection()
      
      CSS:web/static/style.css
      - .dict-tables-table thead .el-checkbox, .dict-fields-table thead .el-checkbox
        { visibility: hidden; pointer-events: none; }
        (display:none 会算错表头高度)
      
      addCustomRule:去除 tableSummary 兜底(后端 /api/dict/fields 每行附 table_comment)
      
      踩坑:
      - EP 2.14.3 + height → <el-scrollbar> 包裹 body → @scroll 不触发,必须原生监听
      - selection-change 只返 visible 选中行,直接赋值丢跨页勾选 → set 合并
      - reserve-selection 在 :data 整体替换时失效 → 自写 restoreTableSelection
      a0b10c06
    • Data Governance Dev's avatar
      feat(backend): /api/dict/{tables,fields,all-table-names} 分页端点 + cache 复用 · 6cb0ffa7
      Data Governance Dev authored
      DataDictCache 之前只在 connect/test 写入;这次新增 3 个 GET 端点让前端按需切片:
      - GET /api/dict/tables?db_type=&host=&port=&user=&database=&page=&page_size=&q=
        → 分页返回 table_summary,每项附加 field_count(前端不再算)
      - GET /api/dict/fields?...&tables=t1,t2&page=&page_size=&q=
        → 分页返回 data_dictionary(按 tables 参数过滤),每项附加 table_comment
      - GET /api/dict/all-table-names?...&q=
        → 仅返名字列表,供前端「全选 / 反选」按钮使用(不分页)
      
      实现要点:
      - 全部走 DataDictCache.get_instance().get(cfg),命中即 list[a:b] 切片(微秒级)
      - cache miss 时现场抽一次(缺 password 抛 warning 让前端提示重连)
      - 多连接并存按 5 元组 (db_type, host, port, database, user) 区分 key
      - 排序:tables 按 table_name 字典序;fields 按 (table_name, ordinal_position)
        与旧前端 filteredFields 排序保持一致
      
      踩坑:
      - port=3306 默认参数必须放最末位(Python SyntaxError)
      - list_dict_fields 重复定义 host 别名导致 FastAPI 参数解析歧义 → 简化为单一 host 参数
      - list_columns SQL 不带 table_comment,/api/dict/fields 响应里要手动从 table_summary 关联
      6cb0ffa7
    • Data Governance Dev's avatar
      fix(custom-rules): 自定义规则结果「规则」列显示可读 SQL(替代 'tree' 字面量) · 21170b4b
      Data Governance Dev authored
      用户反馈:自定义规则命中明细表里「规则」列只显示 'tree',没有信息量。
      
      ## 改动
      - step9_custom_rules.py: 新增 _serialize_rule_human(group) 递归把条件树
        序列化为可读 SQL 形态字符串
        - 叶子: col LIKE '%X%' / col NOT LIKE '%Y%' / col > 10 / col < '2026-01-01'
        - 顶层组: children 用 AND/OR 串接;单 child 不加括号
        - 嵌套组: (children) 加括号让优先级清晰
      - 每条 entry 增加 rule_text 字段(common dict);删除无意义的 rule_type 赋值
      - ColumnSpec: prop 'rule_type' → 'rule_text';render tag → code(等宽字体)
        ;min_width 120 → 240
      - step8_report._md_custom_value_check / docx 版同步:
        - 去掉「关键字」列(v1 字段,v2 已不存在)
        - 「规则」列改用 rule_text
      
      ## 端到端验证
      mock DB 跑一条含嵌套组的规则:
        输入: AND(contains(X), OR(contains(Y), not_contains(Z)))
        rule_text: 'col LIKE \'%X%\' AND (col LIKE \'%Y%\' OR col NOT LIKE \'%Z%\')'
        真实 SQL: '... WHERE (`id_card` LIKE %s ESCAPE \'!\' AND (`id_card` LIKE %s ESCAPE \'!\' OR `id_card` NOT LIKE %s ESCAPE \'!\')) LIMIT 100'
      显示与真实 SQL 语义一致;区别仅是显示用 'col' 占位列名(方便看),真实 SQL 用 quote_ident。
      
      ## 踩坑
      第一版递归在父循环对子组额外包括号 '(" + walk() + ")',而 walk(is_top=False)
      内部对嵌套组已经返回带括号形式 → 嵌套组变成 '((col < 5 OR col = 100))'。
      修法:用 is_top 参数让递归本身决定是否加括号,调用方不再重复加。
      教训:写递归字符串拼接时给一个 is_root / is_top 参数控制顶层 vs 嵌套行为。
      21170b4b
    • Data Governance Dev's avatar
      feat(governance): 抽样范围 partial/full radio(per-IND 独立控制) · 23ebacc6
      Data Governance Dev authored
      用户需求:针对特定字段的分析(国标字段规范、业务字段规范、用户自定义规则)
      希望可切换「部分(前 1000 行)抽样 / 全量扫描」,且每个分析项独立配置(A 项选部分,
      B 项可独立选全量)。
      
      ## 后端
      - 新增 web/core/sql_utils.py: apply_sample_limit(sql, sample_limit, db_type)
        - sample_limit=None  → 移除末尾 LIMIT N / AND ROWNUM <= N(全量扫描)
        - sample_limit=int   → 保留原样(partial 模式)
        - 三方言均覆盖:MySQL / 达梦(标准 LIMIT)/ Oracle 11g(AND ROWNUM <= N)
      - models.ConnectRequest 新增 sample_limits: dict[str, int | None]
      - job_manager.submit 透传 sample_limits 给 orchestrator
      - orchestrator.run_governance_workflow 新增 sample_limits kwarg;按 step_id 取值
        分发给各 _run_* 闭包
      - step7_standards._run_round1/2/3_* 全部加 sample_limit 参数:渲染 SQL 后用
        apply_sample_limit 二次处理(partial→加 LIMIT 1000,full→剥 LIMIT)
      - step9_custom_rules.run_step_custom_value_check 加 sample_limit 参数:
        SQL 构造按 None / oracle / else 三分支
      
      ## 前端
      - index.html sampleScope ref + SAMPLE_PARTIAL_LIMIT 常量
      - 每个 IND-XXX leaf 行(checkbox 之后、字段名输入框之前)加 partial/full radio
        - key = step_id(每个 IND 独立),不再按 group 共享
        - 标签去掉了「1000」后缀,仅显示「部分 / 全量」
      - 自定义规则卡片保持 card-level 单个 radio(共享 custom_value_check step_id):
        step9 后端所有规则共用一个 LIMIT,要下沉需另改接口
      - watch(analysisTree.groups) 加载后为每个 step_id 初始化 sampleScope='partial'
      - buildSampleLimitsForSubmit() 把 sampleScope 转成后端要的 {step_id: int|None}
      - startJob() payload 加 sample_limits 字段
      - style.css 新增 .sample-scope--leaf(margin-left: 8px,紧贴字段名输入框)
      
      ## 端到端验证
      - 用户在 IND-001/002/003 选全量、IND-004 选部分 → payload 正确:
        {std_ind_001: None, std_ind_002: None, std_ind_003: None, std_ind_004: 1000}
      - Pydantic ConnectRequest 解析通过;类型 [(str, 'NoneType'/'int'), ...]
      - 三方言 × partial/full × 多种 SQL 形态,行为符合预期
      
      ## 踩坑
      edit 工具改函数签名+docstring 时只换首行 → 留孤儿 """ → SyntaxError。
      教训:edit 改函数签名 + docstring 时,把整个 docstring 完整贴一遍。
      23ebacc6
    • Data Governance Dev's avatar
      docs(worklog): Oracle 自定义规则踩坑(ORA-01036/00933)+ 多 tab UX 改造 · 39f5a4fb
      Data Governance Dev authored
      新增 2026-08-13 条目,覆盖 6 块改动:
      
      1) ORA-01036: illegal variable name/number
         - sql_builder 历史 Oracle 路径输出 qmark ?,但 python-oracledb 不支持
         - Bind.next_idx 加 numbered :1/:2/... 占位符
      
      2) ORA-00933: SQL command not properly ended
         - step9 无条件 LIMIT 200,Oracle 11g 不支持
         - 截断子句分支:Oracle 走 WHERE ROWNUM <= N
      
      3) 中间 bug:Oracle 分支漏 WHERE 关键字
         - 静态验证三方言 SQL 时暴露,立即补 WHERE
         - 教训:跨方言分支必须同一组测试数据验证
      
      4) evalPathExpr 不支持 dict 索引
         - 设计 data.matches_rule_<idx> 扁平字段避开 this.x[key] 语法
         - 教训:协议路径表达式先看支持语法再设计数据结构
      
      5) flatResultRows 折叠表聚合踩坑
         - 顶层 summary 是 section 共享的,按 tab key 前缀识别 custom_value_check_<idx>
      
      6) UX:默认名「自定义规则N」
         - nextDefaultRuleName() 扫已用名,找最小可用 N
         - 后端兜底回落 <table>.<column>(不再用 (未命名))
      39f5a4fb