- 添加FastAPI应用基础结构,包括主入口、路由和模型定义
- 实现Webhook接收端点(/webhook/{namespace})和健康检查(/health)
- 添加管理后台路由和模板,支持端点、目标、渠道和模板管理
- 包含SQLite数据库模型定义和初始化逻辑
- 添加日志记录和统计服务
- 包含Dockerfile和配置示例文件
- 添加项目文档,包括设计、流程图和验收标准
103 lines
4.4 KiB
Python
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)
|