feat: 集成飞书通知和机器人对话系统
- 新增通知系统 (notifications 表、服务、API) - 新增飞书定时任务结果推送 (webhook + 应用消息) - 新增飞书应用消息发送服务 (feishu_app_service) - 新增飞书 WebSocket 长连接事件监听 (苹果应用) - 新增飞书账号绑定/解绑 API - 新增橙子飞书机器人 (独立 WS 连接,固定路由到橙子助手 Agent) - 执行记录添加 schedule_id,用户添加飞书绑定字段 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
128
backend/app/services/notification_service.py
Normal file
128
backend/app/services/notification_service.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""通知服务 — 创建与查询系统通知"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.notification import Notification
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_notification(
|
||||
db: Session,
|
||||
user_id: str,
|
||||
title: str,
|
||||
content: Optional[str] = None,
|
||||
category: str = "system",
|
||||
ref_type: Optional[str] = None,
|
||||
ref_id: Optional[str] = None,
|
||||
) -> Notification:
|
||||
"""创建一条通知。
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
user_id: 接收用户 ID
|
||||
title: 通知标题
|
||||
content: 通知正文(可选)
|
||||
category: 分类,如 schedule / alert / system
|
||||
ref_type: 关联对象类型
|
||||
ref_id: 关联对象 ID
|
||||
|
||||
Returns:
|
||||
创建的 Notification ORM 对象
|
||||
"""
|
||||
notification = Notification(
|
||||
user_id=user_id,
|
||||
title=title,
|
||||
content=content,
|
||||
category=category,
|
||||
ref_type=ref_type,
|
||||
ref_id=ref_id,
|
||||
)
|
||||
db.add(notification)
|
||||
db.flush()
|
||||
logger.info("通知已创建: user=%s title=%s category=%s", user_id, title, category)
|
||||
return notification
|
||||
|
||||
|
||||
def get_user_notifications(
|
||||
db: Session,
|
||||
user_id: str,
|
||||
unread_only: bool = False,
|
||||
category: Optional[str] = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0,
|
||||
) -> List[Notification]:
|
||||
"""获取用户的通知列表(按创建时间倒序)。
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
user_id: 用户 ID
|
||||
unread_only: 仅未读
|
||||
category: 按分类过滤
|
||||
limit: 分页大小
|
||||
offset: 分页偏移
|
||||
|
||||
Returns:
|
||||
通知列表
|
||||
"""
|
||||
query = db.query(Notification).filter(Notification.user_id == user_id)
|
||||
|
||||
if unread_only:
|
||||
query = query.filter(Notification.is_read == False) # noqa: E712
|
||||
if category:
|
||||
query = query.filter(Notification.category == category)
|
||||
|
||||
return query.order_by(Notification.created_at.desc()).offset(offset).limit(limit).all()
|
||||
|
||||
|
||||
def get_unread_count(db: Session, user_id: str) -> int:
|
||||
"""获取用户未读通知数。"""
|
||||
return (
|
||||
db.query(Notification)
|
||||
.filter(Notification.user_id == user_id, Notification.is_read == False) # noqa: E712
|
||||
.count()
|
||||
)
|
||||
|
||||
|
||||
def mark_as_read(db: Session, notification_id: str, user_id: str) -> Optional[Notification]:
|
||||
"""将指定通知标记为已读。"""
|
||||
notification = (
|
||||
db.query(Notification)
|
||||
.filter(Notification.id == notification_id, Notification.user_id == user_id)
|
||||
.first()
|
||||
)
|
||||
if not notification:
|
||||
return None
|
||||
notification.is_read = True
|
||||
db.flush()
|
||||
return notification
|
||||
|
||||
|
||||
def mark_all_as_read(db: Session, user_id: str) -> int:
|
||||
"""将用户所有通知标记为已读。返回更新的条数。"""
|
||||
count = (
|
||||
db.query(Notification)
|
||||
.filter(Notification.user_id == user_id, Notification.is_read == False) # noqa: E712
|
||||
.update({"is_read": True})
|
||||
)
|
||||
db.flush()
|
||||
return count
|
||||
|
||||
|
||||
def delete_notification(db: Session, notification_id: str, user_id: str) -> bool:
|
||||
"""删除一条通知。"""
|
||||
notification = (
|
||||
db.query(Notification)
|
||||
.filter(Notification.id == notification_id, Notification.user_id == user_id)
|
||||
.first()
|
||||
)
|
||||
if not notification:
|
||||
return False
|
||||
db.delete(notification)
|
||||
db.flush()
|
||||
return True
|
||||
Reference in New Issue
Block a user