"""通知渠道API路由""" from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy.orm import Session from sqlalchemy import desc from ..database import get_db from ..models.notification_channel import NotificationChannel router = APIRouter(prefix="/api/notification-channels", tags=["notification-channels"]) # ==================== Schemas ==================== class ChannelCreate(BaseModel): tenant_id: str channel_name: str channel_type: str # dingtalk_bot, wecom_bot webhook_url: str description: Optional[str] = None class ChannelUpdate(BaseModel): channel_name: Optional[str] = None channel_type: Optional[str] = None webhook_url: Optional[str] = None description: Optional[str] = None is_enabled: Optional[bool] = None # ==================== CRUD ==================== @router.get("") async def list_channels( tenant_id: Optional[str] = None, channel_type: Optional[str] = None, is_enabled: Optional[bool] = None, page: int = Query(1, ge=1), size: int = Query(50, ge=1, le=100), db: Session = Depends(get_db) ): """获取通知渠道列表""" query = db.query(NotificationChannel) if tenant_id: query = query.filter(NotificationChannel.tenant_id == tenant_id) if channel_type: query = query.filter(NotificationChannel.channel_type == channel_type) if is_enabled is not None: query = query.filter(NotificationChannel.is_enabled == is_enabled) total = query.count() items = query.order_by(desc(NotificationChannel.created_at)).offset((page - 1) * size).limit(size).all() return { "total": total, "items": [format_channel(c) for c in items] } @router.get("/{channel_id}") async def get_channel(channel_id: int, db: Session = Depends(get_db)): """获取渠道详情""" channel = db.query(NotificationChannel).filter(NotificationChannel.id == channel_id).first() if not channel: raise HTTPException(status_code=404, detail="渠道不存在") return format_channel(channel) @router.post("") async def create_channel(data: ChannelCreate, db: Session = Depends(get_db)): """创建通知渠道""" channel = NotificationChannel( tenant_id=data.tenant_id, channel_name=data.channel_name, channel_type=data.channel_type, webhook_url=data.webhook_url, description=data.description, is_enabled=True ) db.add(channel) db.commit() db.refresh(channel) return {"success": True, "id": channel.id} @router.put("/{channel_id}") async def update_channel(channel_id: int, data: ChannelUpdate, db: Session = Depends(get_db)): """更新通知渠道""" channel = db.query(NotificationChannel).filter(NotificationChannel.id == channel_id).first() if not channel: raise HTTPException(status_code=404, detail="渠道不存在") if data.channel_name is not None: channel.channel_name = data.channel_name if data.channel_type is not None: channel.channel_type = data.channel_type if data.webhook_url is not None: channel.webhook_url = data.webhook_url if data.description is not None: channel.description = data.description if data.is_enabled is not None: channel.is_enabled = data.is_enabled db.commit() return {"success": True} @router.delete("/{channel_id}") async def delete_channel(channel_id: int, db: Session = Depends(get_db)): """删除通知渠道""" channel = db.query(NotificationChannel).filter(NotificationChannel.id == channel_id).first() if not channel: raise HTTPException(status_code=404, detail="渠道不存在") db.delete(channel) db.commit() return {"success": True} @router.post("/{channel_id}/test") async def test_channel(channel_id: int, db: Session = Depends(get_db)): """测试通知渠道""" import httpx channel = db.query(NotificationChannel).filter(NotificationChannel.id == channel_id).first() if not channel: raise HTTPException(status_code=404, detail="渠道不存在") test_content = f"**测试消息**\n\n渠道名称: {channel.channel_name}\n发送时间: 测试中..." try: if channel.channel_type == 'dingtalk_bot': payload = { "msgtype": "markdown", "markdown": { "title": "渠道测试", "text": test_content } } else: # wecom_bot payload = { "msgtype": "markdown", "markdown": { "content": test_content } } async with httpx.AsyncClient(timeout=10) as client: response = await client.post(channel.webhook_url, json=payload) result = response.json() # 钉钉返回 errcode=0,企微返回 errcode=0 if result.get('errcode') == 0: return {"success": True, "message": "发送成功"} else: return {"success": False, "message": f"发送失败: {result}"} except Exception as e: return {"success": False, "message": f"发送失败: {str(e)}"} # ==================== Helpers ==================== def format_channel(channel: NotificationChannel) -> dict: """格式化渠道数据""" return { "id": channel.id, "tenant_id": channel.tenant_id, "channel_name": channel.channel_name, "channel_type": channel.channel_type, "webhook_url": channel.webhook_url, "description": channel.description, "is_enabled": channel.is_enabled, "created_at": channel.created_at, "updated_at": channel.updated_at }