文件
hyb-oa/.trellis/spec/hyb-oa/backend-fastify-prisma.md
T

7.2 KiB

Backend Fastify / Prisma

Scope

Server work lives under server/. Prefer existing route, service, utility and QA script patterns over new abstractions.

Rules

  • Validate Prisma changes with cd server && npm run prisma:validate.
  • For route/service edits, run node --check on the touched files and the focused regression listed in the current Trellis task.
  • Prisma migrations require rollback notes in the Trellis task design or PRD.
  • Do not resolve migration drift, reset databases, or rewrite migration history without explicit approval.
  • Workflow, approval and permission behavior must be verified at process level, not only syntax level.

Common Checks

  • cd server && node --check src/routes/reimbursements.js
  • cd server && node --check src/routes/workflow.js
  • cd server && ./scripts/regression-workflow-actions.sh
  • cd server && ./scripts/regression-all.sh

Scenario: Local Regression Migration Drift

1. Scope / Trigger

  • Trigger: a local or attached E2E fails with Prisma missing column, column does not exist, enum missing, or route 500 immediately after a schema-backed feature was added.
  • Applies to server/prisma/schema.prisma, server/prisma/migrations/, local regression databases such as hyb_oa_regression, and Playwright tests that use real API/database flows.

2. Signatures

  • Read-only drift check: cd server && npx prisma migrate status.
  • Apply committed migrations after explicit confirmation: cd server && npx prisma migrate deploy.
  • Local service smoke for attached E2E: HOST=127.0.0.1 PORT=3000 JOBS_ENABLED=false SERVE_CLIENT_DIST=true npm run dev.
  • Server entrypoints that call getConfig() before buildApp() must load dotenv/config before reading config.

3. Contracts

  • migrate status is safe and read-only; it may be run before asking the user.
  • migrate deploy writes database schema state and requires explicit user confirmation unless the current task already approved that exact local DB.
  • Do not use prisma migrate reset for E2E recovery without separate backup and destructive-action approval.
  • Attached local E2E should disable background jobs with JOBS_ENABLED=false unless the test explicitly validates job behavior.

4. Validation & Error Matrix

  • migrate status reports pending migrations -> record the migration names, inspect whether they are additive/destructive, then ask before applying.
  • Missing JWT_SECRET during npm run dev while .env exists -> verify the entrypoint loads dotenv/config before getConfig().
  • Missing column after migrate deploy -> inspect actual columns and migration history; do not patch tests to hide schema drift.
  • E2E selector failure after schema is fixed -> treat as page/test contract drift and update the focused E2E assertion, not the database.

5. Good/Base/Bad Cases

  • Good: run migrate status, apply only committed migrations after approval, seed dev users, then rerun the focused Playwright flow.
  • Base: if migrations are already current, skip DB writes and debug the test failure directly.
  • Bad: use migrate reset or manually add columns to unblock a test.
  • Bad: start the server with an ad hoc node -r dotenv/config workaround and leave npm run dev broken.

6. Tests Required

  • cd server && npm run prisma:validate.
  • bash scripts/qa/check-local-migration-drift.sh when local DB drift was the cause.
  • cd server && node --check src/index.js if server bootstrap changed.
  • Focused Playwright flow that previously failed.
  • Frontend npm run build when Playwright or client behavior changed.

7. Wrong vs Correct

Wrong

cd server && npx prisma migrate reset

Correct

cd server && npx prisma migrate status
# after explicit approval for the local regression database only
cd server && npx prisma migrate deploy

Scenario: Portal Announcement Acknowledgement

1. Scope / Trigger

  • Trigger: extending announcements_v1 from read-only announcements into formal employee acknowledgement.
  • Applies to server/prisma/schema.prisma, server/src/routes/announcements.js, client/src/api/announcements.ts, employee /announcements, and admin /system/announcements.
  • Use this only for lightweight policy acknowledgement; do not treat it as electronic signature, CA signing, contract signing, or full policy CMS.

2. Signatures

  • DB: PortalAnnouncement may carry category, requires_acknowledgement, acknowledgement_statement, acknowledgement_due_at.
  • DB: PortalAnnouncementAcknowledgement stores one receipt per (announcement_id, employee_id) with statement/title/summary snapshots and actor_id.
  • API: GET /api/announcements?manage=0 returns visible announcements and the current employee's acknowledgement status.
  • API: GET /api/announcements?manage=1 requires system.settings and returns acknowledgement summary plus minimal pending employees.
  • API: POST /api/announcements/:id/acknowledge accepts { "confirmed": true }.

3. Contracts

  • Required acknowledgement status values: not_required, pending, acknowledged, overdue.
  • Employee response must not include other employees' acknowledgement rows or pending lists.
  • Admin pending list may include only id, employeeNo, name, and status; do not include phone, email, WeCom userid, account identifiers, or sensitive profile fields.
  • Acknowledgement is explicit only: opening an announcement or clicking its link must never create a receipt.
  • Policy content changes after any acknowledgement require a new announcement version; do not mutate title, summary, category, required flag, or statement in place.

4. Validation & Error Matrix

  • announcements_v1 disabled -> 409 VALID_001.
  • policy acknowledgement without statement -> 400 VALID_001.
  • manage=1 without system.settings -> 403 PERM_001.
  • acknowledge invisible/missing announcement -> 404 ANN_404.
  • acknowledge normal announcement -> 409 VALID_001.
  • acknowledge without { confirmed: true } -> 400 VALID_001.
  • edit protected policy fields after acknowledgement exists -> 409 VALID_001.

5. Good/Base/Bad Cases

  • Good: admin creates a policy announcement, employee checks the confirmation box, server creates one receipt and one audit log.
  • Base: normal announcement remains compatible and returns not_required.
  • Bad: frontend displays another employee's receipt status on /announcements.
  • Bad: admin updates an acknowledged policy title instead of creating a new version.

6. Tests Required

  • Prisma validate after schema/migration changes.
  • Route focused tests for required statement, permission, idempotency, normal/invisible announcement rejection, admin summary, and protected edit rejection.
  • Frontend typecheck and build.
  • Playwright flow for admin create -> employee acknowledge -> admin summary update when UI changes.

7. Wrong vs Correct

Wrong

await prisma.portalAnnouncement.update({
  where: { id },
  data: { title: "制度 V2" },
});

Correct

const acknowledgementCount = await prisma.portalAnnouncementAcknowledgement.count({
  where: { announcement_id: id },
});
if (acknowledgementCount > 0) {
  throw new AppError({ code: "VALID_001", message: "参数校验失败", statusCode: 409 });
}