feat(education): add native question publication lifecycle
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# EDU-010 — Tenant content publication and graph integrity
|
||||
|
||||
- **Status:** in progress — catalog graph-integrity slice delivered; authoring/publication lifecycle remains
|
||||
- **Status:** in progress — catalog graph-integrity and the verified JAVA_READ tenant-question lifecycle slice are delivered; broader classification/collection authoring remains
|
||||
- **Type:** implementation program
|
||||
- **Phase:** 3
|
||||
- **Blockers:** EDU-004 ✓ (done), EDU-009 ✓ (done), provider-authority decision ✓ (resolved 2026-07-30, see decisions.md), PUBLIC graph-semantics decision ✓ (resolved 2026-07-30, see decisions.md)
|
||||
@@ -20,11 +20,12 @@ Authorized tenant administrators can author, classify, publish, archive, and ret
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Admin permission and data-scope matrix is explicit.
|
||||
- [x] Admin permission and data-scope matrix is explicit.
|
||||
- [x] Cross-tenant graph relationships cannot be persisted in the current V4020 catalog graph.
|
||||
- [x] PUBLIC and tenant-owned reference rules are enforced across the current 19 reference edges, with exact trigger-mapping and focused behavior tests.
|
||||
- [ ] Unpublished/archived content is never student-visible.
|
||||
- [ ] Publication is transactional and auditable.
|
||||
- [x] V4080 extends the graph guard to the new Question Version → Question edge and makes the version table the twelfth ownership-protected catalog table.
|
||||
- [x] Unpublished/archived content is never student-visible.
|
||||
- [x] Publication is transactional and auditable.
|
||||
- [x] Database graph-integrity changes use `flyway-postgresql` and forward migration V4070.
|
||||
|
||||
## Delivery progress — graph-integrity slice (2026-07-30)
|
||||
@@ -41,16 +42,45 @@ Delivered:
|
||||
|
||||
No non-Education module changed. Application rollback can disable native authoring when it is introduced; database recovery remains a higher forward migration and must preserve existing content.
|
||||
|
||||
## Remaining publication slice
|
||||
## Publication-slice constraints (resolved)
|
||||
|
||||
Do not add a local-only admin write path while `SCALAR_READ` remains the default authoritative provider: a successful PostgreSQL write would not be visible to students. The next bounded slice is a `JAVA_READ`-only tenant question `draft → publish → archive → student read` path that fails closed in unsupported provider modes.
|
||||
Do not add a local-only admin write path while `SCALAR_READ` remains the default authoritative provider: a successful PostgreSQL write would not be visible to students. The implemented bounded slice is a `JAVA_READ`-only tenant question `draft → publish → archive → student read` path that fails closed in unsupported provider modes.
|
||||
|
||||
Before its Flyway/application implementation, resolve and document:
|
||||
The slice resolves the prerequisite decisions as follows:
|
||||
|
||||
1. the exact `DRAFT/PUBLISHED/ARCHIVED/RETIRED` state machine and mapping of V4020 `HIDDEN/INACTIVE` rows;
|
||||
2. immutable question-version and transactional publication-audit ownership;
|
||||
3. whether ancestor entry/node/collection/archive state makes a question unavailable to fresh student reads;
|
||||
4. the explicit tenant-admin versus platform-curator permission/data-scope matrix.
|
||||
1. the command surface is `DRAFT → PUBLISHED → ARCHIVED`; `RETIRED` remains undefined, and V4020 `HIDDEN/INACTIVE` rows map to `ARCHIVED`;
|
||||
2. Education owns immutable question versions and transactional lifecycle audit;
|
||||
3. a question's state controls direct visibility while entry/node/collection availability gates only route-specific discovery;
|
||||
4. tenant admin action permissions are separate, tenant-wide, and tenant-bound; platform-curator/PUBLIC writes fail closed in this slice.
|
||||
|
||||
## Delivery progress — JAVA_READ tenant-question lifecycle slice (2026-07-30)
|
||||
|
||||
Delivered:
|
||||
|
||||
- Explicit Admin APIs and independent RBAC permissions for tenant question author, publish, and archive operations. Requests cannot choose tenant, scope, lifecycle, or actor identity:
|
||||
- `POST /admin-api/education/questions/drafts` — `education:question:author`;
|
||||
- `PUT /admin-api/education/questions/{id}/publish` — `education:question:publish`;
|
||||
- `PUT /admin-api/education/questions/{id}/archive` — `education:question:archive`.
|
||||
- Provider-authority guard: authoring is accepted only for `JAVA_READ`; `SCALAR_READ` fails before any Mapper access.
|
||||
- V4080 changes native defaults to `DRAFT/false`, normalizes legacy visibility states, fails closed on unsafe historical published content, constrains the single direction `DRAFT → PUBLISHED → ARCHIVED`, creates immutable Question Content Versions, and creates append-only lifecycle audit facts.
|
||||
- V4080 adds the Question Version → Question reference as the twentieth guarded catalog edge. In addition to the generic scope rule, a version's tenant and scope must exactly equal its question's ownership; the version table is the twelfth catalog table protected against ownership mutation.
|
||||
- When the adopted platform schema contains `system_menu`, V4080 conditionally seeds the Education capability plus author/publish/archive permissions. A fixed ID already occupied by a different permission aborts migration, and no role assignment is seeded.
|
||||
- Publication and archive use tenant/scope/expected-state CAS updates and append audit facts in the same transaction. Audit failure rolls back the state change.
|
||||
- PostgreSQL integration-test coverage exercises the student read-after-write seam: draft is invisible, published content is returned only through the existing Safe Question projection, and archived content is invisible while stored snapshots remain independent.
|
||||
- Direct question visibility is controlled by the question lifecycle. Container availability is a route-level discovery gate; classification, collection, and platform-curator write paths remain outside this bounded slice.
|
||||
|
||||
Permission and data-scope matrix is recorded in `07-decisions.md`. The three lifecycle endpoints manage tenant-wide shared catalog assets within the current framework tenant; department/self DataPermission does not grant additional row access, PUBLIC authoring fails closed, and the V4080 seed intentionally grants no role.
|
||||
|
||||
The listed acceptance criteria are satisfied for the native catalog graph and tenant-question lifecycle slice. EDU-010 remains in progress because its tenant-admin outcome also includes classification, collection, blueprint, retire, and platform-curator workflows that are not part of this bounded slice.
|
||||
|
||||
## Verification evidence (2026-07-30)
|
||||
|
||||
- `mvn -pl yudao-module-education test` with the five `EDU_TEST_POSTGRES_*` variables pointed at the local Docker PostgreSQL: **485 tests passed**, including 18 Flyway migration tests and five lifecycle PostgreSQL integration tests. V4080 was actually executed in disposable PostgreSQL schemas.
|
||||
- The lifecycle integration tests prove concurrent double-publish has one success and one lifecycle conflict with exactly one publish audit, cross-tenant and PUBLIC management fail closed, and `draft → publish → archive` matches student visibility.
|
||||
- The real Spring Method Security contract tests prove each of author/publish/archive requires its own permission and rejected calls do not reach the lifecycle service.
|
||||
- `mvn -pl yudao-server -am -DskipTests clean compile`: **22 reactor modules passed**.
|
||||
- `git diff --check`: passed.
|
||||
- `target/classes/db/migration/education/V4080__add_question_publication_lifecycle.sql`: present after the module build.
|
||||
|
||||
## Risk and rollback
|
||||
|
||||
|
||||
Reference in New Issue
Block a user