feat: restore source parity and harden agent runtime
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
#!/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))
|
||||
Reference in New Issue
Block a user