chore: 更新后端依赖,初始化数据库迁移与 MOM 外部系统连接
This commit is contained in:
@ -0,0 +1,85 @@
|
|||||||
|
"""init: production_orders, products, tasks, task_logs
|
||||||
|
|
||||||
|
Revision ID: 80f8cb64544a
|
||||||
|
Revises:
|
||||||
|
Create Date: 2026-08-04 10:08:19.670158
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '80f8cb64544a'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = None
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('production_orders',
|
||||||
|
sa.Column('id', sa.UUID(), nullable=False),
|
||||||
|
sa.Column('order_no', sa.String(length=64), nullable=False, comment='订单编号'),
|
||||||
|
sa.Column('customer_info', sa.String(length=500), nullable=True, comment='客户信息'),
|
||||||
|
sa.Column('status', sa.String(length=50), nullable=False, comment='订单状态'),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, comment='创建时间'),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_production_orders_order_no'), 'production_orders', ['order_no'], unique=True)
|
||||||
|
op.create_table('products',
|
||||||
|
sa.Column('id', sa.UUID(), nullable=False),
|
||||||
|
sa.Column('serial_number', sa.String(length=16), nullable=False, comment='产品序列号(16位)'),
|
||||||
|
sa.Column('order_id', sa.UUID(), nullable=False, comment='所属订单ID'),
|
||||||
|
sa.Column('material_id', sa.String(length=64), nullable=True, comment='物料ID(逻辑外键→老系统)'),
|
||||||
|
sa.Column('parent_product_id', sa.UUID(), nullable=True, comment='父产品ID'),
|
||||||
|
sa.Column('status', sa.String(length=50), nullable=False, comment='产品状态'),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, comment='创建时间'),
|
||||||
|
sa.ForeignKeyConstraint(['order_id'], ['production_orders.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['parent_product_id'], ['products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_products_serial_number'), 'products', ['serial_number'], unique=True)
|
||||||
|
op.create_table('tasks',
|
||||||
|
sa.Column('id', sa.UUID(), nullable=False),
|
||||||
|
sa.Column('product_id', sa.UUID(), nullable=False, comment='所属产品ID'),
|
||||||
|
sa.Column('parent_task_id', sa.UUID(), nullable=True, comment='父任务ID'),
|
||||||
|
sa.Column('task_name', sa.String(length=200), nullable=False, comment='任务名称'),
|
||||||
|
sa.Column('assignee_id', sa.String(length=64), nullable=True, comment='负责人ID(逻辑外键→老系统)'),
|
||||||
|
sa.Column('status', sa.String(length=50), nullable=False, comment='任务状态'),
|
||||||
|
sa.Column('notify_parent_on_complete', sa.Boolean(), nullable=False, comment='完成后是否通知父任务'),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, comment='创建时间'),
|
||||||
|
sa.ForeignKeyConstraint(['parent_task_id'], ['tasks.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_tasks_parent_task_id'), 'tasks', ['parent_task_id'], unique=False)
|
||||||
|
op.create_table('task_logs',
|
||||||
|
sa.Column('id', sa.UUID(), nullable=False),
|
||||||
|
sa.Column('task_id', sa.UUID(), nullable=False, comment='所属任务ID'),
|
||||||
|
sa.Column('operator_id', sa.String(length=64), nullable=True, comment='操作人ID(逻辑外键→老系统)'),
|
||||||
|
sa.Column('action_type', sa.String(length=50), nullable=False, comment='操作类型'),
|
||||||
|
sa.Column('remark', sa.Text(), nullable=True, comment='备注'),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, comment='创建时间'),
|
||||||
|
sa.ForeignKeyConstraint(['task_id'], ['tasks.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_task_logs_task_id'), 'task_logs', ['task_id'], unique=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_index(op.f('ix_task_logs_task_id'), table_name='task_logs')
|
||||||
|
op.drop_table('task_logs')
|
||||||
|
op.drop_index(op.f('ix_tasks_parent_task_id'), table_name='tasks')
|
||||||
|
op.drop_table('tasks')
|
||||||
|
op.drop_index(op.f('ix_products_serial_number'), table_name='products')
|
||||||
|
op.drop_table('products')
|
||||||
|
op.drop_index(op.f('ix_production_orders_order_no'), table_name='production_orders')
|
||||||
|
op.drop_table('production_orders')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@ -1,7 +1,15 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from app.api.v1.endpoints.products import router as products_router
|
||||||
|
from app.api.v1.endpoints.tasks import router as tasks_router
|
||||||
|
from app.api.v1.endpoints.orders import router as orders_router
|
||||||
|
from app.api.v1.endpoints.dashboard import router as dashboard_router
|
||||||
|
from app.api.v1.endpoints.auth import router as auth_router
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
|
||||||
# 后续在此注册子路由
|
api_router.include_router(auth_router)
|
||||||
# from app.api.v1 import users, products, orders, ...
|
api_router.include_router(dashboard_router)
|
||||||
# api_router.include_router(users.router, prefix="/users", tags=["用户管理"])
|
api_router.include_router(orders_router)
|
||||||
|
api_router.include_router(products_router)
|
||||||
|
api_router.include_router(tasks_router)
|
||||||
|
|||||||
24
backend/app/core/mom_database.py
Normal file
24
backend/app/core/mom_database.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""MOM 系统数据库 — 只读连接,用于登录验证 sys_user 表"""
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker, Session
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
# MOM 数据库连接(同步引擎,仅用于登录验证)
|
||||||
|
import os
|
||||||
|
# Docker 容器内用 host.docker.internal 访问宿主机
|
||||||
|
MOM_HOST = os.environ.get("MOM_DB_HOST", "host.docker.internal")
|
||||||
|
MOM_PORT = os.environ.get("MOM_DB_PORT", "5435")
|
||||||
|
MOM_DATABASE_URL = f"postgresql://prod_user:StrongPassword123!@{MOM_HOST}:{MOM_PORT}/inventory_system"
|
||||||
|
|
||||||
|
mom_engine = create_engine(
|
||||||
|
MOM_DATABASE_URL,
|
||||||
|
echo=False,
|
||||||
|
poolclass=NullPool, # 登录频率低,不用连接池
|
||||||
|
)
|
||||||
|
|
||||||
|
MomSessionLocal = sessionmaker(
|
||||||
|
mom_engine,
|
||||||
|
class_=Session,
|
||||||
|
autocommit=False,
|
||||||
|
autoflush=False,
|
||||||
|
)
|
||||||
@ -4,7 +4,6 @@ from app.models.production_order import ProductionOrder
|
|||||||
from app.models.product import Product
|
from app.models.product import Product
|
||||||
from app.models.task import Task
|
from app.models.task import Task
|
||||||
from app.models.task_log import TaskLog
|
from app.models.task_log import TaskLog
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Base",
|
"Base",
|
||||||
"ProductionOrder",
|
"ProductionOrder",
|
||||||
|
|||||||
@ -36,3 +36,10 @@ typing_extensions==4.16.0
|
|||||||
uvicorn==0.52.1
|
uvicorn==0.52.1
|
||||||
watchfiles==1.2.0
|
watchfiles==1.2.0
|
||||||
websockets==17.0.1
|
websockets==17.0.1
|
||||||
|
|
||||||
|
# QR code generation
|
||||||
|
qrcode[pil]==8.2
|
||||||
|
Pillow==12.3.0
|
||||||
|
|
||||||
|
# MOM 登录对接 — Werkzeug scrypt 密码验证
|
||||||
|
werkzeug==3.0.6
|
||||||
|
|||||||
Reference in New Issue
Block a user