From b14773f81b54c0d24b7633232784781f63d58613 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Mon, 10 Aug 2026 17:37:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E5=8D=8F=E5=90=8C?= =?UTF-8?q?=E7=95=99=E8=A8=80=E6=9D=BF=20=E2=80=94=20ProductMessage=20?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=20+=20API=20+=20=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ProductMessage 模型,挂载在 products.id(ON DELETE CASCADE) - GET/POST /products/{id}/messages 接口 - 注册到 Alembic 自动发现 --- .../g1h2i3j4k5l6_add_product_messages.py | 33 +++++++++++++++++++ backend/app/models/__init__.py | 2 ++ backend/app/models/message.py | 32 ++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 backend/alembic/versions/g1h2i3j4k5l6_add_product_messages.py create mode 100644 backend/app/models/message.py diff --git a/backend/alembic/versions/g1h2i3j4k5l6_add_product_messages.py b/backend/alembic/versions/g1h2i3j4k5l6_add_product_messages.py new file mode 100644 index 0000000..70a4b4d --- /dev/null +++ b/backend/alembic/versions/g1h2i3j4k5l6_add_product_messages.py @@ -0,0 +1,33 @@ +"""add_product_messages + +Revision ID: g1h2i3j4k5l6 +Revises: c9d0e1f2a3b4 +Create Date: 2026-08-10 + +""" +from typing import Sequence, Union +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +revision: str = "g1h2i3j4k5l6" +down_revision: Union[str, None] = "c9d0e1f2a3b4" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "product_messages", + sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), + sa.Column("product_id", postgresql.UUID(as_uuid=True), + sa.ForeignKey("products.id", ondelete="CASCADE"), + index=True, nullable=False), + sa.Column("operator_id", sa.String(50), nullable=False), + sa.Column("content", sa.Text, nullable=False), + sa.Column("created_at", sa.DateTime, nullable=True), + ) + + +def downgrade() -> None: + op.drop_table("product_messages") diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 5a393ec..87dcd1f 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -6,6 +6,7 @@ from app.models.task import Task, TaskRecord from app.models.task_log import TaskLog from app.models.notification import Notification from app.models.app_version import AppVersion +from app.models.message import ProductMessage __all__ = [ "Base", "ProductionOrder", @@ -15,4 +16,5 @@ __all__ = [ "TaskLog", "Notification", "AppVersion", + "ProductMessage", ] diff --git a/backend/app/models/message.py b/backend/app/models/message.py new file mode 100644 index 0000000..71102da --- /dev/null +++ b/backend/app/models/message.py @@ -0,0 +1,32 @@ +"""产品协同留言板模型""" +import uuid +from datetime import datetime +from sqlalchemy import String, Text, DateTime, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Mapped, mapped_column + +from app.models.base import Base + + +class ProductMessage(Base): + __tablename__ = "product_messages" + + id: Mapped[uuid.UUID] = mapped_column( + UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, + ) + product_id: Mapped[uuid.UUID] = mapped_column( + UUID(as_uuid=True), + ForeignKey("products.id", ondelete="CASCADE"), + index=True, + nullable=False, + comment="所属产品 ID", + ) + operator_id: Mapped[str] = mapped_column( + String(50), nullable=False, comment="留言人姓名或工号", + ) + content: Mapped[str] = mapped_column( + Text, nullable=False, comment="留言内容", + ) + created_at: Mapped[datetime] = mapped_column( + DateTime, default=datetime.utcnow, comment="留言时间", + )