277 lines
9.7 KiB
Python
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))
|