For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 为 pl_plan 增加业务字段「创建日期」(create_date/createDate),在新增/编辑弹窗中可编辑;新增时取货/送达时间若为空则默认等于计划日期。
Architecture: MySQL 增量迁移回填存量;SQLAlchemy 模型 + Pydantic Create/Update/Read 贯通;创建接口缺省时服务端用当天。前端仅改 pl-plan-form-dialog(及仍在用的 plan/form.vue),列表/查看暂不展示。
Tech Stack: FastAPI + SQLAlchemy + Pydantic v2;Vue 3 + Element Plus;MySQL 5.7
Spec: docs/superpowers/specs/2026-07-27-pl-plan-create-date-design.md
| 文件 | 职责 |
|---|---|
backend/scripts/migrate_20260727_pl_plan_create_date.sql |
增量 DDL + 存量回填 |
docs/sql/shanxia2.0.sql |
pl_plan 建表定义同步加 create_date |
backend/app/models/pl_models.py |
PlPlan.create_date |
backend/app/schemas/pl/plan.py |
Create/Update/Read/CreateFromContractRequest 的 createDate |
backend/app/api/pl/plan.py |
create / create-full / create-from-contract / update 写入 |
backend/tests/test_pl_plan_create_date.py |
缺省日期解析纯函数单测(若抽 helper)或创建默认行为说明见 Task 3 |
web-admin/src/api/pl/index.ts |
Plan / PlanCreateFullPayload 增加 createDate |
web-admin/src/components/business/pl-plan-form-dialog/index.vue |
UI + 默认值 + 提交 |
web-admin/src/views/pl/plan/form.vue |
与弹窗对齐(路由仍挂载时) |
Files:
backend/scripts/migrate_20260727_pl_plan_create_date.sqlModify: docs/sql/shanxia2.0.sql(CREATE TABLE pl_plan 中 plan_date 前插入 create_date)
[ ] Step 1: 写增量迁移脚本
-- backend/scripts/migrate_20260727_pl_plan_create_date.sql
-- 运输计划:业务创建日期 create_date(与系统 create_time 独立)
ALTER TABLE `pl_plan`
ADD COLUMN `create_date` date DEFAULT NULL COMMENT '创建日期' AFTER `plan_no`;
UPDATE `pl_plan`
SET `create_date` = DATE(`create_time`)
WHERE `create_date` IS NULL AND `create_time` IS NOT NULL;
UPDATE `pl_plan`
SET `create_date` = CURDATE()
WHERE `create_date` IS NULL;
ALTER TABLE `pl_plan`
MODIFY COLUMN `create_date` date NOT NULL COMMENT '创建日期';
docs/sql/shanxia2.0.sql在 CREATE TABLE \pl_plan`中,于plan_no行之后、plan_date` 行之前增加:
`create_date` date NOT NULL COMMENT '创建日期',
(不必改历史 INSERT 行;本地/环境以迁移脚本为准。若本机有库,执行 Step 1 脚本。)
git add backend/scripts/migrate_20260727_pl_plan_create_date.sql docs/sql/shanxia2.0.sql
git commit -m "$(cat <<'EOF'
chore(pl): add pl_plan.create_date migration and DDL snapshot
EOF
)"
Files:
backend/app/models/pl_models.py(PlPlan,约 plan_no / plan_date 附近)Modify: backend/app/schemas/pl/plan.py
[ ] Step 1: 模型增加列
在 plan_no 与 plan_date 之间:
plan_no = Column(String(32), nullable=False, unique=True, comment="计划订单编号 POYYYYMMDD###")
create_date = Column(Date, nullable=False, comment="创建日期")
plan_date = Column(Date, nullable=False, comment="计划日期")
PlanCreate(约 126 行)增加(紧挨 plan_date):
class PlanCreate(PlanBase):
create_date: Optional[date] = Field(
default=None, alias="createDate", description="创建日期;缺省则服务端用当天"
)
plan_date: date = Field(..., alias="planDate", description="计划日期")
products: Optional[list[PlanProductCreate]] = Field(default=None, description="产品明细列表")
model_config = {"populate_by_name": True}
PlanUpdate 增加:
create_date: Optional[date] = Field(default=None, alias="createDate")
plan_date: Optional[date] = Field(default=None, alias="planDate")
PlanRead 在 plan_date 旁增加:
create_date: date = Field(alias="createDate")
plan_date: date = Field(alias="planDate")
CreateFromContractRequest 增加可选:
create_date: Optional[date] = Field(
default=None, alias="createDate", description="创建日期;缺省当天"
)
plan_date: date = Field(..., alias="planDate", description="计划日期")
确保文件顶部已有 Optional、date 导入。
git add backend/app/models/pl_models.py backend/app/schemas/pl/plan.py
git commit -m "$(cat <<'EOF'
feat(pl): add create_date to PlPlan model and schemas
EOF
)"
Files:
backend/app/api/pl/plan.py(create_plan、create_plan_full、create_from_contract、update_plan)说明:update_plan 已用 payload.model_dump(exclude_unset=True) + setattr,Schema 加字段后更新路径自动生效,无需改循环逻辑。
在 plan.py 模块级增加:
from datetime import date
def _resolve_create_date(value: date | None) -> date:
return value or date.today()
(若文件已 from datetime import date,勿重复导入。)
在 PlPlan(...) 构造处,plan_date=payload.plan_date 旁增加:
create_date=_resolve_create_date(getattr(payload, "create_date", None)),
plan_date=payload.plan_date,
覆盖:
create_plan(约 396–400 行)create_plan_full(约 478–482 行)create_from_contract 内 PlPlan(...)(约 598–602 行)— 使用 payload.create_date# backend/tests/test_pl_plan_create_date.py
from datetime import date
from app.api.pl.plan import _resolve_create_date
def test_resolve_create_date_uses_explicit():
assert _resolve_create_date(date(2026, 1, 15)) == date(2026, 1, 15)
def test_resolve_create_date_defaults_today():
assert _resolve_create_date(None) == date.today()
Run: cd backend && source venv/bin/activate && pytest tests/test_pl_plan_create_date.py -v
Expected: PASS
git add backend/app/api/pl/plan.py backend/tests/test_pl_plan_create_date.py
git commit -m "$(cat <<'EOF'
feat(pl): persist create_date on plan create APIs
EOF
)"
Files:
Modify: web-admin/src/api/pl/index.ts
[ ] Step 1: 类型增加 createDate
Plan 接口(约 planDate 旁):
planNo: string
createDate: string
planDate: string
PlanCreateFullPayload:
contractId: string
createDate: string
planDate: string
createFromContract 可选扩展(若合同入口会传):
createFromContract: (data: {
contractId: string
planDate: string
createDate?: string
followUserId?: string
followUserName?: string
}) =>
git add web-admin/src/api/pl/index.ts
git commit -m "$(cat <<'EOF'
feat(web-admin): add createDate to transport plan API types
EOF
)"
Files:
Modify: web-admin/src/components/business/pl-plan-form-dialog/index.vue
[ ] Step 1: 模板 — 计划日期前加创建日期
在基础信息 form-grid-4 内,「计划日期」el-form-item 之前插入:
<el-form-item label="创建日期" prop="createDate">
<el-date-picker
v-model="formData.createDate"
type="date"
placeholder="选择日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
辅助函数(script 内):
function todayYmd(): string {
return new Date().toISOString().split('T')[0]
}
formData 初始:
createDate: todayYmd(),
planDate: todayYmd(),
// ...
pickupTime: '', // reset 时再按计划日期填充
deliveryTime: '',
basicRules:
createDate: [{ required: true, message: '请选择创建日期', trigger: 'change' }],
planDate: [{ required: true, message: '请选择计划日期', trigger: 'change' }],
resetForm 的 Object.assign:
createDate: todayYmd(),
planDate: todayYmd(),
// ...其它字段同现有...
pickupTime: todayYmd(), // 与默认 planDate 相同;用户改后不再联动
deliveryTime: todayYmd(),
注意:resetForm 里先算一次 const d = todayYmd(),createDate/planDate/pickupTime/deliveryTime 都用同一个 d,避免跨日边界不一致。
loadPlanForEdit 的 Object.assign:
createDate: data.createDate || formatDate(data.createTime) || todayYmd(),
planDate: data.planDate,
// pickup/delivery 仍用接口值,空则保持空(不要用计划日期覆盖)
pickupTime: formatDate(data.pickupTime) || '',
deliveryTime: formatDate(data.deliveryTime) || '',
buildPlanCommonPayload:
createDate: formData.createDate,
planDate: formData.planDate,
确认 没有 对 planDate 的 watch 去改取货/送达。
createTime 未变。git add web-admin/src/components/business/pl-plan-form-dialog/index.vue
git commit -m "$(cat <<'EOF'
feat(web-admin): add createDate and default pickup/delivery in plan form dialog
EOF
)"
Files:
Modify: web-admin/src/views/pl/plan/form.vue
[ ] Step 1: 与弹窗同构的最小改动
若该页仍可从路由 /pl/plan/form 打开:同样在计划日期前加「创建日期」,formData/rules/提交/reset 增加 createDate;新增时 pickupTime/deliveryTime 空则填 planDate。
若页面已废弃不用,可在 PR 说明中注明「未改 form.vue」,并跳过本 Task。
git add web-admin/src/views/pl/plan/form.vue
git commit -m "$(cat <<'EOF'
feat(web-admin): align plan form page with createDate field
EOF
)"
Files: 无强制新文件
cd backend && source venv/bin/activate && python scripts/check_field_consistency.py
若报 createDate/create_date 相关缺失,按脚本提示补齐后重跑至通过或确认脚本未覆盖 pl_plan。
./scripts/update-graph.sh all
或按仓库约定:graphify update .(根目录若配置)/ 仅 backend+web-admin。
git add backend/graphify-out web-admin/graphify-out graphify-out 2>/dev/null || true
git status
# 仅提交实际变更的 graph 文件
git commit -m "$(cat <<'EOF'
chore: update graphify after pl_plan create_date
EOF
)"
| Spec 项 | Task |
|---|---|
D1 create_date 独立字段 |
1–3 |
| D2 存量回填 | 1 |
| D3 仅弹窗 UI | 5(列表/查看不改) |
| D4 新增取货/送达默认计划日期、不联动 | 5 |
| D5 计划日期默认当天不变 | 5 |
| D6 安排弹窗不改 | —(无 Task) |
| 后端缺省当天 | 3 |
| 验收:createTime 不被改写 | 3(未写 create_time)+ 5 手工 |
无 TBD/TODO;步骤含具体 SQL/代码/命令。
全程使用:DB create_date、API/前端 createDate、Python create_date / alias createDate。