同步全局协作台账与工作台界面优化
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
工作台新增与项目平级的全局协作台账,复用共享库在线协作能力。两本台账使用合同台账明细表、临床运营项目编号的空白模板,支持独立账号授权、在线编辑和历史版本保留,禁止删除整个台账及历史版本。 台账管理拆分为信息维护、访问与权限、历史版本三个独立入口;后两者与项目共享组件。统一项目和台账的卡片高度、数量徽标与标题布局,缩小工作台顶部留白,优化信息维护弹窗,并修复空用途说明和版本命名的默认提示。同步现有飞线图白色残影修复,以及在线文档的字体构建支持。 已验证:前端五百五十四项测试、后端台账与在线文档七十四项测试、类型检查、界面约束、运行时边界、桌面发布静态检查、网页构建和本地桌面应用构建均通过。数据库表结构升级已完成离线脚本生成检查,存储持久化检查通过。桌面凭据测试显式隔离构建环境,并覆盖使用默认服务器地址读取凭据的场景;在注入默认服务器地址的环境中完成全部前端测试。 提交包含必需的表结构升级和只有表头的初始模板;不包含本地台账业务记录、数据库导出、账号授权运行数据或上传目录中的历史文件。字体文件沿用部署方单独提供的方式。
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, File, Form, Query, Response, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_current_user, get_db_session, require_roles
|
||||
from app.schemas.collaboration import (
|
||||
CollaborationEditorConfigRead, CollaborationExportRecord, CollaborationRevisionRead,
|
||||
CollaborationEditRequestRead, CollaborationEditRequestResolve, CollaborationOwnershipTransferRequest,
|
||||
CollaborationShareLinkRead, CollaborationShareLinkUpdate, CollaborationRevisionUpdate,
|
||||
)
|
||||
from app.schemas.ledger import (
|
||||
LedgerAccessSettings, LedgerCandidateRead, LedgerMemberRead, LedgerMemberUpsert, LedgerRead, LedgerRestore, LedgerSettings,
|
||||
)
|
||||
from app.schemas.onlyoffice import OnlyOfficePreviewConfigRead
|
||||
from app.services import collaboration_service, collaboration_share_service, ledger_service, onlyoffice_collaboration_service, onlyoffice_service
|
||||
|
||||
|
||||
def no_store(response: Response):
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
|
||||
|
||||
router = APIRouter(dependencies=[Depends(no_store)])
|
||||
|
||||
|
||||
@router.get("", response_model=list[LedgerRead])
|
||||
async def list_ledgers(db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
return await ledger_service.list_ledgers(db, user)
|
||||
|
||||
|
||||
@router.post("/initialize", response_model=list[LedgerRead])
|
||||
async def initialize(db: AsyncSession = Depends(get_db_session), user=Depends(require_roles(["ADMIN"]))):
|
||||
return await ledger_service.initialize_ledgers(db, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}", response_model=LedgerRead)
|
||||
async def read(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user)
|
||||
return await ledger_service.read_ledger(db, item, user)
|
||||
|
||||
|
||||
@router.patch("/{ledger_id}", response_model=LedgerRead)
|
||||
async def update(ledger_id: uuid.UUID, payload: LedgerSettings, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await ledger_service.update_settings(db, item, payload, user)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/import", response_model=LedgerRead)
|
||||
async def import_template(
|
||||
ledger_id: uuid.UUID, file: UploadFile = File(...), generation: int = Form(..., ge=1),
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await ledger_service.import_content(db, item, file, generation, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/members", response_model=list[LedgerMemberRead])
|
||||
async def members(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True)
|
||||
return await ledger_service.list_members(db, item)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/candidates", response_model=list[LedgerCandidateRead])
|
||||
async def candidates(
|
||||
ledger_id: uuid.UUID, keyword: str = Query(default="", max_length=100),
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
await ledger_service.get_ledger(db, ledger_id, user, manage=True)
|
||||
return await ledger_service.candidates(db, keyword)
|
||||
|
||||
|
||||
@router.put("/{ledger_id}/members", status_code=204)
|
||||
async def grant(
|
||||
ledger_id: uuid.UUID, payload: LedgerMemberUpsert,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
await ledger_service.set_member(db, item, payload, user)
|
||||
|
||||
|
||||
@router.delete("/{ledger_id}/members/{user_id}", status_code=204)
|
||||
async def revoke(
|
||||
ledger_id: uuid.UUID, user_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
await ledger_service.remove_member(db, item, user_id, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/editor-config", response_model=CollaborationEditorConfigRead)
|
||||
async def editor_config(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, lock=True)
|
||||
return await onlyoffice_collaboration_service.build_editor_config(db, item, user)
|
||||
|
||||
|
||||
@router.patch("/{ledger_id}/access-settings", response_model=LedgerRead)
|
||||
async def access_settings(ledger_id: uuid.UUID, payload: LedgerAccessSettings,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await ledger_service.update_access_settings(db, item, payload, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/share-link", response_model=CollaborationShareLinkRead)
|
||||
async def share_link(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await collaboration_share_service.get_share_link(db, item, user)
|
||||
|
||||
|
||||
@router.put("/{ledger_id}/share-link", response_model=CollaborationShareLinkRead)
|
||||
async def update_share_link(ledger_id: uuid.UUID, payload: CollaborationShareLinkUpdate,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await collaboration_share_service.update_share_link(db, item, payload, user)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/edit-requests", response_model=CollaborationEditRequestRead)
|
||||
async def request_edit(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, lock=True)
|
||||
return await collaboration_service.create_edit_request(db, item, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/edit-requests", response_model=list[CollaborationEditRequestRead])
|
||||
async def edit_requests(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True)
|
||||
return await collaboration_service.list_edit_requests(db, item, user)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/edit-requests/{request_id}/resolve", response_model=CollaborationEditRequestRead)
|
||||
async def resolve_request(ledger_id: uuid.UUID, request_id: uuid.UUID, payload: CollaborationEditRequestResolve,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await collaboration_service.resolve_edit_request(db, item, request_id, payload, user)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/transfer-ownership", response_model=LedgerRead)
|
||||
async def transfer_owner(ledger_id: uuid.UUID, payload: CollaborationOwnershipTransferRequest,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
item = await collaboration_service.transfer_ownership(db, item, payload, user)
|
||||
return await ledger_service.read_ledger(db, item, user)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/downloads", status_code=204)
|
||||
async def download_check(
|
||||
ledger_id: uuid.UUID, payload: CollaborationExportRecord,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user)
|
||||
await collaboration_service.record_download(db, item, user, payload.file_type)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/revisions", response_model=list[CollaborationRevisionRead])
|
||||
async def revisions(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user)
|
||||
return await collaboration_service.list_revisions(db, item)
|
||||
|
||||
|
||||
@router.post("/{ledger_id}/revisions/{revision_id}/restore", response_model=CollaborationRevisionRead)
|
||||
async def restore(
|
||||
ledger_id: uuid.UUID, revision_id: uuid.UUID, payload: LedgerRestore,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True)
|
||||
return await ledger_service.restore_revision(db, item, revision_id, payload.generation, user)
|
||||
|
||||
|
||||
@router.patch("/{ledger_id}/revisions/{revision_id}", response_model=CollaborationRevisionRead)
|
||||
async def name_revision(
|
||||
ledger_id: uuid.UUID, revision_id: uuid.UUID, payload: CollaborationRevisionUpdate,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, lock=True)
|
||||
return await collaboration_service.update_revision(db, item, revision_id, payload, user)
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/revisions/{revision_id}/download")
|
||||
async def download_revision(
|
||||
ledger_id: uuid.UUID, revision_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user, lock=True)
|
||||
await collaboration_service.require_file_exporter(db, item, user)
|
||||
revision = await collaboration_service.prepare_revision_preview(db, item, revision_id, user)
|
||||
await collaboration_service.record_download(db, item, user, "xlsx")
|
||||
return FileResponse(revision.file_uri, filename=item.title, media_type=revision.mime_type,
|
||||
headers={"Cache-Control": "no-store"})
|
||||
|
||||
|
||||
@router.get("/{ledger_id}/revisions/{revision_id}/preview-config", response_model=OnlyOfficePreviewConfigRead)
|
||||
async def preview(
|
||||
ledger_id: uuid.UUID, revision_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user),
|
||||
):
|
||||
item = await ledger_service.get_ledger(db, ledger_id, user)
|
||||
revision = await collaboration_service.prepare_revision_preview(db, item, revision_id, user)
|
||||
await onlyoffice_service.ensure_onlyoffice_available()
|
||||
return onlyoffice_service.build_preview_config(
|
||||
resource_type="collaboration_revision", resource_id=revision.id, file_name=item.title,
|
||||
file_hash=revision.file_hash, user_id=user.id, user_name=user.full_name,
|
||||
)
|
||||
Reference in New Issue
Block a user