#!/usr/bin/env python # -*- coding: utf-8 -*- """ 应用管理API """ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.config import settings from app.base_schema import PaginatedResponse, ResponseModel from core.application.schema import ( ApplicationCreate, ApplicationUpdate, ApplicationResponse, ApplicationListResponse, ) from core.application.service import ApplicationService router = APIRouter(prefix="/applications", tags=["应用管理"]) @router.post("/", response_model=ApplicationResponse, summary="创建应用") async def create_application( data: ApplicationCreate, db: AsyncSession = Depends(get_db) ): """ 创建新应用 - **name**: 应用名称 - **code**: 应用编码(唯一标识,用于URL路由,只能包含字母、数字、下划线和短横线) - **description**: 应用描述(可选) - **icon**: 应用图标(可选) - **app_type**: 应用类型(可选,默认mixed) """ # 检查编码唯一性 if not await ApplicationService.check_unique(db, field="code", value=data.code): raise HTTPException(status_code=400, detail="应用编码已存在") # 检查名称唯一性 if not await ApplicationService.check_unique(db, field="name", value=data.name): raise HTTPException(status_code=400, detail="应用名称已存在") return await ApplicationService.create(db=db, data=data) @router.get("/", response_model=PaginatedResponse[ApplicationListResponse], summary="获取应用列表") async def get_applications( 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="每页数量"), keyword: str = Query(default=None, description="搜索关键词"), app_type: str = Query(default=None, alias="appType", description="应用类型"), status: str = Query(default=None, description="状态"), db: AsyncSession = Depends(get_db) ): """ 获取应用列表(分页) 支持按关键词、类型、状态筛选 """ items, total = await ApplicationService.search( db, keyword=keyword, app_type=app_type, status=status, page=page, page_size=page_size ) return PaginatedResponse(items=items, total=total) @router.get("/stats", response_model=ResponseModel, summary="获取应用统计") async def get_stats(db: AsyncSession = Depends(get_db)): """ 获取应用统计信息 """ stats = await ApplicationService.get_stats(db) return ResponseModel(message="获取成功", data=stats) @router.get("/check/unique", response_model=ResponseModel, summary="检查字段唯一性") async def check_unique( field: str = Query(..., description="字段名:code 或 name"), value: str = Query(..., description="字段值"), exclude_id: str = Query(default=None, alias="excludeId", description="排除的记录ID"), db: AsyncSession = Depends(get_db) ): """ 检查字段值是否唯一 """ allowed_fields = ["code", "name"] if field not in allowed_fields: raise HTTPException(status_code=400, detail=f"不支持检查字段: {field}") is_unique = await ApplicationService.check_unique(db, field=field, value=value, exclude_id=exclude_id) return ResponseModel( message="可用" if is_unique else "已存在", data={"unique": is_unique} ) @router.get("/code/{code}", response_model=ApplicationResponse, summary="根据编码获取应用") async def get_application_by_code( code: str, db: AsyncSession = Depends(get_db) ): """ 根据应用编码获取应用详情 """ db_obj = await ApplicationService.get_by_code(db, code=code) if db_obj is None: raise HTTPException(status_code=404, detail="应用不存在") return db_obj @router.get("/{record_id}", response_model=ApplicationResponse, summary="获取应用详情") async def get_application( record_id: str, db: AsyncSession = Depends(get_db) ): """ 根据ID获取应用详情 """ db_obj = await ApplicationService.get_by_id(db, record_id=record_id) if db_obj is None: raise HTTPException(status_code=404, detail="应用不存在") return db_obj @router.put("/{record_id}", response_model=ApplicationResponse, summary="更新应用") async def update_application( record_id: str, data: ApplicationUpdate, db: AsyncSession = Depends(get_db) ): """ 更新应用信息 """ # 检查编码唯一性(排除自身) if data.code and not await ApplicationService.check_unique(db, field="code", value=data.code, exclude_id=record_id): raise HTTPException(status_code=400, detail="应用编码已存在") # 检查名称唯一性(排除自身) if data.name and not await ApplicationService.check_unique(db, field="name", value=data.name, exclude_id=record_id): raise HTTPException(status_code=400, detail="应用名称已存在") db_obj = await ApplicationService.update(db, record_id=record_id, data=data) if db_obj is None: raise HTTPException(status_code=404, detail="应用不存在") # 清除该应用相关的菜单缓存 from core.menu.service import MenuService await MenuService.invalidate_app_menu_cache(db_obj.code) return db_obj @router.post("/{record_id}/publish", response_model=ApplicationResponse, summary="发布应用") async def publish_application( record_id: str, db: AsyncSession = Depends(get_db) ): """ 发布应用;已停用状态也可调用本接口重新启用(状态变为已发布) """ db_obj = await ApplicationService.publish(db, record_id=record_id) if db_obj is None: raise HTTPException(status_code=404, detail="应用不存在") return db_obj @router.post("/{record_id}/disable", response_model=ApplicationResponse, summary="停用应用") async def disable_application( record_id: str, db: AsyncSession = Depends(get_db) ): """ 停用应用 """ db_obj = await ApplicationService.disable(db, record_id=record_id) if db_obj is None: raise HTTPException(status_code=404, detail="应用不存在") return db_obj @router.delete("/{record_id}", response_model=ResponseModel, summary="删除应用") async def delete_application( record_id: str, hard: bool = Query(default=False, description="是否物理删除"), db: AsyncSession = Depends(get_db) ): """ 删除应用 - **hard=false**: 逻辑删除(默认) - **hard=true**: 物理删除 """ success = await ApplicationService.delete(db, record_id=record_id, hard=hard) if not success: raise HTTPException(status_code=404, detail="应用不存在") return ResponseModel(message="删除成功")