Files
zTools2/app/dao/whiteboard_dao.py
zikai 9af28f41b4 refactor: 清理死代码/提前失败/日志/高内聚低耦合
死代码移除:
- whiteboard_hub.py: 移除未引用的 reset_hub 单例重置函数
- tunnel_service.py: 移除未引用的 is_port_allowed (逻辑已在 sftp_server 内联)
- tunnel_session_dao.py: 移除未引用的 get_active_by_port
- pdf_job_dao.py: 移除未用 datetime 导入
- pdf_converter.py: 移除未用 shutil 导入
- pdf_service.py: 移除未用 PdfSubmitResponse 导入 + _do_convert 内未用 hashlib 导入
- upload_html.py: 移除未用 escape 导入 (JS 侧自有 escapeHtml)
- pdf_controller.py: 移除 _resolve_cookie 内未用 cfg 局部变量

提前失败/分层修复:
- database.py init_db_schema: 建表后用 inspector 校验既有表列与模型一致,
  缺列即抛 RuntimeError (fail-fast on schema drift), 避免运行期才暴露
- whiteboard_dao.get_or_create: 仅 IntegrityError 才回滚重读, 其他异常向上抛
  (原 except Exception 会掩盖 schema/连接等真实故障)
- pdf_service.admin_delete/_safe_delete_file: 改用 PdfJobDAO.delete /
  UploadedFileDAO.delete, 不再直接操作 job_dao.db / file_dao.db (修复分层契约:
  DAO 头注释声明 service 不直接操作 session)
- PdfJobDAO 新增 delete(job) 方法

日志补全 (8 处 silent catch):
- whiteboard_hub.py disconnect/close_board 关闭 ws: logger.debug
- whiteboard_controller _safe_send/_safe_close: logger.debug
- sftp_server _close_tunnel_dao/读用户名: logger.debug
- sftp_server validate_public_key: logger.warning (auth 路径, 避免静默失败)

文档:
- 新增 docs/routes.md, docs/configuration.md, docs/error-handling.md
- README.md 精简为简介/结构/外部依赖/apache2 配置/Ubuntu 安装/docs 链接
2026-07-28 11:34:35 +08:00

82 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Whiteboard 的 DAO文本记事本
所有写操作均在该层 commitservice 不直接操作 session。
get_or_create 用于「访问即新建」语义(路由 GET /api/wb/{id} 不存在则建)。
"""
from __future__ import annotations
import logging
from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from ..models.whiteboard import Whiteboard
logger = logging.getLogger("zikai.whiteboard")
class WhiteboardDAO:
def __init__(self, db: Session) -> None:
self.db = db
def create(self, board: Whiteboard) -> Whiteboard:
self.db.add(board)
self.db.commit()
self.db.refresh(board)
return board
def get(self, board_id: str) -> Whiteboard | None:
stmt = select(Whiteboard).where(Whiteboard.board_id == board_id).limit(1)
return self.db.scalars(stmt).first()
def get_or_create(self, board_id: str) -> Whiteboard:
"""存在则返回,否则新建空板。利用 unique 约束兜底并发首访。
仅 IntegrityError并发下另一事务已插入违反唯一约束才回滚重读
其他异常向上抛,避免掩盖 schema/连接等真实故障。
"""
board = self.get(board_id)
if board is not None:
return board
board = Whiteboard(board_id=board_id, content="", version=0, edit_count=0)
try:
return self.create(board)
except IntegrityError:
# 并发下另一事务已插入:回滚后重新读
self.db.rollback()
return self.get(board_id) # type: ignore[return-value]
def update_content(self, board_id: str, content: str) -> Whiteboard | None:
"""整体替换文本内容version +1、edit_count +1。"""
board = self.get(board_id)
if board is None:
return None
board.content = content
board.version = (board.version or 0) + 1
board.edit_count = (board.edit_count or 0) + 1
self.db.commit()
self.db.refresh(board)
return board
def list_all(self, limit: int = 100, offset: int = 0) -> list[Whiteboard]:
stmt = (
select(Whiteboard)
.order_by(Whiteboard.updated_at.desc())
.limit(limit)
.offset(offset)
)
return list(self.db.scalars(stmt).all())
def count(self) -> int:
return self.db.scalar(select(func.count()).select_from(Whiteboard)) or 0
def delete(self, board_id: str) -> bool:
board = self.get(board_id)
if board is None:
return False
self.db.delete(board)
self.db.commit()
return True