"""图片:业务规则(文件落盘 + 路径写入 images 表)。""" from __future__ import annotations import json import os import sys from typing import Any, Dict, Optional from content_manager.config import get_skill_data_dir, resolve_stored_path from content_manager.db import images_repository as ir from content_manager.db.connection import get_conn, init_db from content_manager.services import file_store from content_manager.util.timeutil import now_unix, unix_to_iso def _row_to_public_dict(row: tuple) -> Dict[str, Any]: rid, file_path, title, status, source, account_id, error_msg, extra_json, cat, uat = row abs_path = resolve_stored_path(str(file_path)) d: Dict[str, Any] = { "id": int(rid), "kind": "image", "file_path": file_path, "absolute_path": abs_path, "title": title, "status": status or "draft", "source": source or "manual", "account_id": account_id, "error_msg": error_msg, "created_at": unix_to_iso(cat), "updated_at": unix_to_iso(uat), } if extra_json: try: ex = json.loads(extra_json) if isinstance(ex, dict): d["extra"] = ex except json.JSONDecodeError: pass return d def cmd_add(src_file: str, title: Optional[str] = None, source: str = "manual") -> None: init_db() src_file = os.path.abspath(src_file.strip()) if not os.path.isfile(src_file): print(f"❌ 找不到文件:{src_file}") sys.exit(1) skill_data = get_skill_data_dir() ts = now_unix() conn = get_conn() try: new_id = ir.insert_row( conn, file_path="", title=(title or "").strip() or None, status="draft", source=source, account_id=None, error_msg=None, extra_json=None, created_at=ts, updated_at=ts, ) rel, _abs = file_store.copy_into_skill_data(skill_data, "images", new_id, src_file) ir.update_file_path(conn, new_id, rel, now_unix()) conn.commit() except Exception: conn.rollback() raise finally: conn.close() print(f"✅ 已新增图片 id={new_id} | 路径:{rel}") def cmd_get(image_id: str) -> None: init_db() if not str(image_id).strip().isdigit(): print("❌ 图片 id 必须是纯数字。请先 image list 查看。") sys.exit(1) iid = int(image_id) conn = get_conn() try: row = ir.fetch_by_id(conn, iid) finally: conn.close() if not row: print("❌ 没有这条图片记录。") sys.exit(1) print(json.dumps(_row_to_public_dict(row), ensure_ascii=False)) def cmd_list(limit: int = 20, max_chars: int = 80) -> None: init_db() conn = get_conn() try: rows = ir.list_recent(conn, limit) finally: conn.close() if not rows: print("暂无图片") return def trunc(s: str) -> str: if not s: return "" return s if len(s) <= max_chars else s[:max_chars] + "..." sep = "_" * 39 for idx, r in enumerate(rows): rid, file_path, title, status, source, account_id, error_msg, extra_json, cat, uat = r print(f"id:{rid}") print(f"file_path:{trunc(str(file_path or ''))}") print(f"title:{title or ''}") print(f"status:{status or ''}") print(f"source:{source or ''}") print(f"account_id:{account_id or ''}") print(f"error_msg:{error_msg or ''}") print(f"created_at:{unix_to_iso(cat) or ''}") print(f"updated_at:{unix_to_iso(uat) or ''}") if idx != len(rows) - 1: print(sep) print() def cmd_delete(image_id: str) -> None: init_db() if not str(image_id).strip().isdigit(): print("❌ 图片 id 必须是纯数字。") sys.exit(1) iid = int(image_id) skill_data = get_skill_data_dir() conn = get_conn() try: row = ir.fetch_by_id(conn, iid) if not row: print("❌ 没有 id 为 {} 的图片记录。".format(iid)) sys.exit(1) rel = row[1] n = ir.delete_by_id(conn, iid) if n == 0: sys.exit(1) conn.commit() finally: conn.close() file_store.remove_files_for_relative_path(skill_data, str(rel)) print(f"✅ 已删除图片 id={iid}") def cmd_feedback( image_id: str, status: str, account_id: Optional[str] = None, error_msg: Optional[str] = None, ) -> None: init_db() if not str(image_id).strip().isdigit(): print("❌ 图片 id 必须是纯数字。") sys.exit(1) iid = int(image_id) ts = now_unix() conn = get_conn() try: if ir.fetch_by_id(conn, iid) is None: print("❌ 没有 id 为 {} 的图片记录。".format(iid)) sys.exit(1) ir.update_feedback(conn, iid, status, account_id, error_msg, ts) conn.commit() finally: conn.close() print("✅ 状态已更新")