Files
zTools2/app/dao/whiteboard_dao.py
zikai 655e039aad refactor: 白板从画笔画板改为文本记事本
原实现是 Canvas 画笔画板,与「文字白板/记事本」需求不符。重做为纯文本实时协作:
- model:strokes JSON -> content TEXT + version INT(乐观锁)+ edit_count;
  schema/dao/service 同步重构,append_stroke/replace_strokes -> update_content。
- WS 协议:stroke -> edit(发完整文本,debounce 400ms);init 下发 content/version。
  update 帧广播给他人,cleared 广播给所有人。
- 前端:canvas -> textarea;收到远端 update 用最长公共前后缀算变更区间,
  仅替换该区间并保留光标(区间前不动/后平移/内移末尾);清空/复制文本按钮。
- schema.sql 更新 whiteboard 表 DDL;DB 旧表 DROP 重建(开发环境)。
- 测试脚本与 README 同步更新。
2026-07-21 15:00:25 +00:00

73 lines
2.4 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
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from ..models.whiteboard import 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 约束兜底并发首访。"""
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 Exception:
# 并发下另一事务已插入:回滚后重新读
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