import arlo
This commit is contained in:
25
database/Readme.md
Normal file
25
database/Readme.md
Normal 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
0
database/__init__.py
Normal file
BIN
database/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/__pycache__/database.cpython-312.pyc
Normal file
BIN
database/__pycache__/database.cpython-312.pyc
Normal file
Binary file not shown.
37
database/database.py
Normal file
37
database/database.py
Normal 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() # 确保会话关闭
|
||||
0
database/tcontentdispatch/__init__.py
Normal file
0
database/tcontentdispatch/__init__.py
Normal file
BIN
database/tcontentdispatch/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/tcontentdispatch/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/tcontentdispatch/__pycache__/curd.cpython-312.pyc
Normal file
BIN
database/tcontentdispatch/__pycache__/curd.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/tcontentdispatch/__pycache__/model.cpython-312.pyc
Normal file
BIN
database/tcontentdispatch/__pycache__/model.cpython-312.pyc
Normal file
Binary file not shown.
69
database/tcontentdispatch/curd.py
Normal file
69
database/tcontentdispatch/curd.py
Normal 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
|
||||
33
database/tcontentdispatch/model.py
Normal file
33
database/tcontentdispatch/model.py
Normal 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()
|
||||
0
database/thealthknowledge/__init__.py
Normal file
0
database/thealthknowledge/__init__.py
Normal file
BIN
database/thealthknowledge/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/thealthknowledge/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
38
database/thealthknowledge/health_knowledge.py
Normal file
38
database/thealthknowledge/health_knowledge.py
Normal 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()
|
||||
0
database/tscheduler/__init__.py
Normal file
0
database/tscheduler/__init__.py
Normal file
BIN
database/tscheduler/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/tscheduler/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/tscheduler/__pycache__/crud.cpython-312.pyc
Normal file
BIN
database/tscheduler/__pycache__/crud.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/tscheduler/__pycache__/model.cpython-312.pyc
Normal file
BIN
database/tscheduler/__pycache__/model.cpython-312.pyc
Normal file
Binary file not shown.
35
database/tscheduler/crud.py
Normal file
35
database/tscheduler/crud.py
Normal 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
|
||||
26
database/tscheduler/model.py
Normal file
26
database/tscheduler/model.py
Normal 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='任务逻辑的函数名称')
|
||||
0
database/ttaskqueue/__init__.py
Normal file
0
database/ttaskqueue/__init__.py
Normal file
BIN
database/ttaskqueue/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/ttaskqueue/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/ttaskqueue/__pycache__/curd.cpython-312.pyc
Normal file
BIN
database/ttaskqueue/__pycache__/curd.cpython-312.pyc
Normal file
Binary file not shown.
BIN
database/ttaskqueue/__pycache__/model.cpython-312.pyc
Normal file
BIN
database/ttaskqueue/__pycache__/model.cpython-312.pyc
Normal file
Binary file not shown.
21
database/ttaskqueue/curd.py
Normal file
21
database/ttaskqueue/curd.py
Normal 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
|
||||
|
||||
|
||||
18
database/ttaskqueue/model.py
Normal file
18
database/ttaskqueue/model.py
Normal 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='任务是否执行完成')
|
||||
Reference in New Issue
Block a user