feat: 数据管理列宽可调/行编辑删除/模板导入,CellData 处理日志细化
- 数据表列宽可拖拽 + 新增「操作」列(编辑/删除该行) - 工具栏新增「模板」「导入」:CSV 按模板字段校验,不一致则导入失败 - 后端 DatabaseManager/MetrixWarehouse 增加行更新/删除/CSV 导入 + 对应路由 - CellData 解压/解析/写库日志细化(逐文件、分批进度) - 修复 ScriptPanel monaco mysql 深层导入缺类型声明导致的 vue-tsc 构建失败
This commit is contained in:
@@ -9,6 +9,7 @@ query_table / truncate_table / drop_table / drop_all_tables / execute_sql。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from app.config import AppConfig
|
||||
@@ -34,6 +35,10 @@ def _quote_value(value: str) -> str:
|
||||
return "'" + str(value).replace("\\", "\\\\").replace("'", "''") + "'"
|
||||
|
||||
|
||||
def _sql_literal(value: Any) -> str:
|
||||
return "NULL" if value is None else _quote_value(str(value))
|
||||
|
||||
|
||||
class MetrixWarehouse:
|
||||
"""用 Metrix 数据库 API 实现 DatabaseManager 的只读/管理子集。"""
|
||||
|
||||
@@ -153,6 +158,41 @@ class MetrixWarehouse:
|
||||
self._run(drop_sql)
|
||||
return {"success": True, "dropped_count": len(tables), "tables": tables}
|
||||
|
||||
def _row_where(self, identifier: Dict[str, Any]) -> str:
|
||||
parts = [
|
||||
f"{_quote_ident(col)} IS NULL" if val is None else f"{_quote_ident(col)} = {_sql_literal(val)}"
|
||||
for col, val in identifier.items()
|
||||
]
|
||||
return " AND ".join(parts) if parts else "1 = 0"
|
||||
|
||||
def update_row(self, table_name: str, identifier: Dict[str, Any], values: Dict[str, Any]) -> int:
|
||||
if not values:
|
||||
return 0
|
||||
set_clause = ", ".join(f"{_quote_ident(col)} = {_sql_literal(val)}" for col, val in values.items())
|
||||
sql = f"UPDATE {_quote_ident(table_name)} SET {set_clause} WHERE {self._row_where(identifier)} LIMIT 1"
|
||||
ok, result = self.execute_sql(sql)
|
||||
if not ok:
|
||||
raise RuntimeError(str(result))
|
||||
return int(result.get("affected_rows", 0)) if isinstance(result, dict) else 0
|
||||
|
||||
def delete_row(self, table_name: str, identifier: Dict[str, Any]) -> int:
|
||||
sql = f"DELETE FROM {_quote_ident(table_name)} WHERE {self._row_where(identifier)} LIMIT 1"
|
||||
ok, result = self.execute_sql(sql)
|
||||
if not ok:
|
||||
raise RuntimeError(str(result))
|
||||
return int(result.get("affected_rows", 0)) if isinstance(result, dict) else 0
|
||||
|
||||
def import_csv(self, file_path: str, table_name: str) -> int:
|
||||
"""走 Metrix 平台导入 API 追加导入到已存在的表(字段映射由平台按列名处理)。"""
|
||||
job_id = self.client.import_csv(
|
||||
self.conn_id, table_name, Path(file_path), mode="append",
|
||||
database=self.database, create_table=False,
|
||||
)
|
||||
job = self.client.wait_job(job_id)
|
||||
if job.get("status") != "success":
|
||||
raise RuntimeError(job.get("error_code") or job.get("status") or "导入失败")
|
||||
return int(job.get("row_count") or 0)
|
||||
|
||||
def execute_sql(self, sql: str) -> Tuple[bool, Any]:
|
||||
try:
|
||||
res = self.client.run_script(self.conn_id, content=sql, database=self.database, run_timeout=600)
|
||||
|
||||
Reference in New Issue
Block a user