forked from wangziqi/ruoyi-vue-pro
175 lines
7.7 KiB
Markdown
175 lines
7.7 KiB
Markdown
# Provider-neutral question content safety contract
|
|
|
|
> Status: proposed for EDU-P0-S1
|
|
>
|
|
> Evidence basis: current Education providers and services plus the legacy question import and learning rules. This document defines the implementation contract; it does not report tests as executed.
|
|
|
|
## Decision
|
|
|
|
Student-visible question content is validated by one provider-neutral contract before it is projected as a safe question or persisted/restored as a practice snapshot. Provider-specific parsing may reject malformed transport data earlier, but switching between Scalar and Java must not change whether the same logical question is considered safe.
|
|
|
|
Invalid or unsupported content fails closed. It must not be normalized into an apparently valid question with an empty option list.
|
|
|
|
## Question-type families
|
|
|
|
Type matching is case-insensitive after trimming. Persisted and returned canonical values remain an implementation concern; validation uses the following families.
|
|
|
|
### Option-backed
|
|
|
|
```text
|
|
choice
|
|
multi
|
|
multi_choice
|
|
judge
|
|
image
|
|
```
|
|
|
|
An option-backed question requires:
|
|
|
|
- at least two options;
|
|
- every option object to be non-null;
|
|
- a non-blank string `label`;
|
|
- a non-blank string `content`;
|
|
- labels unique after trimming;
|
|
- each `order`, when present, to be a finite number;
|
|
- no duplicate non-null order value;
|
|
- no answer-bearing field in the student-safe projection or snapshot.
|
|
|
|
Options are emitted in deterministic order: numeric `order` first, preserving source order only when order is absent. This slice does not infer or repair missing labels, contents, or order values.
|
|
|
|
### Optionless
|
|
|
|
```text
|
|
fill
|
|
text
|
|
terms
|
|
short_answer
|
|
composition
|
|
discuss
|
|
translation
|
|
case_analysis
|
|
brief_analysis
|
|
calculation
|
|
analysis_design
|
|
combination
|
|
solution
|
|
```
|
|
|
|
An optionless question may have null or empty options. If options are supplied, the content is inconsistent with its type and fails closed rather than silently discarding them.
|
|
|
|
This slice only establishes safe display and snapshot behavior. It does not add text-answer submission or scoring semantics. Existing answer-saving behavior must not pretend an optionless question is option-backed.
|
|
|
|
### Composite
|
|
|
|
```text
|
|
reading
|
|
```
|
|
|
|
A composite question requires sub-questions in the legacy model. The current target `CatalogQuestionDTO`, safe response, and practice snapshot do not carry a supported sub-question contract. Therefore a top-level `reading` question is unsupported by EDU-P0-S1 and fails closed rather than appearing as an optionless standalone question.
|
|
|
|
Supporting composite questions requires a later explicit model and API contract.
|
|
|
|
### Unknown or missing type
|
|
|
|
A null, blank, or unknown type fails closed. The target must not default unknown content to `choice`, because doing so can turn malformed content into a different assessment.
|
|
|
|
## Common option shape
|
|
|
|
The provider-neutral safe option shape is:
|
|
|
|
```text
|
|
label: non-blank string, unique after trim
|
|
content: non-blank string
|
|
order: optional finite number, unique when present
|
|
```
|
|
|
|
Provider DTOs may temporarily contain `isCorrect` for internal scoring or migration needs, but that field and all answer-bearing fields are discarded before creation of a Safe Question or the student-visible Question Snapshot JSON. A separately stored Protected Answer Key may retain correctness and explanation data for stable server-side scoring, but it is never part of the safe snapshot projection or a pre-submit response.
|
|
|
|
The safety validator must not require `isCorrect`, because safe restored snapshots intentionally do not store it. Correct-answer completeness is a content-authoring/scoring concern and is outside this read-only display contract.
|
|
|
|
## Failure semantics
|
|
|
|
### Fresh provider content
|
|
|
|
Any of the following makes the provider result unsafe:
|
|
|
|
- malformed options transport or JSON;
|
|
- null option element;
|
|
- wrong field types;
|
|
- fewer than two options for an option-backed question;
|
|
- options on an optionless question;
|
|
- blank or duplicate labels;
|
|
- blank content;
|
|
- non-finite or duplicate explicit order;
|
|
- unsupported composite content;
|
|
- null, blank, or unknown type.
|
|
|
|
A single-question request returns the existing controlled unsafe/malformed content error. Page and collection requests fail the response closed rather than silently changing totals or returning a partial assessment set.
|
|
|
|
### Persisted practice snapshot
|
|
|
|
A null/empty options value is valid only for an optionless type. Malformed JSON or a shape that violates the type family makes the snapshot unavailable. Session restoration must return a controlled error for the session/question; it must not convert parse failure into `[]`.
|
|
|
|
No automatic repair is performed during read or restore. Historical repair, quarantine, or backfill requires a separately reviewed migration or administrative process.
|
|
|
|
## Boundary placement
|
|
|
|
The contract is applied at two domain boundaries:
|
|
|
|
1. provider `CatalogQuestionDTO` → student-safe projection or practice-session creation;
|
|
2. persisted `PracticeQuestionDO` snapshot → practice-session response.
|
|
|
|
Both boundaries use the same type classification and option-shape rules. Scalar and Java adapters remain responsible only for transport/database parsing and mapping; they must not define divergent business validity.
|
|
|
|
## Compatibility notes
|
|
|
|
- Legacy source evidence identifies `choice`, `multi`, `judge`, and `image` as objective/option-backed and requires at least two options.
|
|
- Legacy source recognizes `reading` as a composite type with sub-questions.
|
|
- Legacy source recognizes the optionless types listed above and requires answer text for authoring; answer text is intentionally not exposed by the safe read contract.
|
|
- Current target tests sometimes construct `choice` questions with null, one, or empty options. Those fixtures describe previous permissive behavior and must be corrected where they cross a student-visible or snapshot boundary.
|
|
- `multi_choice` is retained as a target compatibility alias because current submit tests use it, while the legacy canonical type is `multi`.
|
|
|
|
## Required tests
|
|
|
|
### Shared contract
|
|
|
|
- each recognized type is classified correctly;
|
|
- type matching trims and ignores case;
|
|
- null, blank, and unknown types fail;
|
|
- option-backed types reject null, empty, and one-option lists;
|
|
- optionless types accept null/empty and reject supplied options;
|
|
- `reading` fails as unsupported composite content;
|
|
- null elements, wrong field types, blank labels, duplicate labels, blank contents, non-finite orders, and duplicate explicit orders fail;
|
|
- valid options preserve safe fields and never expose `isCorrect`.
|
|
|
|
### Provider paths
|
|
|
|
Run the same logical contract cases against Scalar and Java mappings. Transport-specific malformed data may fail earlier, but no provider may turn malformed input into an empty valid list.
|
|
|
|
### Service paths
|
|
|
|
- single-question browsing fails closed for unsafe content;
|
|
- page browsing fails the whole response for unsafe content;
|
|
- collection browsing fails the whole response for unsafe content;
|
|
- unpublished, hidden, inactive, disabled-source, and unavailable-source behavior remains fail-closed;
|
|
- successful output contains no answer-bearing fields.
|
|
|
|
### Practice paths
|
|
|
|
- session creation rejects unsafe source questions before persisting snapshots;
|
|
- a valid option-backed snapshot restores its options;
|
|
- a valid optionless snapshot restores an empty option list;
|
|
- malformed or type-inconsistent snapshots fail closed;
|
|
- snapshot JSON contains only `label`, `content`, and `order`;
|
|
- cross-tenant and ownership protections remain unchanged.
|
|
|
|
## Out of scope
|
|
|
|
- choosing Scalar or Java as the authoritative provider;
|
|
- adding composite/sub-question APIs;
|
|
- adding subjective answer submission or scoring;
|
|
- validating that correct answers exist or are unique;
|
|
- repairing historical snapshots;
|
|
- changing database schema or Flyway migrations;
|
|
- submit-idempotency redesign.
|