import arlo

This commit is contained in:
konjacpotato
2025-11-05 21:00:19 +08:00
commit 2c8426d543
69 changed files with 789 additions and 0 deletions

25
database/Readme.md Normal file
View File

@ -0,0 +1,25 @@
# 数据库模块
数据库模块主要分为三个部分database.py、model.py、crud.py。
- database.py包含数据库连接和会话管理。
- model.py定义数据库模型实体类包括表结构、字段类型、约束等。
- crud.py定义数据库操作函数包括增删改查等。
## 使用示例
```python
from database.database import get_session
from database.tscheduler.crud import get_task_by_id
with get_session() as db:
task = get_task_by_id(db, 1)
print(task)
print(task.id)
```
## 新增数据表流程
新增数据表主要涉及模型定义和CRUD实现。
1. 在database包下创建以数据表命名的包名然后在里面创建model.py和crud.py。
2. model.py 定义数据库模型
3. crud.py 定义数据库操作函数

0
database/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

37
database/database.py Normal file
View File

@ -0,0 +1,37 @@
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from log.log_manager import logger
Base = declarative_base()
DATABASE_URL = 'postgresql+psycopg://postgres:K8u3fg0o@47.119.128.161:60001/squirrel'
engine = create_engine(
DATABASE_URL,
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=1800, # 防止数据库端连接过期
connect_args={
'connect_timeout': 15,
'keepalives_idle': 60,
'keepalives_interval': 10,
'keepalives_count': 5
}
)
Base.metadata.create_all(engine)
@contextmanager
def get_session():
session = sessionmaker(bind=engine)()
try:
yield session
session.commit() # 自动提交成功的事务
except Exception as e:
session.rollback() # 异常时回滚
logger.error(f"Database operation failed: {str(e)}")
raise # 重新抛出异常
finally:
session.close() # 确保会话关闭

View File

View File

@ -0,0 +1,69 @@
from datetime import datetime, timedelta, timezone
from sqlalchemy import desc
from database.tcontentdispatch.model import TContentDispatch
def create_content(db, content: TContentDispatch):
db.add(content)
db.commit()
db.refresh(content)
return content
def create_or_update_content(db, content: TContentDispatch):
content_in_db = db.query(TContentDispatch).filter(TContentDispatch.id == content.id).first()
if content_in_db:
# Update existing content
db.commit() # Save changes to the database
db.refresh(content) # Refresh the object with the updated values
else:
# Create new content if not found in DB
db.add(content)
db.commit() # Save new content to the database
db.refresh(content) # Refresh to get the content's updated state (e.g., ID if it's auto-generated)
def get_content_by_id(db, content_id: int):
return db.query(TContentDispatch).filter(TContentDispatch.id == content_id).first()
def get_content_by_title_and_category(db, title: str, category: str):
return db.query(TContentDispatch).filter(TContentDispatch.title == title, TContentDispatch.category == category).first()
def get_contents_to_dispatch(db) -> list[TContentDispatch]:
return db.query(TContentDispatch).filter(TContentDispatch.is_sent == False).all()
def get_recent_24_hours_content_to_dispatch(db, category: str) -> TContentDispatch:
# 获取查询日期的起始和结束时间
end_time = datetime.now(timezone.utc)
start_time = end_time - timedelta(hours=24)
# 查询最近的一条未发送的内容
return db.query(TContentDispatch).filter(
TContentDispatch.category == category,
# TContentDispatch.is_sent == False,
TContentDispatch.creation_date >= start_time,
TContentDispatch.creation_date < end_time
).order_by(desc(TContentDispatch.creation_date)).first()
def finish_contents_to_dispatch(db, ids):
db.query(TContentDispatch).filter(TContentDispatch.id.in_(ids)).update({'is_sent': True})
db.commit()
def update_content(db, content_id: int, updates: dict):
content = db.query(TContentDispatch).filter(TContentDispatch.id == content_id).first()
if content:
for key, value in updates.items():
setattr(content, key, value)
db.commit()
db.refresh(content)
return content
def delete_content(db, content_id: int):
content = db.query(TContentDispatch).filter(TContentDispatch.id == content_id).first()
if content:
db.delete(content)
db.commit()
return content

View File

@ -0,0 +1,33 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Text, Boolean, TIMESTAMP, func
from sqlalchemy.dialects.postgresql import BIGINT
from dataclasses import dataclass
from database.database import Base
@dataclass
class TContentDispatch(Base):
__tablename__ = 't_content_dispatch'
id: int = Column(BIGINT, primary_key=True, autoincrement=True, comment='自动递增的唯一任务ID')
creation_date: datetime = Column(TIMESTAMP(timezone=True), server_default=func.now(), nullable=False, comment='记录数据条目的创建时间')
is_sent: bool = Column(Boolean, default=False, nullable=False, comment='表示数据条目是否已被发送')
category: str = Column(String(255), nullable=False, comment='类别')
title: str = Column(String(255), nullable=False, comment='标题')
cover_image: str = Column(Text, nullable=True, comment='封面')
poster_image: str = Column(Text, nullable=True, comment='海报')
opening_text: str = Column(Text, nullable=True, comment='开头语')
content: str = Column(Text, nullable=False, comment='内容')
ai_content: str = Column(Text, nullable=True, comment='AI编辑的内容')
closing_text: str = Column(Text, nullable=True, comment='结束语')
is_scheduled: bool = Column(Boolean, default=False, nullable=False, comment='是否设置为定时发送')
schedule_time: str = Column(TIMESTAMP, nullable=True, comment='定时发送的具体时间')
format: str = Column(String(50), nullable=True, comment='数据条目的格式')
ai_generate: int = Column(Integer, default=0, nullable=False, comment='是否AI生成。0否1部分参与2是。')
def __repr__(self):
return f"<TContentDispatch(id={self.id}, title={self.title}, category={self.category})>"
def get_creation_date_in_localtime(self) -> datetime:
# 将 UTC 时间转换为localtime
return self.creation_date.astimezone()

View File

View File

@ -0,0 +1,38 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from sqlalchemy import Column, String, TIMESTAMP, func, Boolean
from sqlalchemy.dialects.postgresql import BIGINT
from database.database import Base
@dataclass
class THealthKnowledge(Base):
__tablename__ = 't_health_knowledge'
id: int = Column(BIGINT, primary_key=True, autoincrement=True, comment='序号')
subject: Optional[str] = Column(String, nullable=True, comment='主题')
knowledge: Optional[str] = Column(String, nullable=True, comment='知识内容')
keywords: Optional[str] = Column(String, nullable=True, comment='关键字')
url: Optional[str] = Column(String, nullable=True, comment='链接')
is_used: bool = Column(Boolean, default=False, nullable=False, comment='表示数据条目是否已被使用')
create_time: datetime = Column(TIMESTAMP(timezone=True), server_default=func.now(), nullable=False, comment='创建时间')
def __repr__(self):
return (f"<THealthKnowledge(id={self.id}, "
f"subject={self.subject}, "
f"knowledge={self.knowledge}, "
f"create_time={self.create_time})>")
# 把未使用的数据按照create_time排序获取最老的1条数据
def get_oldest_unused_data(db):
unused_data = db.query(THealthKnowledge).filter(THealthKnowledge.is_used == False).order_by(
THealthKnowledge.create_time).first()
return unused_data
# 把数据标记为已使用
def mark_data_as_used(db, data):
data.is_used = True
db.commit()

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,35 @@
from database.tscheduler.model import TScheduler
def create_task(db, task: TScheduler):
db.add(task)
db.commit()
db.refresh(task)
return task
def get_task_by_id(db, task_id: int):
return db.query(TScheduler).filter(TScheduler.id == task_id).first()
def get_active_tasks(db):
return db.query(TScheduler).filter(TScheduler.active == True).all()
def get_tasks_by_executor(db, executor: str):
return db.query(TScheduler).filter(
TScheduler.executor == executor,
TScheduler.active == True
).all()
def update_task(db, task_id: int, updates: dict):
task = db.query(TScheduler).filter(TScheduler.id == task_id).first()
if task:
for key, value in updates.items():
setattr(task, key, value)
db.commit()
db.refresh(task)
return task
def delete_task(db, task_id: int):
task = db.query(TScheduler).filter(TScheduler.id == task_id).first()
if task:
db.delete(task)
db.commit()
return task

View File

@ -0,0 +1,26 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from sqlalchemy import Column, Integer, String, Boolean, Text, DateTime
from database.database import Base
@dataclass
class TScheduler(Base):
__tablename__ = 't_scheduler'
id: int = Column(Integer, primary_key=True, autoincrement=True, comment='自动递增的唯一任务ID')
task_name: str = Column(String(64), nullable=False, comment='任务名称')
trigger: str = Column(String(10), nullable=False, comment='调度方式interval、cron、date')
interval_seconds: Optional[int] = Column(Integer, nullable=True, comment='固定时间间隔(秒),用于 interval 类型')
cron_expression: Optional[str] = Column(String(255), nullable=True, comment='CRON 表达式,用于 cron 类型')
execution_date: Optional[datetime] = Column(DateTime, nullable=True, comment='执行时间,用于 date 类型')
task_payload: Optional[str] = Column(Text, nullable=True, comment='任务相关的参数或数据')
active: Optional[bool] = Column(Boolean, default=False, nullable=True, comment='任务状态,是否启用')
executor: Optional[str] = Column(String(32), nullable=True, comment='任务执行者')
handler: Optional[str] = Column(String(32), nullable=True, comment='任务执行程序')
last_run: Optional[datetime] = Column(DateTime, nullable=True, comment='上一次执行时间')
next_run: Optional[datetime] = Column(DateTime, nullable=True, comment='下一次执行时间')
create_time: datetime = Column(DateTime, default=datetime.utcnow, nullable=True, comment='创建时间')
update_time: datetime = Column(DateTime, default=datetime.utcnow, nullable=True, comment='更新时间')
module_path: Optional[str] = Column(String(255), nullable=True, comment='任务逻辑所在模块名称')
function_name: Optional[str] = Column(String(32), nullable=True, comment='任务逻辑的函数名称')

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,21 @@
from database.ttaskqueue.model import TTaskQueue
def create_task(db, task: TTaskQueue):
db.add(task)
db.commit()
db.refresh(task)
return task
def get_tasks_to_finish(db) -> list[TTaskQueue]:
return db.query(TTaskQueue).filter(TTaskQueue.finished == False).all()
def finish_task(db, task_id: int):
task = db.query(TTaskQueue).filter(TTaskQueue.id == task_id).first()
if task:
task.finished = True
db.commit()
db.refresh(task)
return task

View File

@ -0,0 +1,18 @@
from datetime import datetime
from sqlalchemy import Column, String, Boolean, TIMESTAMP, func
from sqlalchemy.dialects.postgresql import BIGINT
from dataclasses import dataclass
from database.database import Base
@dataclass
class TTaskQueue(Base):
__tablename__ = 't_task_queue'
id: int = Column(BIGINT, primary_key=True, autoincrement=True, comment='自动递增的唯一任务ID')
create_time: datetime = Column(TIMESTAMP(timezone=True), server_default=func.now(), nullable=False, comment='任务创建时间')
task_name: str = Column(String, nullable=True, comment='任务名称')
module_path: str = Column(String, nullable=True, comment='任务模块路径')
function_name: str = Column(String, nullable=True, comment='任务函数名称')
scheduler: str = Column(String, nullable=True, comment='任务执行者名称')
finished: bool = Column(Boolean, default=False, nullable=False, comment='任务是否执行完成')