WebhockTransfer/app/db.py
houhuan 2bc7460f1f feat: 初始化Webhook中继系统项目
- 添加FastAPI应用基础结构,包括主入口、路由和模型定义
- 实现Webhook接收端点(/webhook/{namespace})和健康检查(/health)
- 添加管理后台路由和模板,支持端点、目标、渠道和模板管理
- 包含SQLite数据库模型定义和初始化逻辑
- 添加日志记录和统计服务
- 包含Dockerfile和配置示例文件
- 添加项目文档,包括设计、流程图和验收标准
2025-12-21 18:43:12 +08:00

103 lines
4.4 KiB
Python

from sqlalchemy import create_engine, Column, Integer, String, JSON, Boolean, Table, ForeignKey, DateTime, Text
from sqlalchemy.orm import declarative_base, sessionmaker, relationship, backref
import os
from datetime import datetime
Base = declarative_base()
class Target(Base):
__tablename__ = 'targets'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True, index=True)
url = Column(String)
timeout_ms = Column(Integer, default=5000)
class NotificationChannel(Base):
__tablename__ = 'notification_channels'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
channel_type = Column(String) # feishu, wecom
webhook_url = Column(String)
class MessageTemplate(Base):
__tablename__ = 'message_templates'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True) # 方便识别,如 "收款成功通知"
template_content = Column(Text) # "收到{amt}元"
class WebhookEndpoint(Base):
__tablename__ = 'webhook_endpoints'
id = Column(Integer, primary_key=True)
namespace = Column(String, unique=True, index=True)
description = Column(String, nullable=True)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
rules = relationship("ProcessingRule", back_populates="endpoint", cascade="all, delete-orphan")
class ProcessingRule(Base):
__tablename__ = 'processing_rules'
id = Column(Integer, primary_key=True)
endpoint_id = Column(Integer, ForeignKey('webhook_endpoints.id'))
endpoint = relationship("WebhookEndpoint", back_populates="rules")
# Tree structure support
parent_rule_id = Column(Integer, ForeignKey('processing_rules.id'), nullable=True)
children = relationship("ProcessingRule", backref=backref('parent', remote_side=[id]), cascade="all, delete-orphan")
priority = Column(Integer, default=0) # Higher executes first (if we want ordering)
match_field = Column(String) # e.g. "trans_order_info.remark" or "event_define_no"
operator = Column(String, default="eq") # eq, neq, contains, startswith, regex
match_value = Column(String) # e.g. "imcgcd03" or "pay.success"
actions = relationship("RuleAction", back_populates="rule", cascade="all, delete-orphan")
class RuleAction(Base):
__tablename__ = 'rule_actions'
id = Column(Integer, primary_key=True)
rule_id = Column(Integer, ForeignKey('processing_rules.id'))
rule = relationship("ProcessingRule", back_populates="actions")
action_type = Column(String) # "forward" or "notify"
# Forward params
target_id = Column(Integer, ForeignKey('targets.id'), nullable=True)
target = relationship("Target")
# Notify params
channel_id = Column(Integer, ForeignKey('notification_channels.id'), nullable=True)
channel = relationship("NotificationChannel")
template_id = Column(Integer, ForeignKey('message_templates.id'), nullable=True)
template = relationship("MessageTemplate")
# Extra params for templating (e.g. {"pay_method": "微信"})
template_vars = Column(JSON, nullable=True)
class RequestLog(Base):
__tablename__ = 'request_logs'
id = Column(Integer, primary_key=True)
namespace = Column(String, index=True)
remark = Column(String, nullable=True) # 保留用于快速筛选,可选
event_no = Column(String, nullable=True) # 保留用于快速筛选,可选
raw_body = Column(JSON)
received_at = Column(DateTime, default=datetime.utcnow)
status = Column(String) # success, error
delivery_logs = relationship("DeliveryLog", back_populates="request_log", cascade="all, delete-orphan")
class DeliveryLog(Base):
__tablename__ = 'delivery_logs'
id = Column(Integer, primary_key=True)
request_id = Column(Integer, ForeignKey('request_logs.id'))
target_name = Column(String)
type = Column(String) # relay, notify
status = Column(String) # success, failed
response_summary = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
request_log = relationship("RequestLog", back_populates="delivery_logs")
DB_PATH = os.getenv("DB_PATH", "sqlite:///./config/data.db")
engine = create_engine(DB_PATH, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def init_db():
Base.metadata.create_all(bind=engine)