feat(api): 新增核心 API 端点 + 数据库迁移

新增 3 个 API 端点:
- POST /tasks/{id}/receive: 确认接收 (PENDING→WIP)
- POST /tasks/{id}/reject: 品质驳回 + 返工闭环
- POST /tasks/{id}/transfer: 完工裂变转交 (多路分支+入库)

数据库迁移 (a1b2c3d4e5f6):
- tasks 表新增 received_at, completed_at, reject_reason, is_rework
- products 表新增 current_location_id
- 数据迁移: 已有任务状态小写→大写 (pending→PENDING 等)
This commit is contained in:
2026-08-04 17:03:43 +08:00
parent 8028be58c4
commit 9ec4f8efa7
2 changed files with 312 additions and 0 deletions

View File

@ -0,0 +1,102 @@
"""add task rework fields and product current_location
Revision ID: a1b2c3d4e5f6
Revises: 80f8cb64544a
Create Date: 2026-08-04 12:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'a1b2c3d4e5f6'
down_revision: Union[str, Sequence[str], None] = '80f8cb64544a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# === 数据迁移:将已有任务状态从小写转为大写 ===
op.execute("""
UPDATE tasks SET status = 'PENDING' WHERE lower(status) = 'pending';
""")
op.execute("""
UPDATE tasks SET status = 'WIP' WHERE lower(status) = 'wip';
""")
op.execute("""
UPDATE tasks SET status = 'COMPLETED' WHERE lower(status) = 'completed';
""")
op.execute("""
UPDATE tasks SET status = 'REJECTED' WHERE lower(status) = 'rejected';
""")
op.execute("""
UPDATE tasks SET status = 'ARCHIVED' WHERE lower(status) = 'archived';
""")
# === tasks 表新增字段 ===
op.add_column('tasks', sa.Column(
'received_at',
sa.DateTime(timezone=True),
nullable=True,
comment='操作员确认接收时间',
))
op.add_column('tasks', sa.Column(
'completed_at',
sa.DateTime(timezone=True),
nullable=True,
comment='任务完工转交时间',
))
op.add_column('tasks', sa.Column(
'reject_reason',
sa.String(length=500),
nullable=True,
comment='驳回原因',
))
op.add_column('tasks', sa.Column(
'is_rework',
sa.Boolean(),
nullable=False,
server_default=sa.text('false'),
comment='是否为返工任务',
))
# === products 表新增字段 ===
op.add_column('products', sa.Column(
'current_location_id',
sa.String(length=64),
nullable=True,
comment="当前持有者ID 或 'virtual_warehouse'(仓库)",
))
def downgrade() -> None:
"""Downgrade schema."""
# === products 表移除字段 ===
op.drop_column('products', 'current_location_id')
# === tasks 表移除字段 ===
op.drop_column('tasks', 'is_rework')
op.drop_column('tasks', 'reject_reason')
op.drop_column('tasks', 'completed_at')
op.drop_column('tasks', 'received_at')
# === 数据回迁:将任务状态从大写转回小写 ===
op.execute("""
UPDATE tasks SET status = 'pending' WHERE status = 'PENDING';
""")
op.execute("""
UPDATE tasks SET status = 'wip' WHERE status = 'WIP';
""")
op.execute("""
UPDATE tasks SET status = 'completed' WHERE status = 'COMPLETED';
""")
op.execute("""
UPDATE tasks SET status = 'rejected' WHERE status = 'REJECTED';
""")
op.execute("""
UPDATE tasks SET status = 'archived' WHERE status = 'ARCHIVED';
""")