Scaffold V2 backend foundation

This commit is contained in:
2026-07-06 17:25:06 +08:00
parent 44af745828
commit 17cba7cf61
42 changed files with 1459 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
from app.models.admin import Admin, Role
from app.models.ai_config import ModelConfig, Prompt, SystemConfig
from app.models.base import Base
from app.models.chat import ChatMessage, ChatSession
from app.models.knowledge import Knowledge, UserKnowledgePermission
from app.models.logs import AiRequestLog, OperationLog
from app.models.user import User
__all__ = [
"Admin",
"AiRequestLog",
"Base",
"ChatMessage",
"ChatSession",
"Knowledge",
"ModelConfig",
"OperationLog",
"Prompt",
"Role",
"SystemConfig",
"User",
"UserKnowledgePermission",
]

View File

@@ -0,0 +1,33 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin
class Role(Base, TimestampMixin):
__tablename__ = "sys_role"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
code: Mapped[str] = mapped_column(String(50), unique=True, index=True, nullable=False)
name: Mapped[str] = mapped_column(String(50), nullable=False)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
admins: Mapped[list["Admin"]] = relationship("Admin", back_populates="role")
class Admin(Base, TimestampMixin):
__tablename__ = "sys_admin"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
username: Mapped[str] = mapped_column(String(50), unique=True, index=True, nullable=False)
password: Mapped[str] = mapped_column(String(255), nullable=False)
name: Mapped[str] = mapped_column(String(50), nullable=False)
role_id: Mapped[int | None] = mapped_column(ForeignKey("sys_role.id"), nullable=True)
status: Mapped[int] = mapped_column(default=1, nullable=False)
last_login_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
role: Mapped[Role | None] = relationship("Role", back_populates="admins")

View File

@@ -0,0 +1,43 @@
from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from sqlalchemy import BigInteger, DateTime, Integer, Numeric, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
class Prompt(Base):
__tablename__ = "sys_prompt"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
prompt_content: Mapped[str] = mapped_column(Text, nullable=False)
updated_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)
class ModelConfig(Base):
__tablename__ = "sys_model"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
provider: Mapped[str] = mapped_column(String(50), nullable=False)
model_name: Mapped[str] = mapped_column(String(100), nullable=False)
api_url: Mapped[str] = mapped_column(String(255), nullable=False)
api_key: Mapped[str] = mapped_column(Text, nullable=False)
temperature: Mapped[Decimal | None] = mapped_column(Numeric(4, 2), nullable=True)
max_token: Mapped[int | None] = mapped_column(Integer, nullable=True)
timeout_second: Mapped[int] = mapped_column(Integer, default=30, nullable=False)
enabled: Mapped[int] = mapped_column(default=0, nullable=False)
class SystemConfig(Base):
__tablename__ = "sys_system_config"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
config_key: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
config_value: Mapped[str] = mapped_column(Text, nullable=False)
description: Mapped[str | None] = mapped_column(String(255), nullable=True)
updated_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)

View File

@@ -0,0 +1,19 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class TimestampMixin:
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)
class SoftDeleteMixin:
is_deleted: Mapped[int] = mapped_column(default=0, nullable=False)

View File

@@ -0,0 +1,40 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin
class ChatSession(Base, TimestampMixin):
__tablename__ = "sys_chat_session"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("sys_user.id"), index=True, nullable=False)
title: Mapped[str] = mapped_column(String(100), nullable=False)
message_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
last_message_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
is_deleted: Mapped[int] = mapped_column(default=0, nullable=False)
user: Mapped["User"] = relationship("User", back_populates="chat_sessions")
messages: Mapped[list["ChatMessage"]] = relationship("ChatMessage", back_populates="session")
class ChatMessage(Base):
__tablename__ = "sys_chat_message"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
session_id: Mapped[int] = mapped_column(ForeignKey("sys_chat_session.id"), index=True, nullable=False)
user_id: Mapped[int] = mapped_column(ForeignKey("sys_user.id"), index=True, nullable=False)
role: Mapped[str] = mapped_column(String(20), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
message_status: Mapped[str] = mapped_column(String(20), default="FINISHED", nullable=False)
token_input: Mapped[int | None] = mapped_column(Integer, nullable=True)
token_output: Mapped[int | None] = mapped_column(Integer, nullable=True)
response_time_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
model_id: Mapped[int | None] = mapped_column(ForeignKey("sys_model.id"), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)
session: Mapped[ChatSession] = relationship("ChatSession", back_populates="messages")

View File

@@ -0,0 +1,37 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin
class Knowledge(Base, TimestampMixin):
__tablename__ = "sys_knowledge"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(100), nullable=False)
feishu_space_id: Mapped[str] = mapped_column(String(100), nullable=False)
feishu_node_id: Mapped[str] = mapped_column(String(100), nullable=False)
status: Mapped[int] = mapped_column(default=1, nullable=False)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
user_permissions: Mapped[list["UserKnowledgePermission"]] = relationship(
"UserKnowledgePermission", back_populates="knowledge"
)
class UserKnowledgePermission(Base, TimestampMixin):
__tablename__ = "sys_user_kb"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("sys_user.id"), index=True, nullable=False)
knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False)
effective_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
expired_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
created_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
user: Mapped["User"] = relationship("User", back_populates="knowledge_permissions")
knowledge: Mapped[Knowledge] = relationship("Knowledge", back_populates="user_permissions")

View File

@@ -0,0 +1,43 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
class AiRequestLog(Base):
__tablename__ = "sys_ai_request_log"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
session_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True)
message_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True)
user_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True)
model_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
prompt: Mapped[str | None] = mapped_column(Text, nullable=True)
knowledge_ids: Mapped[str | None] = mapped_column(String(500), nullable=True)
retrieve_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
input_token: Mapped[int | None] = mapped_column(Integer, nullable=True)
output_token: Mapped[int | None] = mapped_column(Integer, nullable=True)
total_token: Mapped[int | None] = mapped_column(Integer, nullable=True)
cost_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
status: Mapped[str] = mapped_column(String(20), nullable=False)
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)
class OperationLog(Base):
__tablename__ = "sys_operation_log"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
admin_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True)
module: Mapped[str] = mapped_column(String(50), nullable=False)
action: Mapped[str] = mapped_column(String(50), nullable=False)
target_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
before_json: Mapped[str | None] = mapped_column(Text, nullable=True)
after_json: Mapped[str | None] = mapped_column(Text, nullable=True)
ip: Mapped[str | None] = mapped_column(String(50), nullable=True)
result: Mapped[str] = mapped_column(String(20), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)

View File

@@ -0,0 +1,28 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, Integer, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, SoftDeleteMixin, TimestampMixin
class User(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "sys_user"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
phone: Mapped[str] = mapped_column(String(20), unique=True, index=True, nullable=False)
name: Mapped[str] = mapped_column(String(50), nullable=False)
nickname: Mapped[str | None] = mapped_column(String(50), nullable=True)
status: Mapped[int] = mapped_column(default=1, nullable=False)
daily_chat_limit: Mapped[int] = mapped_column(Integer, default=100, nullable=False)
daily_chat_used: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
effective_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
expired_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_login_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
knowledge_permissions: Mapped[list["UserKnowledgePermission"]] = relationship(
"UserKnowledgePermission", back_populates="user"
)
chat_sessions: Mapped[list["ChatSession"]] = relationship("ChatSession", back_populates="user")