Files
zkxw/docs/superpowers/plans/2026-05-24-pilot-application-phase1.md
T
2026-05-25 15:14:06 +08:00

25 KiB
Raw Blame History

中试服务申请单 Phase 1 实施计划

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: 实现中试服务申请单的完整 CRUD、患者信息管理、输注方案/计划管理,含后端 API + 前端列表页+详情Tab页。

Architecture: 独立后端模块方案(方案A)。新建 crud/pilot_application.py 和 api/pilot_application.py,在 models.py 中新增 4 个 Model。前端新增列表页+详情页,路由追加到 sqxp.ts。

Tech Stack: Python 3.9 / FastAPI / SQLAlchemy / Pydantic v2 / Vue 3 / TypeScript / Element Plus / Tailwind CSS 4

Spec: docs/superpowers/specs/2026-05-24-pilot-application-phase1-design.md


Task 1: SQL 迁移文件 + Backend Models

Files:

  • Create: docs/sql/pilot-application-migration.sql

  • Modify: backend/app/models/models.py

  • Step 1: 创建 SQL 迁移文件

创建 docs/sql/pilot-application-migration.sql,内容为 ALTER biz_pilot_application 增加 4 个字段 + 唯一索引:

-- 中试服务申请单 Phase 1 迁移
-- 为 biz_pilot_application 增加申请单号、状态、渠道缓存字段

ALTER TABLE biz_pilot_application
  ADD COLUMN application_no VARCHAR(64) DEFAULT NULL
    COMMENT '申请单号(自动生成,前缀ZSSQ)' AFTER id,
  ADD COLUMN application_status VARCHAR(16) NOT NULL DEFAULT 'dks'
    COMMENT 'dks待开始 jxz进行中 ywc已完成 yzf已作废' AFTER application_no,
  ADD COLUMN channel_id VARCHAR(64) DEFAULT NULL
    COMMENT '渠道ID(缓存自服务订单)' AFTER channel_service_order_id,
  ADD COLUMN channel_name VARCHAR(200) DEFAULT NULL
    COMMENT '渠道名称(缓存)' AFTER channel_id;

ALTER TABLE biz_pilot_application
  ADD UNIQUE KEY uk_application_no (application_no, del_flag);
  • Step 2: 在 models.py 末尾(BizChannelServiceOrderTxn 之后)新增 4 个 Model

遵循项目已有 Model 模式(参考 BizChannelServiceOrder 在 L1761 的写法):

  • 字段顺序:id → 业务字段 → public 字段(tenant_no / order_num / remark / create_by / create_dept_id / create_time / update_by / update_time / del_flag)
  • 类型映射:VARCHAR→String, INT→Integer, TINYINT→SmallInteger, DATETIME→DateTime, DECIMAL→Numeric
  • 添加 __tablename__ 和中文注释

新增的 4 个 Model:

BizPilotApplication → biz_pilot_application

  • id, application_no, application_status(default='dks'), channel_service_order_id, channel_id, channel_name
    • public fields

BizPilotPatient → biz_pilot_patient

  • id, pilot_application_id, customer_id, patient_name, sex, age, id_card_no, phone_no, emergency_contact, emergency_phone, clinical_diagnosis, diagnosis_attachments, need_checkup(default=1)
    • public fields

BizPilotInfusionPlan → biz_pilot_infusion_plan

  • id, pilot_application_id, cell_type, spec_text, total_course_count, course_interval, infusion_route, infusion_start_date
    • public fields

BizPilotInfusionSchedule → biz_pilot_infusion_schedule

  • id, infusion_plan_id, cell_type, spec_text, infusion_time, schedule_status(default=''), attachments

    • public fields
  • Step 3: 验证

Run: cd backend && source venv/bin/activate && python -c "from app.models.models import BizPilotApplication, BizPilotPatient, BizPilotInfusionPlan, BizPilotInfusionSchedule; print('Models imported OK')" Expected: Models imported OK

  • Step 4: 运行 LSP 诊断

Run lsp_diagnostics on backend/app/models/models.py — expect 0 errors.


Task 2: Backend CRUD

Files:

  • Create: backend/app/crud/pilot_application.py

  • Step 1: 创建 CRUD 文件

创建 backend/app/crud/pilot_application.py,遵循 crud/pilot_service.py 的模式(_row_to_dict_* + 分页列表 + CRUD 函数)。

import 部分:

import uuid
import json
from datetime import datetime, date
from typing import Dict, List, Optional, Any
from sqlalchemy.orm import Session
from sqlalchemy import and_
from app.models.models import (
    BizPilotApplication, BizPilotPatient, BizPilotInfusionPlan, BizPilotInfusionSchedule,
    BizChannelServiceOrder, BizChannelServiceOrderProduct, BizCustomer
)
from app.core.database import get_db

_row_to_dict 函数(4 个):

  1. _row_to_dict_application(row, db=None, tenant_no=None):

    • 基础字段:id, applicationNo, applicationStatus, channelServiceOrderId, channelId, channelName, remark, createTime, updateTime
    • 扩展字段(需 db 查询):
      • patientName: 从 BizPilotPatient 查询(WHERE pilot_application_id=row.id AND del_flag=0)
      • infusionPlanCount: 从 BizPilotInfusionPlan COUNT(WHERE pilot_application_id=row.id AND del_flag=0)
      • serviceOrderNo: 从 BizChannelServiceOrder 查询(WHERE id=row.channel_service_order_id)取其关联的订单信息
      • orderType: 同上,服务订单的 order_type
  2. _row_to_dict_patient(row):

    • id, pilotApplicationId, customerId, patientName, sex, age, idCardNo, phoneNo, emergencyContact, emergencyPhone, clinicalDiagnosis, diagnosisAttachments, needCheckup, remark, createTime, updateTime
  3. _row_to_dict_infusion_plan(row, db=None, tenant_no=None):

    • id, pilotApplicationId, cellType, specText, totalCourseCount, courseInterval, infusionRoute, infusionStartDate, remark, createTime, updateTime
    • 扩展:scheduleCount (COUNT schedules), completedCount (COUNT WHERE schedule_status='ywc')
  4. _row_to_dict_infusion_schedule(row):

    • id, infusionPlanId, cellType, specText, infusionTime, scheduleStatus, attachments, remark, createTime, updateTime

CRUD 函数(16 个):

Application(8 个):

  • list_applications(db, tenant_no, page, page_size, channel_id=None, application_status=None, channel_service_order_id=None) — 分页列表,按 create_time DESC 排序
  • get_application(db, app_id, tenant_no) — 获取详情
  • create_application(db, data, tenant_no, user_id, dept_id) — 校验服务订单存在且 status='sxz',从服务订单缓存 channel_id/channel_name,生成 application_no(ZSSQ+YYYYMMDD+4位序列)
  • update_application(db, app_id, data, tenant_no) — 仅 dks 状态可编辑
  • soft_delete_application(db, app_id, tenant_no) — 仅 dks 状态
  • start_application(db, app_id, tenant_no, user_id) — dks→jxz
  • complete_application(db, app_id, tenant_no, user_id) — jxz→ywc
  • void_application(db, app_id, tenant_no, user_id) — dks或jxz→yzf

Patient(2 个):

  • get_patient_by_application(db, app_id, tenant_no)
  • save_patient(db, app_id, data, tenant_no, user_id, dept_id) — 如果有 customer_id 则更新 biz_customer;如果无则新建 biz_customer 并回填 customer_id。然后用 upsert 逻辑保存 biz_pilot_patient(先查再更新或新建)。

InfusionPlan(4 个):

  • list_infusion_plans(db, app_id, tenant_no) — 按 order_num / create_time ASC
  • create_infusion_plan(db, app_id, data, tenant_no, user_id, dept_id)
  • update_infusion_plan(db, plan_id, data, tenant_no)
  • soft_delete_infusion_plan(db, plan_id, tenant_no) — 级联软删除其下所有 schedule

InfusionSchedule(6 个):

  • list_schedules_by_plan(db, plan_id, tenant_no) — 按 order_num ASC
  • auto_generate_schedules(db, plan_id, tenant_no, user_id, dept_id) — 读取 plan.total_course_count,批量插入 N 条 schedule
  • create_schedule(db, plan_id, data, tenant_no, user_id, dept_id) — 手动添加单条
  • update_schedule(db, schedule_id, data, tenant_no)
  • soft_delete_schedule(db, schedule_id, tenant_no)
  • mark_schedule_complete(db, schedule_id, tenant_no, user_id) — schedule_status='ywc'

application_no 生成逻辑:

def _generate_application_no(db, tenant_no):
    today_str = datetime.now().strftime('%Y%m%d')
    prefix = f'ZSSQ{today_str}'
    # 查今天已有的最大序号
    existing = db.query(BizPilotApplication.application_no).filter(
        BizPilotApplication.tenant_no == tenant_no,
        BizPilotApplication.application_no.like(f'{prefix}%'),
        BizPilotApplication.del_flag == 0
    ).order_by(BizPilotApplication.application_no.desc()).first()
    seq = 1
    if existing and existing[0]:
        seq = int(existing[0][-4:]) + 1
    return f'{prefix}{seq:04d}'
  • Step 2: 验证导入

Run: cd backend && source venv/bin/activate && python -c "from app.crud.pilot_application import list_applications; print('CRUD imported OK')" Expected: CRUD imported OK

  • Step 3: 运行 LSP 诊断

Run lsp_diagnostics on backend/app/crud/pilot_application.py — expect 0 errors.


Task 3: Backend API Routes + Router Registration

Files:

  • Create: backend/app/api/pilot_application.py

  • Modify: backend/app/api/__init__.py

  • Modify: backend/app/main.py

  • Step 1: 创建 API 路由文件

创建 backend/app/api/pilot_application.py,遵循 api/pilot_service.py 的模式(Pydantic schemas 内联 + 路由函数)。

import 部分:

from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
from typing import Optional, List
from app.core.database import get_db
from app.core.menu_guard import require_admin_or_super_admin
from app.crud import pilot_application

Pydantic Schemas(6 个):

class ApplicationCreateBody(BaseModel):
    channel_service_order_id: str
    remark: Optional[str] = None

class ApplicationUpdateBody(BaseModel):
    remark: Optional[str] = None

class PatientSaveBody(BaseModel):
    customer_id: Optional[str] = None
    patient_name: str
    sex: Optional[str] = None
    age: Optional[int] = None
    id_card_no: Optional[str] = None
    phone_no: Optional[str] = None
    emergency_contact: Optional[str] = None
    emergency_phone: Optional[str] = None
    clinical_diagnosis: Optional[str] = None
    diagnosis_attachments: Optional[str] = None
    need_checkup: int = 1

class InfusionPlanCreateBody(BaseModel):
    pilot_application_id: str
    cell_type: str
    spec_text: Optional[str] = None
    total_course_count: int
    course_interval: Optional[str] = None
    infusion_route: Optional[str] = None
    infusion_start_date: Optional[str] = None

class InfusionPlanUpdateBody(BaseModel):
    cell_type: Optional[str] = None
    spec_text: Optional[str] = None
    total_course_count: Optional[int] = None
    course_interval: Optional[str] = None
    infusion_route: Optional[str] = None
    infusion_start_date: Optional[str] = None

class InfusionScheduleUpdateBody(BaseModel):
    infusion_time: Optional[str] = None
    remark: Optional[str] = None
    attachments: Optional[str] = None

Router:

router = APIRouter(prefix="/web/admin", tags=["中试服务申请单管理"])

API 路由(19 个):

方法 路径 函数名 依赖 说明
GET /pilot-application/list api_list_applications require_admin 分页列表,query params: page/page_size/channel_id/application_status/channel_service_order_id
GET /pilot-application/detail api_get_application require_admin query param: id
POST /pilot-application/create api_create_application require_admin body: ApplicationCreateBody
PUT /pilot-application/update api_update_application require_admin body: ApplicationUpdateBody + id
DELETE /pilot-application/delete api_delete_application require_admin body: {id}
POST /pilot-application/start api_start_application require_admin body: {id}
POST /pilot-application/complete api_complete_application require_admin body: {id}
POST /pilot-application/void api_void_application require_admin body: {id}
GET /pilot-patient/detail api_get_patient require_admin query param: application_id
POST /pilot-patient/save api_save_patient require_admin body: PatientSaveBody + application_id
GET /pilot-infusion-plan/list api_list_plans require_admin query param: application_id
POST /pilot-infusion-plan/create api_create_plan require_admin body: InfusionPlanCreateBody
PUT /pilot-infusion-plan/update api_update_plan require_admin body: InfusionPlanUpdateBody + id
DELETE /pilot-infusion-plan/delete api_delete_plan require_admin body: {id}
POST /pilot-infusion-schedule/auto-generate api_auto_generate require_admin body: {plan_id}
POST /pilot-infusion-schedule/create api_create_schedule require_admin body: {plan_id, infusion_time?, remark?}
PUT /pilot-infusion-schedule/update api_update_schedule require_admin body: InfusionScheduleUpdateBody + id
DELETE /pilot-infusion-schedule/delete api_delete_schedule require_admin body: {id}
POST /pilot-infusion-schedule/mark-complete api_mark_complete require_admin body: {id}

每个路由函数结构:

@router.get("/pilot-application/list")
def api_list_applications(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    channel_id: Optional[str] = Query(None),
    application_status: Optional[str] = Query(None),
    channel_service_order_id: Optional[str] = Query(None),
    _user: dict = Depends(require_admin_or_super_admin),
    db: Session = Depends(get_db),
):
    tenant_no = _user["tenant_no"]
    result = pilot_application.list_applications(db, tenant_no, page, page_size, channel_id, application_status, channel_service_order_id)
    return {"code": 0, "data": result}
  • Step 2: 注册路由

在 backend/app/api/__init__.py 中追加:

from app.api.pilot_application import router as pilot_application_router

并在 routers 列表中追加 pilot_application_router。

在 backend/app/main.py 中追加路由 include(参考已有的 pilot_service_router 注册方式)。

  • Step 3: 验证

Run: cd backend && source venv/bin/activate && python -c "from app.api.pilot_application import router; print(f'Routes: {len(router.routes)}')" Expected: Routes: 19

  • Step 4: 运行 LSP 诊断

Run lsp_diagnostics on backend/app/api/pilot_application.py, backend/app/api/__init__.py, backend/app/main.py — expect 0 errors on all.


Task 4: Frontend API 函数 + 状态标签 + 路由

Files:

  • Modify: web-admin/src/api/admin.ts

  • Modify: web-admin/src/utils/status-tag-registry.ts

  • Modify: web-admin/src/router/modules/sqxp.ts

  • Step 1: 在 admin.ts 末尾追加 19 个 API 函数

遵循文件中已有函数模式(参考 fetchPilotApplicationList 风格),追加以下函数:

// ==================== 中试服务申请单 ====================

export function fetchPilotApplicationList(params: any) {
  return request({ url: '/pilot-application/list', method: 'get', params })
}
export function fetchPilotApplicationDetail(params: any) {
  return request({ url: '/pilot-application/detail', method: 'get', params })
}
export function createPilotApplication(data: any) {
  return request({ url: '/pilot-application/create', method: 'post', data })
}
export function updatePilotApplication(data: any) {
  return request({ url: '/pilot-application/update', method: 'put', data })
}
export function deletePilotApplication(data: any) {
  return request({ url: '/pilot-application/delete', method: 'delete', data })
}
export function startPilotApplication(data: any) {
  return request({ url: '/pilot-application/start', method: 'post', data })
}
export function completePilotApplication(data: any) {
  return request({ url: '/pilot-application/complete', method: 'post', data })
}
export function voidPilotApplication(data: any) {
  return request({ url: '/pilot-application/void', method: 'post', data })
}

// 患者信息
export function fetchPilotPatient(params: any) {
  return request({ url: '/pilot-patient/detail', method: 'get', params })
}
export function savePilotPatient(data: any) {
  return request({ url: '/pilot-patient/save', method: 'post', data })
}

// 输注方案
export function fetchPilotInfusionPlanList(params: any) {
  return request({ url: '/pilot-infusion-plan/list', method: 'get', params })
}
export function createPilotInfusionPlan(data: any) {
  return request({ url: '/pilot-infusion-plan/create', method: 'post', data })
}
export function updatePilotInfusionPlan(data: any) {
  return request({ url: '/pilot-infusion-plan/update', method: 'put', data })
}
export function deletePilotInfusionPlan(data: any) {
  return request({ url: '/pilot-infusion-plan/delete', method: 'delete', data })
}

// 输注计划
export function fetchPilotInfusionScheduleList(params: any) {
  return request({ url: '/pilot-infusion-schedule/list', method: 'get', params })
}
export function autoGenerateSchedules(data: any) {
  return request({ url: '/pilot-infusion-schedule/auto-generate', method: 'post', data })
}
export function updatePilotInfusionSchedule(data: any) {
  return request({ url: '/pilot-infusion-schedule/update', method: 'put', data })
}
export function deletePilotInfusionSchedule(data: any) {
  return request({ url: '/pilot-infusion-schedule/delete', method: 'delete', data })
}
export function markScheduleComplete(data: any) {
  return request({ url: '/pilot-infusion-schedule/mark-complete', method: 'post', data })
}
  • Step 2: 在 status-tag-registry.ts 追加申请单状态标签函数
/** 中试申请单状态 */
export function applicationStatusLabel(status: string): string {
  const map: Record<string, string> = { dks: '待开始', jxz: '进行中', ywc: '已完成', yzf: '已作废' }
  return map[status] || status || '-'
}
export function applicationStatusTagType(status: string): '' | 'success' | 'warning' | 'danger' | 'info' {
  const map: Record<string, '' | 'success' | 'warning' | 'danger' | 'info'> = {
    dks: 'info', jxz: '', ywc: 'success', yzf: 'danger'
  }
  return map[status] || 'info'
}
  • Step 3: 在 sqxp.ts 追加 2 个路由

在 PilotService 的 children 数组末尾追加:

{
  path: 'pilot-application',
  name: 'PilotApplication',
  component: () => import('@views/pilot-service/pilot-application/index.vue'),
  meta: { title: '中试服务申请单', icon: 'i-ep:document' }
},
{
  path: 'pilot-application/detail',
  name: 'PilotApplicationDetail',
  component: () => import('@views/pilot-service/pilot-application/ApplicationDetail.vue'),
  meta: { title: '申请单详情', hidden: true }
},

注意:路由 ID 需要根据现有 PilotService 的 ID 段确定,查看 sqxp.ts 中已有的路由 ID 模式。中试服务模块 ID 段为 8000...,检查当前已使用的最大 ID 后递增。

  • Step 4: 运行 LSP 诊断

Run lsp_diagnostics on all 3 modified files — expect 0 errors.


Task 5: 前端列表页

Files:

  • Create: web-admin/src/views/pilot-service/pilot-application/index.vue

  • Step 1: 创建列表页

创建目录 web-admin/src/views/pilot-service/pilot-application/ 和 index.vue。

参考模板页: src/views/system/order/index.vue(复杂列表页)

页面结构:

  • 筛选区:申请单号(el-input)、渠道(el-select,数据源 fetchChannelList)、服务订单(el-select,联动渠道)、状态(el-radio-group 或 tag 按钮)
  • 操作栏:新增按钮(el-button type="primary")
  • 表格(el-table):列如 spec 中定义
  • 分页(el-pagination)

关键交互:

  • 新增:弹窗(el-dialog),选择渠道 → 联动加载该渠道的服务订单(调用已有的 fetchServiceOrderList,传入 channel_id 筛选)→ 选择服务订单 → 填写备注 → 提交 → 成功后 router.push 到详情页
  • 状态操作:根据 applicationStatus 动态显示按钮
    • dks:显示"开始"(primary) + "作废"(danger)
    • jxz:显示"完成"(primary) + "作废"(danger)
    • ywc/yzf:无按钮
  • 编辑/删除:仅 dks 状态可操作
  • 行点击:router.push({ name: 'PilotApplicationDetail', query: { id: row.id } })
  • 状态列用 el-tag + applicationStatusLabel/applicationStatusTagType

数据加载: onMounted 调用 fetchPilotApplicationList,筛选变更时重新加载。

样式: 遵循项目 Fresh Mint 色系(--primary #10b981),使用已有的 SCSS 变量和 mixin。

  • Step 2: 运行 LSP 诊断

Run lsp_diagnostics on web-admin/src/views/pilot-service/pilot-application/index.vue — expect 0 errors.


Task 6: 前端详情页 — 基本信息 Tab + 输注方案 Tab

Files:

  • Create: web-admin/src/views/pilot-service/pilot-application/ApplicationDetail.vue

  • Step 1: 创建详情页

参考模板页: src/views/system/product/index.vue(含详情展示的复杂页面)

页面结构:

  • 顶部:返回按钮 + 申请单号 + 状态标签 + 操作按钮(开始/完成/作废,根据状态显示)
  • el-tabs:4 个 tab(基本信息 / 输注方案 / 体检信息-locked / 交付信息-locked)

基本信息 Tab:

  • 上半区:el-descriptions 展示申请单信息(只读)
    • 申请单号、渠道名称、关联服务订单、状态、创建时间、备注
  • 下半区:患者信息面板(el-card)
    • "选择客户"按钮 → el-dialog 弹窗展示客户列表(调用已有 API 获取 biz_customer 列表,支持姓名/手机搜索)
    • 选中后自动填充表单字段
    • 表单字段:patientName, sex, age, idCardNo, phoneNo, emergencyContact, emergencyPhone, clinicalDiagnosis, diagnosisAttachments(附件上传), needCheckup(el-switch)
    • "保存患者信息"按钮

输注方案 Tab:

  • 工具栏:"新增输注方案"按钮
  • 方案列表用 el-table,每行显示方案的完整信息
    • 最后一列加 expand 展开行,展开后显示该方案的输注计划列表
  • 展开行内的计划列表(el-table):
    • 列:序号、细胞类型、规格、输注时间(可编辑)、状态、操作
    • 操作:编辑(弹窗修改时间/备注)、删除、标记完成
    • "手动添加计划"按钮在展开行内

新增/编辑输注方案弹窗(el-dialog):

  • 表单字段:cell_type(el-select,数据源从细胞产品类型 API)、spec_text、total_course_count(el-input-number)、course_interval、infusion_route、infusion_start_date(el-date-picker)
  • label-position="top",遵循 .form-grid-2 两列布局
  • 保存成功后 ElMessageBox.confirm("是否立即生成 N 条输注计划?"),确认则调用 autoGenerateSchedules

数据加载: onMounted 根据 route.query.id 调用 fetchPilotApplicationDetail 获取详情(含患者+方案),同时 fetchPilotInfusionPlanList 获取方案列表,每个方案再 fetchPilotInfusionScheduleList 获取计划。

样式约定:

  • Dialog forms: label-position="top", .dialog-form-layout, .form-grid-2

  • 无 backdrop blur: backdrop-filter: none

  • Panel heading: .title-bar-heading + ::before 竖线

  • 状态 Tag: el-tag + semantic colors

  • Step 2: 运行 LSP 诊断

Run lsp_diagnostics on web-admin/src/views/pilot-service/pilot-application/ApplicationDetail.vue — expect 0 errors.


Task 7: 集成验证

Files: 无新文件

  • Step 1: 后端启动验证

Run: cd backend && source venv/bin/activate && python -c " from app.main import app routes = [r.path for r in app.routes if hasattr(r, 'path')] pilot_routes = [r for r in routes if 'pilot-application' in r or 'pilot-patient' in r or 'pilot-infusion' in r] print(f'Pilot application routes: {len(pilot_routes)}') for r in sorted(pilot_routes): print(f' {r}') " Expected: 19 routes listed

  • Step 2: 前端构建验证

Run: cd web-admin && pnpm build Expected: Build succeeds with exit code 0

  • Step 3: 前端 LSP 全量诊断

Run lsp_diagnostics on directory web-admin/src/views/pilot-service/pilot-application/ — expect 0 errors.

  • Step 4: 更新知识图谱

Run: ./scripts/update-graph.sh backend frontend Expected: 知识图谱更新完成


自审检查

Spec Coverage

Spec 需求 对应 Task
ALTER biz_pilot_application Task 1
4 个 Model 类 Task 1
Application CRUD 8 个函数 Task 2
Patient save/get 2 个函数 Task 2
InfusionPlan CRUD 4 个函数 Task 2
InfusionSchedule 6 个函数 Task 2
API routes 19 个 Task 3
Router registration Task 3
19 个前端 API 函数 Task 4
状态标签函数 Task 4
路由追加 Task 4
列表页 Task 5
详情页(基本信息+输注方案) Task 6
集成验证 Task 7

Placeholder 扫描

无 TBD/TODO/占位符。所有步骤含具体代码或明确指令。

Type 一致性

  • Model 字段名与 CRUD _row_to_dict 输出的 camelCase key 一致
  • API 路径与前端 request URL 一致
  • Pydantic schema 字段与 CRUD 函数参数一致