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 同步更新。
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""白板服务:CRUD + 笔画操作。
|
||||
"""白板服务(文本记事本):CRUD + 文本更新 + 清空。
|
||||
|
||||
不持有 WebSocket 连接状态(那是 hub 的职责);删除白板时通过可选的 hub 回调
|
||||
通知 hub 踢出该 board 的所有在线连接,由 controller 在装配时注入,避免 service -> hub
|
||||
@@ -8,7 +8,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -30,6 +30,8 @@ class WhiteboardService:
|
||||
cfg = get_settings().whiteboard
|
||||
self.max_board_id_length = cfg.max_board_id_length
|
||||
self.list_limit = cfg.list_limit
|
||||
# 文本内容长度上限(防滥用)
|
||||
self.max_content_length = 256 * 1024
|
||||
|
||||
# ---------------- 校验 ----------------
|
||||
|
||||
@@ -42,6 +44,15 @@ class WhiteboardService:
|
||||
):
|
||||
raise HTTPException(400, "board_id 非法(仅允许字母数字下划线短横线,1-64 字符)")
|
||||
|
||||
def validate_content(self, content: str) -> None:
|
||||
if not isinstance(content, str):
|
||||
raise HTTPException(400, "content 必须是字符串")
|
||||
if len(content) > self.max_content_length:
|
||||
raise HTTPException(
|
||||
413,
|
||||
f"文本过长({len(content)} > {self.max_content_length}),请缩减内容",
|
||||
)
|
||||
|
||||
# ---------------- 读 ----------------
|
||||
|
||||
def get_or_create(self, board_id: str) -> WhiteboardOut:
|
||||
@@ -59,21 +70,18 @@ class WhiteboardService:
|
||||
|
||||
# ---------------- 写 ----------------
|
||||
|
||||
def append_stroke(self, board_id: str, stroke: dict[str, Any]) -> WhiteboardOut:
|
||||
"""追加一条笔画并返回最新状态。"""
|
||||
def update_content(self, board_id: str, content: str) -> WhiteboardOut:
|
||||
"""整体替换文本内容(客户端 debounce 后发完整文本)。"""
|
||||
self.validate_board_id(board_id)
|
||||
board = self.dao.append_strokes(board_id, [stroke])
|
||||
self.validate_content(content)
|
||||
board = self.dao.update_content(board_id, content)
|
||||
if board is None:
|
||||
raise HTTPException(404, "白板不存在")
|
||||
return WhiteboardOut.model_validate(board)
|
||||
|
||||
def clear(self, board_id: str) -> WhiteboardOut:
|
||||
"""清空白板;stroke_count 仍自增以记录这次修改。"""
|
||||
self.validate_board_id(board_id)
|
||||
board = self.dao.replace_strokes(board_id, [])
|
||||
if board is None:
|
||||
raise HTTPException(404, "白板不存在")
|
||||
return WhiteboardOut.model_validate(board)
|
||||
"""清空白板(内容置空),edit_count 仍自增以记录这次修改。"""
|
||||
return self.update_content(board_id, "")
|
||||
|
||||
def delete(self, board_id: str) -> bool:
|
||||
"""删除白板;同时通知 hub 踢出该 board 的所有在线连接。"""
|
||||
|
||||
Reference in New Issue
Block a user