- 24 Aug, 2026 20 commits
-
-
Data Governance Dev authored
编辑弹窗打开时 tableOptions = [](用户没点过测试连接), el-select 渲染 label 时查不到匹配 entry,退化成只显示纯表名(GR_GRXX)。 form.tableComment 本身已经从 props.task.table_comment 回填了, 只是没注入到 tableOptions 里。修法:从 props.task 构造一条合成 entry 注入 tableOptions,点「测试连接」后 loadTables 会用真实列表覆盖, 不影响后续流程。
-
Data Governance Dev authored
默认配置下 server.host 不配 → vite 绑 localhost,LAN IP(如 192.168.x.x) 访问直接 ERR_CONNECTION_REFUSED。 加 host: '0.0.0.0' 监听所有网卡;第一次启动 Windows 防火墙会弹询问, 需要允许。
⚠ ️ dev server 现在暴露在局域网里,敏感环境慎用。 -
Data Governance Dev authored
原来两行 input/textarea 独占整行右侧,视觉上跟下面 Python 函数/正则行 (右侧带 复制 按钮)对不齐,弹窗右边空旷不对称。 按 .btn(padding 0 16px + 1px border + 14px 中文 2 字 ≈ 28px = 62px) 设 .rule-line-spacer 占位宽度,规则名 / 规则说明行也用 .rule-flex-line 包成 [input/textarea][62px 占位] 跟 .code-line / .test-line 同一套结构。 flex 子项显式 min-width: 0,覆盖全局 .text-input { min-width: 160px } 避免窄容器溢出。顺手给原裸 textarea 补 padding/border/radius/font 跟其他输入框视觉一致。 -
Data Governance Dev authored
1. MySQL 8.0 INFORMATION_SCHEMA.COLUMNS 没有 TABLE_COMMENT 列 - 原查询 SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.COLUMNS 直接报 1054 - 改成 LEFT JOIN INFORMATION_SCHEMA.TABLES 拿表注释 2. schema/table 名含 '-' 时 quote_ident 校验失败 - web3/backend/core/db_adapter.py::quote_ident 用 ^[A-Za-z_][A-Za-z0-9_]*$ 校验 - 但 MySQL 的 schema/table 名合法可含 '-', 只要用反引号包即可 - 仅在 skill 脚本 table_row_count() 里给 MySQL 分支走自定义 _safe_mysql_ident, 其他方言照旧用 quote_ident, 不动通用 db_adapter 逻辑 触发场景: 查 smart-build 库时直接报错; 修完后能正常输出 6 张含 credit_code/uscc 的表 附加: 踩坑已记录到 memory (mysql-information-schema-columns-no-table-comment) -
Data Governance Dev authored
任务分组应由后端 taskGroups 码表统一管理,弹窗里加 + 新建分组 会让用户随手造出码表外的分组,导致任务列表/侧边栏展示与码表对不上。 全文件 grep 无其他配套代码,直接删干净。
-
Data Governance Dev authored
LLM 审查结论为 match(代码正确实现规则)时,右栏的「
✅ 一致:代码正确 实现了规则」绿条不再展示 —— 既然一致,这条结论本身就成了冗余信息。 违规原因卡片继续展示,左栏的规则说明 + 代码也始终保留,方便用户对照。 -
Data Governance Dev authored
字段配置(FieldConfigDrawer)与任务编辑(TaskModal 默认展示列): - ↑↓ 按钮组前再加一个「⤒ 置顶」按钮,splice(idx, 1) + unshift 把对象字段直接搬到列表最前(非交换),首行禁用 - FieldConfigDrawer 三个按钮右对齐:把 .field-comment-icon 的 margin-left: auto 移除,让 .ord-group 独占推右责任,ⓘ icon 紧跟其后 ExplainIssueDialog LLM 弹框: - LLM 未按 JSON 输出时的重试说明(原独立 .note-block 灰底框) 嵌进违规原因卡片底部,跟 reason-text 一起被父容器滚动 - 虚线分隔 + 小字号 + 灰字,跟违规原因区分但不抢眼
-
Data Governance Dev authored
字段配置(FieldConfigDrawer + TaskModal 字段表)新增 ↑↓ 排序按钮, 调整字段在结果表里的展示列顺序: - FieldConfigDrawer:每项右侧加 ↑↓ 按钮组,icon 左边 - 把原来的 localChecked dict 改成 localItems 数组(同时管顺序 + 勾选) - 保存时 emit 完整字段顺序 + 仅勾选字段 - TaskModal:字段表去掉最左「排序」列,只在「默认展示」checkbox 左边留 ↑↓(统一入口,列宽 130 → 170px 给按钮腾空间) - 空数据行 colspan 同步 6 → 5 - ResultTable:allColumns computed 在 visibleFields 非空时按其顺序排 (FieldConfigDrawer ↑↓ 调整的结果),无 visibleFields 时回退原序 - 样式:.ord-btn / .ord-cell / .ord-group 抽到 global.css 初始上下排版,用户反馈丑 → 改为左右排版(flex-direction: row)
-
Data Governance Dev authored
现象 点「!」弹框左栏「规则说明」desc 很长的规则(如邮箱校验规则 8 / 200+ 字), 整段 desc 没有最大高度限制,把左栏撑爆,下面的「规则代码」标题 + code-block 被视觉截断;右侧的 pane-title-tag(规则类型胶囊)跟 desc-block 视觉重叠,UI 崩。 修复 src/components/ExplainIssueDialog.vue - .explain-dialog 加 min-height: 0 (flex 子容器需要 min-height: 0 才能让 overflow 真正生效; 否则 flex item 默认 min-height: auto,会按内容撑开无视 max-height) - .desc-block 加 max-height: 200px; overflow-y: auto 跟 .code-block 视觉统一;超长 desc 内部独立滚动,不再撑爆左栏 效果 - 规则说明超 200px → 内部独立滚动条 - 整个 dialog 高度超 84vh → .modal-body 兜底滚动 - 两层滚动不冲突(外层兜底,内层是单段 desc 限高) 验证 npx vite build → ✓ built in 6.80s 注 4 个 web3/tests/*.png modified 未提交(与本修复无关)
-
Data Governance Dev authored
动机 1) 每建一个新任务都要从零手写规则,重复劳动严重。 任务间的规则往往通用(手机号格式、身份证校验、年龄范围),希望能从已有任务复制。 2) 导入弹框只显示 ZJHM 这种英文列名,业务人员看不懂字段是啥意思。 需要在字段标题后补字段中文标签(数据库列注释)。 改动 src/components/ImportRulesDialog.vue (新) - 左右两栏布局(960px 宽): · 左列:所有非自身的任务列表(listTasks(),过滤掉 currentTaskId) · 右列:选中任务的所有字段+规则(getTask(id) 拿详情,按字段分组),每条规则 checkbox - 字段标题:field_key + field_comment 横排显示(comment 为空时「(无字段注释)」灰斜体兜底) - 顶部「全选 / 反选」;底部「已选 N 条 + 取消 + 导入 N 条」 src/components/RuleModal.vue - 在 + 添加规则 下方加「
📥 从其他任务导入规则」按钮(绿色边区分) - 新增 taskId prop + importOpen ref + onImportRules() - 去掉后端 id(避免保存时按 id 当 update 走错路径),用 Date.now()+count 生成临时占位 id - 清空 testVal/testResult(让用户在新字段下重新测试) src/components/TaskModal.vue - <RuleModal> 加 :task-id="props.task?.id || 0" backend/routers/tasks.py - FieldOut 加 comment: Optional[str] = None - 新增 _fetch_field_comments(t, conn):用任务自身的 conn_json 连一次数据源, 调 db_adapter.list_columns() 拿 {field_key: column_comment} · 连接信息不全 / 连不上 / 表不存在 → 降级返回空 dict,不报错(comment 是辅助信息) · 复用既有 db_adapter,无新依赖;schema/table_name 走绑定参数无 SQL 注入 - _row_to_out(include_field_list=True) 时拉一次 comment_map 注入每个 FieldOut 关键决策 - 后端主动拉 comment 而非前端:前端拿不到密码 - 失败不报错:comment 缺失不影响主流程 - 不持久化到 Field 表:跟 plan §4 决策一致——数据字典不存,每次按 task 重新拉 Bug 修复 第一次实现时 computed 返 {fieldKey, fieldComment, rules},但模板里写了 v-if="group.comment", comment 没定义 → 永远 falsy → 显示「(无字段注释」兜底。不报错只静默渲染空。 改成 v-if="group.fieldComment"。已记到 memory(vue-computed-template-prop-name-mismatch)。 验证 - 后端 TestClient: GET /api/tasks/24 → field_list[ZJHM].comment = '证件号码 | 数据标准: 01010007' ✓ - npx vite build → ✓ built in 7.09s(1640 modules,无报错) 注 - 4 个 web3/tests/*.png modified 未提交(与本功能无关) - 后端有改动需用户重启进程 -
Data Governance Dev authored
动机 用户在结果表看到红 ! 标记的不合规行时,只能从 tooltip 看违反哪条规则, 但不知道为什么错、这条规则的代码是否真的实现了规则描述。需要点 ! 直接问 LLM,让模型同时审计数据违规原因和 规则 vs 代码 一致性。 同一字段常违反多条规则(如「11 位数字」+「号段分配」),需要开多个 tab 并行诊断。 改动 backend - core/ai_explain.py (新): explain_issue() 拼 prompt 调 LLM,解析 JSON, 失败时附带上次原文 + 修复提示自动重试,最多 3 次;JSON 解析失败不抛异常, 降级返回原文 + consistency='unknown' - routers/ai.py: 新增 POST /api/ai/explain_issue 端点 (ExplainIssueRequest/ExplainIssueResponse Pydantic) - routers/queries.py: _evaluate_row() 的 issues 字典追加 rule_id - core/session_manager.py: CompiledFieldRules.rules 快照元组 6 元→7 元 (追加 rule_id),让 _evaluate_row 能拿到规则 id frontend - components/ExplainIssueDialog.vue (新): Teleport + 自定义 .modal 模式 · ctx-strip 顶部展示字段名 + 原始值(所有 tab 共享) · 多 tab 行:每条违规规则一个 tab,打开即 Promise.allSettled 并行调 LLM · 每个 tab 独立 loading/result/error 状态(互不干扰) · tab 标题旁小圆点:加载中蓝色脉冲 / 失败红
⚠ / 完成按 consistency 染色(绿✓ / 红! / 黄? / 灰·) · 单 issue 时隐藏 tab 行 · 「重新分析」只重跑当前 active tab - components/ResultTable.vue: .err-flag 加 cursor:pointer + @click.stop, emitExplainIssue() 把该字段所有命中的 issue 都 enrich 后塞进 issues[] 一起 emit(之前只取第一条,第二条违规原因被忽略) - views/DataQualityView.vue: 监听 explain-issue → 打开 ExplainIssueDialog - api/ai.js: explainIssue(payload) 导出 LLM Prompt 关键设计 - 系统提示强调「先想清楚再用严格 JSON 输出」 - 用户提示拆任务 1(违规原因)+ 任务 2(一致性审查), 加 ASCII 双引号避坑(历史踩坑:value 里用裸 " 会让 json.loads 中断) - 一致性三档:match / code_bug / rule_ambiguous 关键决策 - Promise.allSettled 而非 Promise.all:单条 LLM 失败不应拖垮其他 tab - LLMUnavailable 不重试(无 key / 网络挂 / 限流):立即降级返回 - 重试时附上次原文:让 LLM 看到自己刚才错在哪,比纯说「修一下 JSON」有效 - 「重新分析当前规则」而非「全部重试」:定位精准,省 token E2E 验证 - regex 类型:consistency='code_bug'(LLM 正确识别硬性限制年份的 bug) - number 类型:consistency='rule_ambiguous'(LLM 指出「0-150 是否合理」需业务再斟酌) - npx vite build → ✓ built in 10.92s(1638 modules transformed,无报错) 注 - work-logs/2026-08-24.md 已记录三轮迭代详情 + 踩坑 - 另有 4 个 web3/tests/*.png modified 未提交(与本功能无关) -
Data Governance Dev authored
用户反馈:邮箱分组跑完后切别的分组能看到老结果 → 上一节已修; 再继续叠加"in-flight 期间切分组→ 切回来自动续跑"方案后又出新 bug (resultFields 与 rows/lastResult 串数据),放弃了复杂的续跑逻辑。 简化方案:watch(type) 头部拦截 runningQuery=true, 弹 ElMessage.warning + router.replace 回原 type,不做切。 - typeReverting 旗子防 router.replace 触发的二次 watch 循环 - 跑完后的 cacheByType / applySnapshot 保留(切走再切回仍能看历史结果) - continueQuery + takeSnapshot.inFlight 元数据暂留(未来如恢复续跑逻辑可复用) - build: ✓ built in 6.81s
-
Data Governance Dev authored
现象:编辑 Oracle 任务,「数据连接」Tab 的 Schema/数据表 下拉空白显示 「请先测试连接」。连接字段正确回填,但 schema/table 区域像新建任务一样 要重新选。 根因:watch(open) 编辑分支同步执行 form.dbType = task.db_type(即把 默认值 'MySQL' 改成 'Oracle')后,Vue 把 watch(form.dbType) 排进 microtask; 同步代码继续把 form.schema / form.table 从 task 回填完毕。等当前 microtask 跑完,watch(form.dbType) 才执行函数体,里面 '切方言就清 schema/table' 的 清理逻辑把刚回填的值一并清空。MySQL 任务也有这个 bug,只是 schema 通常 等于 db,看起来症状没那么明显。 修法(方案 A): - 加 isInitializing ref 作为初始化旗子 - watch(open) 入口 isInitializing=true,结束后 await nextTick() × 2 + false - watch(form.dbType) 头部 if (isInitializing.value) return 用户主动切 dbType / applyPreset() 行为不变(旗子默认 false,watcher 正常清理)。 验证: - npx vite build → ✓ built in 7.00s - 编辑 Oracle 任务 → Schema/数据表 自动回填,不再显示「请先测试连接」 - 新建任务切 dbType / applyPreset() 行为不变
-
Data Governance Dev authored
新建 .claude/skills/find-field/: - SKILL.md:调用方式 / 参数 / 输出格式 / 6 条踩坑 / 安全约束 - scripts/find_field.py:从 web3 connection_preset 读连接,三方言 (MySQL/达梦/Oracle) 扫列名 + COUNT(*) + USER_DEPENDENCIES 溯源 V_* 视图 安全: - 数据库连接只能选 web3 已存的(13 个预设),不允许临时输入密码 - 只读 SELECT,不发 DML(CLAUDE.md 原则) 附带 web3/tests/_probe_gjj80_bankcard.py: GJJ80 一次性探测脚本,作为本次 skill 命名的原型被保留作历史对照。 SKILL.md 里点名「已被本 skill 取代」,但保留 git 历史。 验证:find-field huanggang-gjj 银行卡号 → 与一次性脚本结果完全一致 (V_JCFX_DWXX 11768 + V_JCFX_GRXX 567523 + 基表 GJ_DWXX/GJ_GRXX)
-
Data Governance Dev authored
- src/components/RuleModal.vue:248 addRule() 把 skip_null 默认值 false→true - 编辑/复制老规则不走 addRule(),skip_null 仍按 r.skip_null 实际值显示 - 保存链路 onSave 仍透传 !!rule.skip_null,不强制覆盖存量数据 业务上「空值不计入不合规」更符合直觉,省得用户每加一条新规则手动勾。
-
Data Governance Dev authored
页面上数据明细的红格 + hover tooltip 在 Excel 里丢了, 业务人员拿到的导出文件看不到「哪几格不合规 / 为什么不合规」, 还得回系统查。 src/utils/excel.js 加 applyErrorHighlight(): - 数据行起始偏移 = 2(前两行是注释行 + 字段名行) - row.errorCells.includes(cols[C].key) → 给那个 cell 加浅红底 + 写 Excel Note - 浅红 #FEF0F0 + 红字 #F56C6C,跟页面上 .cell-error 同色 - Note 内容每行一条 issue:规则名:desc(name 空时回退 desc) —— 跟 ResultTable.errTip 同款格式;Note 空间够,把 desc 详细全展开 - 作者标「数据治理工具」方便业务识别 - 调用方零改动:errorCells/issues 已在 DataQualityView 透传,excel.js 直接读 - 不传 errorCells/issues 的复用场景安全:length===0 直接 continue 跳过
-
Data Governance Dev authored
errTip() 改成 .filter() + join(';') 后,格式是 name(desc), 但 desc 太长(动辄一二百字),在小 tooltip 里展开反而看不清是哪几条规则。 cell tooltip 只列违规的规则名(name),desc 不再展开; 表头 ruleHint 保持 name(desc)不变,那里用户需要看规则定义。 效果对比: - 改前:11位数字(11位数字);首位两位数字规则(第 1 位:固定为 1 ...) - 改后:11位数字;首位两位数字规则 老规则(name 为空串)回退到 desc。 -
Data Governance Dev authored
字段原本有规则,点「规则设置」进 RuleModal 把所有规则删完, 「保存」按钮被 length>0 限制变灰卡死。 语义上用户明确点删除就是想清空,字段应该回到「无规则」状态; 后端 _replace_fields 收到空 f.rules 也只是不写任何 Rule,本来就支持。 去掉 length>0 && 这一截。Array.every() 对空数组天然返回 true (vacuous truth),删空所有规则后保存按钮就亮起来。
-
Data Governance Dev authored
数据明细(不合规行)当前只展示「哪个字段不合规」,没有「哪条规则被违反」。 desc 是给 LLM 看的自然语言(含「必须 18 位」指令式描述), 不适合直接给用户看。加独立字段 name 给前端展示,desc 继续喂 LLM。 【schema 变更】 - rule.name TEXT NOT NULL DEFAULT ''(老规则保持空串,迁移不破坏存量) - _migrate_rule_name():ALTER TABLE ADD COLUMN name(幂等,123 条老规则验证保留) 【API 校验】 - RulePayload.name: str = PydField(..., min_length=1) —— 必填 - RuleOut.name: str = "" —— 老规则空串兜底 【检测引擎链路】 - CompiledFieldRules.rules 5 元 → 6 元(追加 name) - _load_task_compiled snapshot / rule_list 都带 name - _evaluate_row 返回 issues: {field, name, desc} → bad_rows 透传到前端「数据明细」备注列 【前端 UI】 - RuleModal:在「规则说明」上方加「规则名称」输入框(红星必填) - hasValidRules 加 name.trim() 校验 - onSave 序列化带 name - namePlaceholder 按 rule_type 给不同示例 - scoped .required 红星样式(不污染全局) - ResultTable:ruleHint / errTip / 备注列 issues 优先展示 name - FieldConfigDrawer:ruleHint 同步升级「name(desc)」格式 【兼容策略】 - 老规则 name=空串,UI 三处自动回退 desc 显示 - 编辑老规则保存时强制补 name(避免历史脏数据持续累积) 【测试】 - test_rule_types_e2e 6 处 rule payload 补 name(Pydantic 必填后端会拒 422) - pytest tests/ → 123 passed - vite build → ✓ built in 9.64s - 冒烟:init_db() 对线上 DB 跑迁移,老规则全保留默认 '' -
Data Governance Dev authored
数据连接 Tab 点测试后,提示文本把按钮挤到 ~30px, 「测试连接」四字竖排(测/试/连/接 各一行)。 根因: - .conn-form-grid 给所有 input 行加了 max-width:150px, 但 selector 没排除 .form-row.full,「测试连接」行 是 .full(横跨 3 列 = 720px),里面的 .form-control 却被锁死 150px - 通用 .btn 没有 flex-shrink:0,被 flex 父挤压 修两处: - .conn-form-grid > .form-row:not(.full) .form-control 限宽 selector 收窄到非 full 行,6 个 input 行照旧, full 行放开用满 720px - 通用 .btn 加 flex-shrink:0 + white-space:nowrap, 顺手防御其他按钮同类问题
-
- 21 Aug, 2026 20 commits
-
-
Data Governance Dev authored
结果表表头已经显示规则数徽章 + tooltip 列规则 desc,但字段配置面板(drawer) 只显示字段名 + 注释图标 —— 用户看不到「这个字段有几条规则」。 复用 f.rules / f.ruleList(DataQualityView 在 resultFields 已经塞了, 不用改父组件),加 el-tooltip +
⚠ N规则 徽章。 布局:rule-mark 插在 .en 后、comment icon 前; .comment-icon { margin-left: auto } 推最右,所以 - 有规则 + 有 comment:cn/en/rule-mark 靠左,icon 推右 - 有规则 + 无 comment:cn/en/rule-mark 自然铺开 - 无规则 + 有 comment:cn/en + icon 推右(不变) - 无规则 + 无 comment:cn/en(不变) scoped CSS .rule-mark:黄橙色 #e6a23c + help 光标, 跟 ResultTable 表头 rule-mark 视觉一致。 -
Data Governance Dev authored
用户反馈:AI 生成 JSON 校验函数,运行时报 ImportError: 'json' is not allowed。 原白名单只有 6 个模块(datetime/time/math/decimal/re/_strptime),太窄。 设计准则:「只能改内存里的 Python 对象,不能碰进程外的东西」—— 纯计算 / 数据结构类库一律放;I/O、网络、进程、动态执行类一律不放。 白名单(31 个): 数据 / 时间 : datetime, time 数学 / 数值 : math, decimal, fractions, statistics, numbers, operator 字符串 / 编码: re, string, unicodedata, html 数据结构 : collections, collections.abc, itertools, functools 序列化 / 二进制: json, csv, struct, array 编码 / 哈希 : hashlib, base64, binascii 实用工具 : uuid, enum, dataclasses, typing, difflib 其他 : copy, pprint, reprlib, ipaddress 故意不放:os/sys/pathlib/subprocess/pickle/ctypes/importlib/socket/... 连 urllib.parse 也不放(import urllib 后 urllib.request 属性访问能拿到,副作用太大)。 沙箱三件套(踩坑): 1. builtin 白名单加 __build_class__ —— class X: / enum.Enum / @dataclasses.dataclass 都靠它; 动态 type('X', (), {}) 走 type.__call__ 不经过它,所以风险 ≈ 0 2. namespace 预填 __name__ / __qualname__ —— enum/dataclass metaclass 会查 3. __name__ 必须用 '__main__' 不能用假名 —— dataclasses._is_type 内部 sys.modules.get(cls.__module__).__dict__,假名返 None 然后 .__dict__ 报错 另: - ai_rule.py 的 number / date / string prompt 都加上「沙箱已预注入的常用模块」清单 - RuleModal.vue 的 string rule 提示区也列已预注入模块清单 - test_rule_runner.py 新增 TestSandboxImports 类,46 个用例 (26 个模块 smoke + 3 个 import 形式 + 16 个危险模块被拦 + 1 个 urllib 被拦) 验证: python -m pytest web3/tests/ → 123 passed, 3 warnings npx vite build → ✓ built in 6.84s -
Data Governance Dev authored
基础信息 tab: - 删除「用户在查询下拉框看到的名称,建议格式:校验类型_表名」提示文字 - form-grid 加 .basic-info-grid class + CSS 让前两个 form-row(任务名称 / 任务分组) 各占 grid 一列水平对齐;任务说明仍 .full 占整行 数据连接 tab 「连接方式」section: - form-grid 加 .conn-form-grid class + CSS 改 3 列布局 + 固定列宽 240px - .form-control { max-width: 150px } 缩 input 容器 —— 只在 conn-form-grid 作用域下生效, 不污染全局 .text-input width:100%(避免影响其他弹框 FieldConfigDrawer / RuleModal 等) - 6 个 input 紧凑靠左排列,不再撑满 modal 数据连接 tab 「数据表」section: - 删除「选择数据表后自动加载该表全部字段」提示文字 - loading 提示「正在加载表列表...」保留 -
Data Governance Dev authored
字符串规则 (rule_type='string'): - RULE_TYPES 扩展;rule/code 列与 number/date 共用,存 Python def check(value) -> bool - ai_rule.py 加 _STRING prompt 模板(字符串语义:len/startswith/re) - RuleModal tab 加「字符串」+ desc/codePlaceholder/aiBtnLabel 分支 + Python 函数 hint 提示 value 是字符串 跳过空值开关 (skip_null): - rule 表加 skip_null 列(INTEGER NOT NULL DEFAULT 0)+ 迁移函数 - run_rule() 入口短路 None/空 str/空 bytes(regex 在 _evaluate_row 内联短路) - RulePayload/RuleOut + _replace/_copy/_row_to_out + _load_task_compiled 全链路透传 - RuleModal 「规则类型」行右侧加 checkbox;测试/保存透传 skip_null - /api/rule/test 同步支持(用户在 RuleModal 测试空值时也走短路) UI 修复: - 「规则类型」行用嵌套 modifier .rule-type-row flex 化,不污染其他 grid cell 行 (之前全局改 .rule-item-ctrl flex 把 .code-line/.test-line/.regex-line 全压塌) 测试: - test_rule_types_e2e.py 加 4 个新用例(string 持久化 + dispatch + skip_null=False/True + regex skip_null) - FAKE_TABLE_ROWS 加 2 行空值('' + None),覆盖 skip_null 场景 - test_rule_runner.py RULE_TYPES 常量断言更新 后端 77 passed;vite build clean
-
Data Governance Dev authored
1. TaskModal.vue form 加 tableComment 字段 watch(form.table) 从 tableOptions 找对应项的 table_comment 写入 编辑场景从 props.task.table_comment 回填 payload 加 table_comment: form.tableComment || null 选表时存一次到 task,列表直接读,零 N+1 查询 2. TaskConfigView.vue 模板加 <th>表注释</th>(位置在数据表后) colspan 8 → 9 td 渲染 {{ row.table_comment || '—' }} 3. 后端 commit 已加 task.table_comment 列 + 字段流通(commit 3) 老任务没存过 → 列表显示「—」,重新编辑任务选一次表就有 -
Data Governance Dev authored
两个独立功能(同一个表的两列)一起做,因为迁移逻辑都走 init_db 兜底: 1. task.checked_at: TIMESTAMP NULL schema.sql / Task ORM 加列 _migrate_task_checked_at() 在 init_db 里兜底 routers/queries.py::start_query 末尾:sm_put 后写 task.checked_at = utc_now try/except + rollback + warning:元数据写失败不能阻塞查询主流程 时刻选 /start(不是最后一页 /page):中断也算「最近关注过」 2. task.table_comment: TEXT NULL schema.sql / Task ORM 加列 _migrate_task_table_comment() 兜底 routers/tasks.py: TaskBase/Update/Out 加字段 + _row_to_out + create_task 写入 update_task 的 setattr 循环自动覆盖 list_tasks 接口直接读这列返前端,零查询开销 3. CLAUDE.md「只做查询」边界 这里写 task 元数据,不是业务数据修改 业务表(field/rule/connection_preset)一律不 UPDATE 4. 前端 TaskConfigView 现在用 row.table_comment 还没显示(commit 4 才加列) formatCheckedAt 已经备好(commit 2),checked_at 写完即可生效
-
Data Governance Dev authored
1. 分组过滤没生效 <select v-model=groupFilter> 只绑了 model,没 watch 触发 load 选了「手机号」不点「查询」list 不刷新 加 watch(groupFilter, () => load()) 自动 reload keyword 保留「回车 + 查询按钮」(避免每个 keystroke 都请求) 2. formatCheckedAt:把 ISO 字符串转成 'YYYY-MM-DD HH:mm' 后端已返 checked_at(先 hardcoded None,下个 commit 接上) —— format 函数先备好,避免后续加 checked_at 字段后还要再改前端 3. import 加 watch
-
Data Governance Dev authored
web3 ResultTable 是自定义 table,el-table resizable 不能直接复用,自实现: 1. ResultTable.vue columnWidths reactive + startResize/onResizeMove/endResize 拖拽状态不放 reactive(频繁写不触发响应式刷新整张表格) window mousemove/mouseup(用户拖快会脱出 th 丢失 mouseup) 拖拽过程 document.body user-select=none + cursor col-resize onBeforeUnmount 清理监听 + 还原 body 样式 2. global.css .resize-handle 样式(透明 + hover 变蓝 409eff) table.dq-table 加 table-layout: fixed(auto 时 width 被无视) thead th 加 position: relative 让 handle absolute 定位 3. 踩坑:改 Vue setup 的 import 段要核对所有 export 符号 我改 import 时删了 watch 符号,但 setup 里 line 248 的 watch() 还在用 → 运行时 ReferenceError: watch is not defined 把页面打白 4. localStorage 列宽持久化没做(用户没要求;后续可加 key→width 存)
-
Data Governance Dev authored
1. DataQualityView 按钮区只留「字段配置 + 导出 Excel」 删导入(btn-success)/批量删除(btn-danger)+ 对应的 mock 函数 导出按钮加 :disabled(rows 为空或导出中禁用) 2. 新增 src/utils/excel.js 从 web2 IdCardTable.vue 抽 exportExcel + applyAllBorders + applyHeaderStyle 封装成纯函数 exportToExcel({ rows, columns, filename, sheetName, confirm }) 三行表头:comment / cn / 数据;冻结前 2 行 + 黑色细边框 + 加粗灰背景 columns 兼容 {key,cn,comment}[] 和 string[](fallback) 大于 5000 行走 confirm(n) 二次确认(弹窗 UI 在调用方做) 3. onExport 列范围 visibleFields 非空 → 用户勾选;为空(未保存过)→ 退化为 resultFields 全量 过滤掉 __reason(ResultTable 内部追加的备注列) 文件名 <任务名>_<时间戳>.xlsx 防多次导出覆盖 4. +xlsx-js-style 依赖(跟 web2 同库,导出样式一致)⚠ bundle 多约 700KB(xlsx-js-style 是胖依赖,build 已超 500KB 阈值) 后续可改 dynamic import(点导出按钮时再加载),本次没做 -
Data Governance Dev authored
1. FieldConfigDrawer:cn 后面加 el-tooltip 包裹的 InfoFilled 图标 仅 f.comment 非空时显示,没注释的字段不出 icon(避免噪声) 2. global.css:.field-comment-icon 样式 margin-left: auto 推到 flex 容器最右(关键,cn/en 用了 gap:8px) 平时淡灰 #c0c4cc,整行 hover 时变蓝 #409eff + cursor: help
-
Data Governance Dev authored
1. ResultTable:自定义分页器换成 el-pagination(带 ... 折叠中间页) pageSizes 默认 [10, 25, 50, 100, 200]、defaultPageSize=10 加 visibleFields prop + allColumns 优先按它过滤(FieldConfigDrawer 联动) 表格下方加 padding 让分页器不贴表格 2. DataQualityView:字段配置抽屉改从 resultFields(后端 /start 返回的真字段)取 visibleFields 默认按 show_default=true 重置勾选;用户手动保存后写入 3. TDZ 修复:fields/visibleFields/watch 块挪到 resultFields ref 声明之后 computed + watch immediate 在 setup 立即跑;const ref 在它前面会 TDZ 报错 顺手清掉不用的 fieldsByType / violationRows import
-
Data Governance Dev authored
用户反馈:达梦 HGWXZJ 库的 YW_GJY(归集业务数据表)在「校验规则」Tab 每个字段 都显示两遍(ID/ID、DWZH/DWZH、DWZJID/DWZJID、...),内容完全一致。 根因推断: 达梦的 ALL_COL_COMMENTS 里同一 (OWNER, TABLE_NAME, COLUMN_NAME) 偶尔有 2+ 行 (历史 COMMENT ON COLUMN 重复 / DBA vs USER 视角双份 / 升级残留), LEFT JOIN 不去重 → ALL_TAB_COLUMNS 的每行匹配 2 行 → 整段 SQL 每列翻倍。 web2 模板跟 web3 一致,理论上同样问题;web3 之前没碰到是因为 web2 没做过 编辑场景自动 listColumns 的 UI 入口(手工流程绕开了)。 改动(dameng + oracle 同源一起改): LEFT JOIN ALL_COL_COMMENTS 改为 ROW_NUMBER() 子查询预聚合, PARTITION BY (OWNER, TABLE_NAME, COLUMN_NAME) 每组取一条, ORDER BY COMMENTS DESC NULLS LAST(启发式:取最长注释当用户最后写的)。 MySQL 模板不动(COLUMN_COMMENT 在 INFORMATION_SCHEMA.COLUMNS 自带, 不在另一张表里,无 1:N 风险)。 无单测(数据字典视图行为依达梦/Oracle 版本而异,fixture 不可靠); 让用户在达梦 disql 跑 SELECT ... GROUP BY ... HAVING COUNT(*) > 1 验证 ALL_COL_COMMENTS 本身 是否真有重复行。
-
Data Governance Dev authored
T4 任务(task_id=16,GJ_DWRYBD 表,39 字段全是大写 YWBH/FCRS/...)跑完查询, 前端表格所有单元格都显示 (NULL),但备注列能正确显示「fcrs: 100到200之间」。 根因(列名大小写不匹配): - 后端 db_adapter._rows_as_dicts 全局把列名归一为小写 (normalize_column_name line 188) - row dict 的 key 全是小写(ywbh / fcrs / ...) - 后端 _evaluate_row 用 cf.field_key = f.field_key.lower() 访问, 所以规则匹配正常(备注列能正确显示就是证据) - 但前端 resultFields.key 用了 f.field_key 原值(大写 FCRS) - ResultTable 里 row[col.key] → row['FCRS'] → undefined → (NULL) 两处表现是同一个根因: - 单元格全 (NULL):row[col.key] 大写找不到 - 错误 tooltip 永远 fallback「该字段不合法」: 原版 errTip 用 col.cn(大写)找 row.issues[].field(小写), 永远匹配不上 改动: DataQualityView.vue resultFields 里: key: (f.field_key || '').toLowerCase() // 跟 row dict 小写对齐 cn: f.field_key // 表头显示保留原大小写 ResultTable.vue errTip: 直接用 col.key(小写)找 row.issues[].field(小写), 去掉 col.cn 那一跳 教训:「DB 列名大小写 × 字段名大小写 × 后端归一化策略」是反复出现的坑。 db_adapter 全局归一为小写是契约,前端落 key 时必须主动 .toLowerCase()。 -
Data Governance Dev authored
用户反馈:T4 跑了几页后点「中断」,结果表里已经加载到的不合规行全没了。 上一版 onCancel 调 resetQueryStatus(),它会把 rows / lastResult / resultFields / queryError 全清空 —— 等于把用户已经看到的命中行擦掉。 新增 clearProgressOnly(): 只清进度相关(sessionId / totalPages / totalRows / scannedPages / scannedRows / receivedPages),保留 rows / lastResult / resultFields onCancel 改用 clearProgressOnly: - 用户已看到的不合规行留住 - 顶部 toast:「已中断(已扫 N 页,命中 K 条)」 - 再次点「查询」会走完整 onQuery,自然用新结果覆盖 教训:中断 ≠ 重查。reserve 用户可见状态是默认行为
-
Data Governance Dev authored
把单次同步 runQuery 替换成分页 + 并发 worker 模式: DataQualityView.vue 重写查询逻辑: onQuery() 1) POST /start → COUNT + 编译规则 + 拿 session_id + total_pages 2) PAGE_CONCURRENCY=3 个 worker 并发抢 queue([1..total_pages]), 每个 worker /page 拉到 → appendPage() 按页号升序拼进 rows 3) 收尾聚合 lastResult + toast onCancel() - /cancel 摘 session(best-effort) - AbortController.abort() 让在飞的 fetch 立即 reject - resetQueryStatus() 清空 sessionId / rows / lastResult / queryError 进度展示:task-info-bar 顶部「查询中 N/M 页 · 命中 K 条」 onBeforeUnmount:cancel + abort,避免组件销毁后 fetch 还在飞 ResultTable.vue 新增 appendMode prop: watch(() => props.rows.length, () => { if (!props.appendMode) currentPage.value = 1 }) 默认 false 保持原行为(rows 替换 → 回第 1 页); append 模式下 rows 增量追加不重置页码 ResultTable 新增 row-key 模式:父组件传 rowKey 函数, 用后端注入的 __row_index 做稳定 key(append 模式下保证 Vue DOM 复用正确) PAGE_SIZE = 500(与后端默认对齐) 无单测(前端无测试基础设施,手动 e2e 验证见 work-logs/2026-08-21.md) -
Data Governance Dev authored
client.js: request() 第三个参数 { signal } 透传给 fetch; http.{get,post,put,patch,delete} 都接受可选 signal。 queries.js: 删 runQuery,新增三个: - startQuery(taskId, { pageSize, signal }) → /api/queries/start - fetchPage(sessionId, pageNo, { signal }) → /api/queries/page - cancelQuery(sessionId, { signal }) → /api/queries/cancel signal 是 AbortController.signal,配合 orchestrator 实现: 1. fetch 主动 reject(节省带宽) 2. 后端 /cancel 摘掉 session(后续 /page 立刻 404 短路) 无单测(纯 plumbing,配合 commit 1 后端测试覆盖)。 -
Data Governance Dev authored
把原来 /api/queries/run 单次同步全表扫描改成 3 个端点: POST /api/queries/start COUNT + 编译规则 + 建 session(UUID) POST /api/queries/page 按页号拉一页,跑规则,只返回不合规的行 POST /api/queries/cancel 摘掉 session(best-effort 中断) session_manager.py 用模块级 dict + Lock 存 QuerySession,TTL 30min 懒清理。 每页新开 DBConnection → fetchall(避开 cursor 生命周期管理),~50ms 重连可接受。 dialect 兼容: mysql : LIMIT <n> OFFSET <m> dameng : OFFSET <m> ROWS FETCH NEXT <n> ROWS ONLY (SQL:2008) oracle : ROWNUM 三层嵌套(兼容 11g/10g;T4 任务实测 11g 服务端用 OFFSET/FETCH NEXT 报 ORA-00933) postgres / sqlserver : 同 dameng 前端友好的关键点: - /start 一次返回 fields(含 rule_list + field_comment),前端 resultFields 不再变 - 后端给每行注入 __row_index = (page_no-1)*page_size + idx,给前端做 Vue row-key - /page 失败不摘 session,让前端可以重试或走 /cancel 测试:新增 web3/tests/test_queries.py 15 条(start / page / cancel / paginate_sql / 并发 / TTL), 老的 test_run_query_only_bad_rows.py 和 test_rule_types_e2e.py 适配新端点(删 max_rows 那条, end-to-end 改成 /start + /page 聚合)。73 条全过。
-
Data Governance Dev authored
无任务 / 选了任务但跑出 0 条不合规行 / 用户还没点「查询」时, rows=[],「备注」列还是会被 allColumns 自动追加,整张表只有一列「备注」 加占位符「—」,看起来很怪。 allColumns 计算属性改成 rows.length > 0 才追加「备注」列。 rows 为空时整张表只渲染业务列 + 「暂无数据」占位。 Vite HMR 自动生效,不用重启。
-
Data Governance Dev authored
问题(commit 5ffa4cc4 之后又暴露): 用户重跑 test3,attendance_in_time(rule_id=31)仍判不合规。 诊断:pymysql/oracledb/dmPython 把 DATETIME 字段自动转成 Python datetime 对象, 但用户规则代码里的 strptime 用了 '%Y-%m-%d'(只接受纯日期), 而 str(datetime_obj) 是 'YYYY-MM-DD HH:MM:SS'(空格分隔的日期+时间), 格式不匹配 → ValueError → except 吞 → False。 修法:在 run_rule 调 check 前先做一次宽泛解析,把任意类型的 val 归一化成 datetime.datetime 对象。用户写规则直接 value.date() / .year / .strftime() 用, 不再关心 val 的原始类型(驱动转的 datetime 对象 / str / bytes / None)。 1. backend/core/rule_runner.py - 新增 _DATE_PARSE_FORMATS:10 种常见格式(MySQL DATETIME 默认 / ISO8601 / 紧凑 / 斜杠分隔 等) - 新增 _patch_date_value(val):datetime/date/str/bytes → datetime.datetime, 解析失败返 None - run_rule 的 date 分支先 patch:成功 → _exec_user_function(code, dt); 失败 → 抛 RuleRunError('无法解析为日期:...') 让 issues 暴露原因 2. backend/core/ai_rule.py - _DATE_USER_TEMPLATE_INITIAL / _DATE_USER_TEMPLATE_FIX 完全改写: 「value 已经是 datetime.datetime 对象(后端已自动宽泛解析)」 去掉所有 datetime.datetime.strptime 教学,改用 value.date() / .year 等 3. src/components/RuleModal.vue - codePlaceholder(date) 改 'def check(value) -> bool:\n return value.date() < datetime.date.today()' - date 类型 form-hint 改 'value 已是 datetime 对象;不要再 strptime' (红字警示) 4. tests/test_rule_runner.py - 删 1 条过时的 strptime 风格用例 test_strptime_format_check - TestDate 加 8 条用例:patch 后传 datetime / datetime 对象直接传 / MySQL 默认格式 / ISO8601 / 紧凑 / 斜杠 / bytes / 解析失败抛 RuleRunError / None 抛 RuleRunError / 老 strptime 风格代码在 patch 下返 False(迁移信号) - 新 TestPatchDateValue 类 11 条用例:锁 _patch_date_value 的 12 种类型行为 5. tests/test_rule_types_e2e.py - test_queries_run_dispatches_by_rule_type 里 birthday 规则的 code 改成新风格 'value.date() <= datetime.date.today()' 对老规则的影响(migration gap): - DB 里的旧 strptime 风格代码(rule_id=28 / 31)继续返 False —— 行为不变, 用户需去 UI 改成新风格。按 CLAUDE.md「本工具只做查询」不动 DB。 - 新生成的或手写正确代码直接用 value.date() 等,跟 patch 行为兼容。 60 条测试全过(+9 新增)。
-
Data Governance Dev authored
沙箱里 datetime 是模块名(不是 from datetime import datetime), datetime.strptime / datetime.now 在沙箱里直接 AttributeError。 三处误导性的写法全部修正: 1. backend/core/ai_rule.py - _DATE_USER_TEMPLATE_INITIAL / _DATE_USER_TEMPLATE_FIX 两份 prompt 把 datetime.strptime 改成 datetime.datetime.strptime, 并加粗警告『绝对不能写成 datetime.strptime(会 AttributeError)』 - 顺手修一笔 def check(value) -> str -> bool 的笔误(多写了 -> str) 2. src/components/RuleModal.vue - codePlaceholder(date) 改用 datetime.datetime.strptime, 注释里点明沙箱语义 - date 类型的 form-hint 改用 datetime.datetime.strptime, 并加红字 warning 警示『不是 datetime.strptime』 3. tests/test_rule_runner.py - TestDate 加 test_datetime_strptime_without_module_qualifier_fails_clearly 锁定『datetime.strptime(缺 .datetime.)必然 AttributeError』这个行为, 防止哪天有人悄悄做成兼容(破坏语义清晰度) 注:已有坏规则(如 attendance_in_time 已落库 4 处)用户自行到 UI 改, 按 CLAUDE.md『本工具只做查询』原则不动 DB。 39 条测试全过。
-