Files
ai-agent-admin/backend-fastapi/online_dev/screen_design/screen_api.py
T

277 lines
9.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
大屏设计器 API
大屏项目的 CRUD、发布、复制、导入导出
"""
import json
import logging
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.config import settings
from app.base_schema import PaginatedResponse
from online_dev.screen_design.screen_model import ScreenProject
from online_dev.screen_design.screen_schema import (
ScreenProjectCreateIn,
ScreenProjectUpdateIn,
ScreenProjectOut,
ScreenProjectListOut,
ScreenPublishIn,
ScreenSetPasswordIn,
ScreenImportCheckIn,
ScreenImportCheckOut,
ScreenImportIn,
)
from online_dev.screen_design.screen_service import ScreenProjectService, ScreenServiceException
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/screen', tags=['大屏设计器'])
@router.get('/list', response_model=PaginatedResponse[ScreenProjectListOut], summary='大屏项目列表')
async def list_projects(
page: int = Query(default=1, ge=1, description="页码"),
page_size: int = Query(default=settings.PAGE_SIZE, ge=1, le=settings.PAGE_MAX_SIZE, alias="pageSize", description="每页数量"),
name: str = Query(default=None, description="项目名称"),
code: str = Query(default=None, description="项目编码"),
status: str = Query(default=None, description="状态"),
application_id: str = Query(default=None, alias="applicationId", description="应用ID"),
db: AsyncSession = Depends(get_db),
):
"""分页查询大屏项目列表(自动应用数据权限)"""
# 构建过滤条件
filters = []
# 应用过滤
if application_id is not None:
if application_id == "":
filters.append(ScreenProject.application_id.is_(None))
else:
filters.append(ScreenProject.application_id == application_id)
if name:
filters.append(ScreenProject.name.contains(name))
if code:
filters.append(ScreenProject.code.contains(code))
if status:
filters.append(ScreenProject.status == status)
# 使用带数据权限的列表查询
items, total = await ScreenProjectService.get_list_with_data_scope(
db, page=page, page_size=page_size, filters=filters
)
# 转换为输出格式
result_items = [ScreenProjectListOut.from_orm_model(item) for item in items]
return PaginatedResponse(items=result_items, total=total)
@router.get('/code/{code}', response_model=ScreenProjectOut, summary='根据编码获取大屏项目')
async def get_project_by_code(
code: str,
db: AsyncSession = Depends(get_db),
):
"""根据编码获取大屏项目详情"""
project = await ScreenProjectService.get_by_code(db, code)
if not project:
raise HTTPException(status_code=400, detail=f"大屏项目不存在: {code}")
return ScreenProjectOut.from_orm_model(project)
@router.get('/{project_id}', response_model=ScreenProjectOut, summary='大屏项目详情')
async def get_project(
project_id: str,
db: AsyncSession = Depends(get_db),
):
"""获取大屏项目详情"""
project = await ScreenProjectService.get_by_id(db, project_id)
if not project:
raise HTTPException(status_code=400, detail="大屏项目不存在")
return ScreenProjectOut.from_orm_model(project)
@router.post('', response_model=ScreenProjectOut, summary='创建大屏项目')
async def create_project(
data: ScreenProjectCreateIn,
db: AsyncSession = Depends(get_db),
):
"""创建大屏项目"""
# 检查编码唯一性
if await ScreenProjectService.check_code_exists(db, data.code):
raise HTTPException(status_code=400, detail=f"项目编码已存在: {data.code}")
project = await ScreenProjectService.create(db, data)
await db.commit()
logger.info(f"大屏项目创建成功: {project.code}")
return ScreenProjectOut.from_orm_model(project)
@router.put('/{project_id}', response_model=ScreenProjectOut, summary='更新大屏项目')
async def update_project(
project_id: str,
data: ScreenProjectUpdateIn,
db: AsyncSession = Depends(get_db),
):
"""更新大屏项目"""
project = await ScreenProjectService.update(db, project_id, data)
if not project:
raise HTTPException(status_code=400, detail="大屏项目不存在")
await db.commit()
logger.info(f"大屏项目更新成功: {project.code}")
return ScreenProjectOut.from_orm_model(project)
@router.delete('/{project_id}', response_model=ScreenProjectOut, summary='删除大屏项目')
async def delete_project(
project_id: str,
db: AsyncSession = Depends(get_db),
):
"""删除大屏项目"""
project = await ScreenProjectService.get_by_id(db, project_id)
if not project:
raise HTTPException(status_code=400, detail="大屏项目不存在")
result = ScreenProjectOut.from_orm_model(project)
await ScreenProjectService.delete(db, project_id)
await db.commit()
return result
@router.delete('/batch', summary='批量删除大屏项目')
async def batch_delete_projects(
ids: List[str] = Query(..., description="项目ID列表"),
db: AsyncSession = Depends(get_db),
):
"""批量删除大屏项目"""
count = await ScreenProjectService.batch_delete(db, ids)
await db.commit()
return {'count': count}
@router.post('/{project_id}/publish', response_model=ScreenProjectOut, summary='发布大屏')
async def publish_project(
project_id: str,
data: ScreenPublishIn = None,
db: AsyncSession = Depends(get_db),
):
"""发布大屏"""
try:
access_password = data.access_password if data else None
project = await ScreenProjectService.publish(db, project_id, access_password)
await db.commit()
return ScreenProjectOut.from_orm_model(project)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/{project_id}/unpublish', response_model=ScreenProjectOut, summary='取消发布')
async def unpublish_project(
project_id: str,
db: AsyncSession = Depends(get_db),
):
"""取消发布大屏"""
try:
project = await ScreenProjectService.unpublish(db, project_id)
await db.commit()
return ScreenProjectOut.from_orm_model(project)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/{project_id}/set-password', response_model=ScreenProjectOut, summary='设置访问密码')
async def set_access_password(
project_id: str,
data: ScreenSetPasswordIn,
db: AsyncSession = Depends(get_db),
):
"""设置或清除大屏访问密码"""
try:
project = await ScreenProjectService.set_password(db, project_id, data.access_password)
await db.commit()
return ScreenProjectOut.from_orm_model(project)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/{project_id}/verify-password', summary='验证访问密码')
async def verify_access_password(
project_id: str,
data: ScreenSetPasswordIn,
db: AsyncSession = Depends(get_db),
):
"""验证大屏访问密码"""
try:
is_valid = await ScreenProjectService.verify_password(db, project_id, data.access_password)
return {'valid': is_valid}
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/{project_id}/copy', response_model=ScreenProjectOut, summary='复制大屏项目')
async def copy_project(
project_id: str,
new_code: str = Query(..., alias="newCode", description="新项目编码"),
new_name: str = Query(default=None, alias="newName", description="新项目名称"),
db: AsyncSession = Depends(get_db),
):
"""复制大屏项目"""
try:
project = await ScreenProjectService.copy(db, project_id, new_code, new_name)
await db.commit()
return ScreenProjectOut.from_orm_model(project)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.get('/{project_id}/export', summary='导出大屏配置')
async def export_project_config(
project_id: str,
db: AsyncSession = Depends(get_db),
):
"""导出大屏配置为 JSON"""
try:
config = await ScreenProjectService.export_config(db, project_id)
content = json.dumps(config, ensure_ascii=False, indent=2)
return StreamingResponse(
iter([content]),
media_type='application/json',
headers={
'Content-Disposition': f'attachment; filename="{config["code"]}.json"'
}
)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/import/check', response_model=ScreenImportCheckOut, summary='导入预检查')
async def check_import_project_config(
data: ScreenImportCheckIn,
db: AsyncSession = Depends(get_db),
):
"""导入预检查:检查项目编码是否冲突"""
try:
return await ScreenProjectService.check_import(db, data.code)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post('/import', response_model=ScreenProjectOut, summary='导入大屏配置')
async def import_project_config(
data: ScreenImportIn,
db: AsyncSession = Depends(get_db),
):
"""导入大屏配置"""
try:
project = await ScreenProjectService.import_config(db, data.model_dump())
await db.commit()
return ScreenProjectOut.from_orm_model(project)
except ScreenServiceException as e:
raise HTTPException(status_code=400, detail=str(e))