Files
ruoyi-vue-pro/docs/education/migration/12-admin-authoring-ui.md

35 KiB
Raw Blame History

Education Admin UI

Implemented on 2026-07-31 as the first two Vben management UI slices. This record covers build-time and disposable PostgreSQL evidence only; it is not Pilot or production runtime evidence.

Delivered pages

The Vben admin submodule now contains the four routes seeded by Education Flyway migration V4220:

Menu component Capability
education/content-node/index Page, create, revise, activate, and archive tenant content nodes
education/question/index Page, create/revise drafts, place on a content node, publish, and archive questions
education/collection/index Page, create/revise drafts, replace ordered question membership, activate, and archive collections
education/practice-blueprint/index Page, create/revise NODE or COLLECTION blueprints, activate, and archive blueprints

Flyway migration V4230 adds four further routes over existing backend contracts:

Menu component Capability and reused RuoYi module
education/import-job/index Infra File-backed upload and malware scan, parser preview, explicit execution, and job lookup
education/classroom/index Class creation, Member user membership display, and expiring idempotent invitations
education/commercialization/index Question-collection binding to Mall SPU and idempotent Member entitlement events suitable for Pay/Mall callbacks
education/operations/index Read-only dependency, Worker, Scanner, and dead-letter health with server-sanitized diagnostics

Flyway migration V4240 adds the remaining bounded admin contracts:

Menu component Capability
education/category/index Tenant category page/detail reads plus create, revise, activate, and archive lifecycle
education/content-export/index Export field-policy evaluation with separate answer-inclusion permission; explicitly does not claim artifact generation

Member migration V4250 and Education migration V4260 add the first member-learning operations slice:

Menu component Capability and reused RuoYi module
education/learning-operations/index Tenant learning summary, student feedback handling/audit, resolved-feedback point rewards, and learning award/badge projection. Member remains authoritative for users, levels, balances, and point records; System remains authoritative for admin identity and notifications.

V4250 adds a fail-closed unique business key for Education-owned entries in the Member point ledger. MemberPointApi.addPointOnce treats only a verified matching ledger row as an idempotent replay, so a retry cannot double-credit a member and unrelated unique-key failures are not hidden. V4260 adds tenant-owned feedback events, optimistic feedback versions, reward delivery state, separate query/feedback/reward RBAC permissions, and menu rows.

Education migration V4270 adds the tenant student-supervision slice:

Menu component Capability and reused RuoYi module
education/supervision/index Risk-student preview over native practice reports, wrong questions, active sessions, and vocabulary progress; configurable supervision rules; idempotent follow-up generation; and optimistic follow-up handling. Member remains authoritative for student accounts. System AdminUser, RBAC, department data scopes, and simple-list selectors remain authoritative for operators.

V4270 adds dept_id and owner_user_id authorization projections to Education classes and registers classes, supervision rules, and follow-ups with RuoYi DeptDataPermissionRule. It does not copy System users, departments, roles, or Member profiles. Stable (tenant_id, batch_key, student_user_id) uniqueness plus PostgreSQL ON CONFLICT DO NOTHING prevents both sequential and concurrent retries from duplicating work without aborting the surrounding transaction. The risk query keeps education_class in the main select so RuoYi's department/self interceptor can scope candidate students; a real LoginUser and DeptDataPermissionRespDTO PostgreSQL test verifies the negative department case.

Education migration V4280 adds configurable tenant badges and the thirteenth Education admin page:

Menu component Capability and reused RuoYi module
education/badge/index Badge definition filtering, creation, optimistic editing, manual Member grant, and grant-history audit. Member remains authoritative for student identity; System remains authoritative for administrator identity, RBAC, and the education_badge_granted notify template/message.

V4280 extends the existing education_learning_award ledger instead of creating a second user-badge table. (tenant_id, user_id, badge_definition_id) makes a badge a lifetime-once grant and PostgreSQL ON CONFLICT DO NOTHING makes manual and automatic replay safe. Automatic evaluation is attached only to real migrated events: practice submission, vocabulary review, and feedback resolution/reward. Check-in, mock-exam, and activity-reward triggers remain unavailable until those source capabilities are migrated; the rule editor does not advertise invented event sources.

Education migration V4290 adds tenant appearance/settings/theme lifecycle and the fourteenth Education admin page:

Menu component Capability and reused RuoYi module
education/tenant-appearance/index Branding, public/admin JSON settings, three platform theme templates, draft preview, and explicit publication. System Tenant remains authoritative for tenant name and websites; System RBAC, tenant validation, AdminUser projection, and operation/access logging are reused.

V4290 adds one tenant-owned optimistic configuration row and one global platform-template table. classic, focus, and high-contrast use the exact legacy theme token/assets payloads. The anonymous public appearance endpoint stays under the normal validated tenant-id context and excludes admin flags, drafts, and operator data. It is deliberately separate from /education/tenant/resolve, whose minimal two-field locator response remains unchanged. Public JSON recursively rejects sensitive key names except secretRef, while renderable theme fields, CSS variables, icons, URLs, modes, densities, colors, and radii are validated by a closed policy at both preview and publication boundaries.

Education migration V4300 adds two Education-menu entry points while reusing existing native UI and backend contracts:

Menu component Capability and reused RuoYi module
pay/app/index Tenant payment applications and channels through Pay's existing controllers, V4320 tenant-scoped App/Channel persistence, channel configuration forms, eight granular app/channel permissions, and V4330's explicit legacy-account import modal.
system/social/client/index.vue Tenant-scoped third-party login clients through System's existing controller, TenantBaseDO, Vben page, and four granular permissions.

V4300 creates no Education page, endpoint, payment account, auth-provider, or credential table. Its unique route names allow the native components to coexist with their original menu locations. System SMS Channel remains a platform-global @TenantIgnore object and does not provide legacy tenant-level PNVS equivalence, so V4300 deliberately does not expose it as a migrated tenant auth provider.

V4330/EDU-021 extends the same native Pay page rather than adding a sixteenth Education page. Operators paste one reviewed manifest containing source IDs/checksum, explicit native channel, new business callbacks, and provider configuration. The modal warns that only tenant_collect WeChat/Alipay is supported and that native Pay retains channel credentials using its existing storage. The action is visible only when both App-create and Channel-create permissions are present, matching the backend AND check. Its template is instructional and contains placeholders that must be replaced. The tenant-filtered audit API stores mappings and digests but no second raw configuration or credential copy; import request-body logging is also disabled across access, non-production, and unexpected-error logs. Account-import audit history does not yet have a dedicated table view.

Education migration V4340 adds three more Education-menu entry points while continuing to reuse native Pay UI and controllers:

Menu component Capability and reused RuoYi module
pay/order/index Tenant-scoped native Pay order/detail queries and export using pay:order:query / pay:order:export.
pay/refund/index Tenant-scoped native Pay refund queries and export using pay:refund:query / pay:refund:export.
pay/notify/index Tenant-scoped callback task/detail/log inspection using pay:notify:query.

V4340/EDU-022 adds no custom Education transaction page. It activates Pay-owned PostgreSQL order, extension, refund, notification-task, and notification-log persistence, makes every corresponding data object tenant-aware, and preserves channel-derived callback context plus the native tenant job. The pages begin with empty ledgers: no legacy order/payment/refund row is imported by inference.

V4350/EDU-023 extends the native pay/order/index page with 迁移旧支付交易. The modal accepts one reviewed terminal aggregate JSON manifest, warns that live states and inconsistent totals fail closed, locks submission while importing, and switches to a compact recent-audit table after success. Import and audit-query permissions are independent; an audit-only operator can open the history tab without receiving write access. The importer does not call channel SDKs, callbacks, or notification jobs, and the UI never renders raw payloads or error originals. The template remains instructional: source UUID/checksum, optional explicit native Member ID, and event digests must come from a controlled export/reconciliation process.

Education migration V4360/EDU-024 adds three more Education-menu entry points while continuing to reuse native Pay UI and controllers:

Menu component Capability and reused RuoYi module
pay/transfer/index Tenant-scoped native transfer query/detail/export using pay:transfer:query and pay:transfer:export; the existing sync job remains tenant-aware.
pay/wallet/balance/index Tenant-scoped native member wallet and transaction inspection using pay:wallet:query; the existing Member page retains the guarded pay:wallet:update-balance action.
pay/wallet/rechargePackage/index Native recharge-package create/update/delete administration with independent CRUD permissions; recharge refund uses pay:wallet-recharge:refund.

V4360 creates empty Transfer/Wallet ledgers and never derives balances from legacy payments. Tenant-qualified Redis locks, conditional administrator subtraction, positive-amount validation, composite tenant foreign keys, and non-negative database constraints protect the existing UI operations without adding an Education financial page or API.

Education migration V4370/EDU-025 adds a nested 商品中心 and five Education-menu entry points while reusing native Mall Product UI and controllers:

Menu component Capability and reused RuoYi module
mall/product/spu/index Native SPU/SKU create, update, status, delete, query, and export using the original Product services and five granular action permissions.
mall/product/category/index Tenant-scoped two-level category tree administration with query/create/update/delete permissions.
mall/product/brand/index Tenant-scoped brand query/create/update/delete administration.
mall/product/property/index Native property and property-value query/create/update/delete administration.
mall/product/comment/index Native comment query, visibility changes, and merchant replies using query/update permissions.

V4370 activates nine Product-owned PostgreSQL tables and makes all corresponding data objects tenant-aware. It creates an empty catalog: the legacy products endpoint exposes display labels, links, and media but no authoritative SKU, integer price, stock, brand, property, or delivery facts, so no automatic product import is performed. The existing SPU form was normalized by oxfmt; no new UI component or runtime dependency was introduced.

Education migration V4380/EDU-026 adds a nested 优惠券中心 and reuses two native Mall Promotion pages:

Menu component Capability and reused RuoYi module
mall/promotion/coupon/template/index Native coupon-template query/create/update/status/delete with Product SPU/category scope validation and four granular template permissions.
mall/promotion/coupon/index Native issued-coupon/member records, administrator send, query, and safe recovery using three granular coupon permissions.

V4380 activates tenant-scoped promotion_coupon_template and promotion_coupon, including composite tenant references, counter/validity/discount/use-state checks, registration issuance and expiry-job compatibility. It starts empty because legacy code campaigns/redemptions are not equivalent to pre-issued native member coupons. No Education coupon API or custom UI page was added.

Education migration V4390/EDU-027 adds a nested 交易中心 and reuses two native Mall Trade pages:

Menu component Capability and reused RuoYi module
mall/trade/order/index Tenant-scoped native order page/summary/detail, remark, price/address update, delivery and pick-up verification using trade:order:query, trade:order:update, and trade:order:pick-up.
mall/trade/config/index One active tenant-owned Trade configuration using trade:config:query and trade:config:save.

V4390 activates tenant-scoped trade_config, trade_cart, trade_order, trade_order_item, and trade_order_log, replaces Promotion's temporary absent-Trade adapter with the native TradeOrderApiImpl, and starts the order ledger empty. Existing legacy payment totals do not prove normalized member/SPU/SKU line items or order lifecycle, so no automatic order import or Education order API/UI was added. Delivery master data is activated by EDU-029; after-sale, brokerage, and special-order tables remain later slices.

Education migration V4400/EDU-028 adds a nested 营销活动 group and reuses two native Mall Promotion pages:

Menu component Capability
mall/promotion/discountActivity/index Native limited-time SKU discount query/create/update/close/delete with the original five action permissions.
mall/promotion/rewardActivity/index Native full-reduction/gift rule query/create/update/close/delete with the original five action permissions.

V4400 activates the three Promotion-owned tables consulted by normal Trade price calculation, tenantizes their native records, and preserves the existing PostgreSQL-compatible findInSet mapper path. No new frontend component or Education promotion API is introduced.

Education migration V4410/EDU-029 adds 配送管理 below the native Trade group and reuses three native pages:

Menu component Capability
mall/trade/delivery/express/index Tenant-scoped express-company query/create/update/delete/export with the original five permissions.
mall/trade/delivery/expressTemplate/index Express template and area-based charge/free rule query/create/update/delete with four permissions.
mall/trade/delivery/pickUpStore/index Pickup-store query/create/update/delete and verifier binding through the existing native controller.

V4410 activates all five Trade-owned delivery tables, connects Product SPUs to templates and pickup orders to stores through tenant-qualified references, and drives the native express calculator over real PostgreSQL persistence. The source has no physical-delivery master data, so no companies, stores, rules, or Product assignments are fabricated and no Education delivery API/UI is introduced.

Education migration V4420/EDU-030 adds 售后退款 below the native Trade group and reuses the native list/detail page:

Menu component Capability
mall/trade/afterSale/index Tenant-scoped after-sale query/detail, agree/disagree, return receipt/refusal, Pay Refund handling, and operation logs using the five exact trade:after-sale:* permissions.

V4420 activates Trade-owned trade_after_sale and trade_after_sale_log, connects Order, Order Item, Product, Pay Refund, Delivery, logs, and the order-item back-reference through tenant-qualified constraints, and retains the native app/admin services. The Vben page now sends auditReason, collects required refuseMemo in a validated locked modal, displays createTime as the application time, and applies exact permission guards to all actions. Source aggregate UUID refunds are not imported because they do not prove native Member, line-item, Product/SKU, return-logistics, or Pay Refund identities; no Education refund API/UI is introduced.

Education migration V4310 adds secure learning activation codes and the fifteenth custom Education admin page:

Menu component Capability and reused RuoYi module
education/activation-code/index Batch filtering/creation/optimistic editing, one-time plaintext generation, masked status queries, and confirmed disable. Mall-owned SPUs are referenced through the existing Education resource-product binding; Member supplies the redeeming principal; the existing Education entitlement event pipeline grants access.

The page separates query, management, and generation permissions. A generated plaintext set exists only in the controlled modal state: operators receive a prominent one-time warning, copy/download actions, and a second confirmation before discarding an unsaved set. Closing the modal clears plaintext; later tables return only masks. Batch product, duration, and prefix become immutable after the first code is generated. The app check/redeem endpoints reject administrator principals and use only the authenticated Member ID.

The operations page also loads /education/capability and shows feature flags, capabilities, migration themes, evidence levels, blockers, owning modules, and remaining legacy dependencies.

Custom Education HTTP contracts are isolated under apps/web-antd/src/api/education/; the Pay-owned legacy bridge remains under apps/web-antd/src/api/pay/legacy-account-import/. Page actions use the exact System RBAC permissions declared by the corresponding controllers and migration menu rows. Mutations use server-returned content, placement, or authoring versions rather than client-invented values.

UI contract

  • Uses the existing Vben, Ant Design Vue, requestClient, VXE Grid, and TableAction interfaces.
  • Preserves the existing application typography and theme; no new runtime design dependency or external font was added.
  • Shows explicit lifecycle labels and confirmations for publish/activate/archive actions.
  • Locks modal submissions during requests and reports validation or request failures through the existing message layer.
  • Rejects invalid question-option JSON and option-level correctness flags before submission.
  • Normalizes optional metadata/access-rule JSON objects and validates ordered membership IDs.
  • Enforces blueprint target selection and minimum <= suggested <= maximum before submission.
  • Keeps import execution separate from upload and preview, surfaces scan/parser state, and never writes an uploaded file directly into the question catalog.
  • Uses Member user IDs for classroom and entitlement subjects instead of introducing a second education account table.
  • Uses server-returned product-binding versions for deactivation and source-system event IDs for entitlement idempotency.
  • Keeps operational health read-only and displays only the bounded, sanitized detail returned by the backend.
  • Displays the module's honest migration/capability manifest alongside health instead of hiding deferred dependencies.
  • Separates education:content-export from the stronger education:content-export:answers permission and labels artifact generation as not yet implemented.
  • Keeps member lookup and point enrichment behind MemberUserApi/MemberPointApi; no Education account, level, balance, or generic point-ledger table was added.
  • Requires a feedback to be RESOLVED before a bounded 1100 point reward can be scheduled, records retry state in Education, and uses the feedback ID as the stable Member ledger business key.
  • Uses optimistic feedback versions, independent feedback/reward permissions, and immutable tenant-owned status events for administrator handling.
  • Applies RuoYi department/self data permission to classes, supervision rules, candidate class scope, and follow-up tasks, while keeping action permissions independent for query, rule authoring, generation, and handling.
  • Computes supervision evidence from Education's existing learning tables, enriches students through MemberUserApi, validates assignees through AdminUserApi, and never creates a duplicate student or administrator directory.
  • Uses optimistic follow-up versions and a tenant/batch/member database key so stale handling and duplicate generation both fail closed.
  • Separates badge query, definition-write, and manual-grant permissions; definition updates use optimistic versions and disabled badges cannot be granted.
  • Validates manual recipients through MemberUserApi, enriches grant history through Member/System public APIs, and sends badge messages through NotifyMessageSendApi without rolling back a durable grant when notification delivery fails.
  • Keeps System Tenant name and websites authoritative; Education stores only presentation extensions and never adds branding/theme fields to the public locator contract.
  • Separates appearance query, branding, settings, and theme permissions; every mutation uses a server-returned optimistic version.
  • Keeps platform templates global while tenant drafts/published state uses RuoYi tenant injection; public projection contains neither admin feature flags nor drafts.
  • Applies recursive public-secret rejection and a closed renderable-theme policy on the server, with matching JSON-object and obvious-secret preflight checks in Vben.
  • Uses responsive Ant Design grids, visible labels, loading states, confirmation before publication, and keyboard-operable template choices without adding a new UI dependency or font.
  • Separates activation-code query, management, and generation permissions; all updates/generation/disable actions submit server-returned optimistic versions.
  • Never lists activation-code plaintext after generation. The one-time modal offers explicit copy/download, warns before unsaved dismissal, clears plaintext after close, and confirms permanent disable actions.
  • Uses Mall SPU IDs, Member principals, and the existing Education resource binding/entitlement event pipeline instead of adding shadow product, account, coupon, or access-ledger models.
  • Reuses native Pay order/refund/notify pages and permissions over composite-tenant PostgreSQL tables; Education does not own a duplicate financial ledger, callback controller, retry worker, or export implementation.
  • Reuses the native order page for EDU-023 terminal aggregate import and recent redacted audit; Pay owns the importer, permissions, native ledgers, and audit tables.
  • Reuses native Pay Transfer, Wallet Balance, and Recharge Package pages for EDU-024; V4360 supplies tenant-scoped empty ledgers and granular permissions, while Member administration retains the existing balance-adjustment form.
  • Reuses native Mall Product SPU/SKU, Category, Brand, Property, and Comment pages for EDU-025; V4370 supplies tenant-scoped catalog persistence, composite graph references, and the original granular Product permissions without adding an Education product API.
  • Reuses native Mall Promotion Coupon Template and Issued Coupon pages for EDU-026; V4380 supplies tenant-scoped template/instance persistence, Product/Member composition, exact coupon permissions, and fail-closed adoption without adding an Education coupon API.
  • Reuses native Mall Trade Order and Config pages for EDU-027; V4390 supplies tenant-scoped order/cart/config persistence, Pay/Product/Coupon composition, exact Trade permissions, and fail-closed deferred-table adoption without adding an Education order API.
  • Reuses native Mall Promotion Discount Activity and Reward Activity pages for EDU-028; V4400 supplies tenant-scoped persistence, Product references, validated rule JSON, exact permissions, and real PostgreSQL API lookup evidence without adding an Education promotion API.
  • Reuses native Trade Express, Express Template, and Pickup Store pages for EDU-029; V4410 supplies tenant-scoped persistence, Product/Order references, exact permissions, and real PostgreSQL freight-calculation evidence without adding an Education delivery API.
  • Reuses the native Trade After Sale list/detail page for EDU-030; V4420 supplies tenant-scoped state/log persistence, Order/Product/Pay/Delivery references, exact permissions, and real PostgreSQL service isolation. Required audit/refusal fields, application time, loading locks, responsive forms, and action guards match the backend contract without adding an Education refund API.

Backend contract correction

The question revise endpoint previously delegated to the default TenantQuestionLifecycleService.reviseDraft implementation and could throw UnsupportedOperationException. It now performs tenant-scoped DRAFT/content-version CAS, increments content_version, and appends an immutable education_question_version snapshot. V4220 protects the same invariant in PostgreSQL. V4220 also backfills the education root and early question permission rows when a system_menu table is introduced after V4080/V4090, while still failing closed on conflicting IDs.

Verification

Successful commands from the Vben submodule root:

node --max-old-space-size=8192 node_modules/vue-tsc/bin/vue-tsc.js --noEmit --skipLibCheck -p apps/web-antd/tsconfig.json
./node_modules/.bin/oxfmt --check apps/web-antd/src/api/education apps/web-antd/src/views/education
./node_modules/.bin/oxlint apps/web-antd/src/api/education apps/web-antd/src/views/education
node --max-old-space-size=8192 ../../node_modules/vite/bin/vite.js build --mode production  # from apps/web-antd

All four commands passed. Production builds emit independent chunks for all fifteen Education pages, including learning-operations-*.js, supervision-*.js, badge-*.js, tenant-appearance-*.js, and activation-code-*.js, and only report the existing non-blocking Lightning CSS warnings for unrelated :deep selectors.

Backend evidence:

mvn -pl yudao-server -am -DskipTests compile
mvn -pl yudao-module-education -am -DskipTests test-compile
mvn -pl yudao-module-education -am -Dtest=EducationFlywayMigrationIntegrationTest -Dsurefire.failIfNoSpecifiedTests=false test
JAVA_HOME=/Users/tiku1/.sdkman/candidates/java/21.0.12-amzn \
  mvn -pl yudao-module-education -am \
  -Dtest=TenantQuestionLifecycleServiceImplTest,InfraFileImportObjectScanGatewayTest,StandardQuestionImportParserTest,QuestionImportJobServiceImplTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
JAVA_HOME=/Users/tiku1/.sdkman/candidates/java/21.0.12-amzn \
  mvn -pl yudao-module-education -am \
  -Dtest=MemberPointApiImplTest,LearningOperationsAdminServiceImplTest,LearningOperationsAdminControllerContractTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
JAVA_HOME=/Users/tiku1/.sdkman/candidates/java/21.0.12-amzn \
  mvn -pl yudao-module-education -am \
  -Dtest=StudentSupervisionAdminServiceImplTest,StudentSupervisionAdminControllerContractTest,StudentSupervisionPostgreSqlIntegrationTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
JAVA_HOME=/Users/tiku1/.sdkman/candidates/java/21.0.12-amzn \
  mvn -pl yudao-module-education -am \
  -Dtest=BadgeAdminControllerContractTest,BadgeAdminServiceImplTest,BadgeGrantServiceImplTest,BadgePostgreSqlIntegrationTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
JAVA_HOME=/Users/tiku1/.sdkman/candidates/java/21.0.12-amzn \
  mvn -pl yudao-module-education -am \
  -Dtest=TenantAppearanceAdminControllerContractTest,TenantAppearancePolicyTest,TenantAppearanceServiceImplTest,TenantAppearancePostgreSqlIntegrationTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl yudao-module-education -am \
  -Dtest=ActivationCodeServiceImplTest,ActivationCodeAdminControllerContractTest,ActivationCodeAppControllerHttpTest,ActivationCodePostgreSqlIntegrationTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl yudao-module-pay -am \
  -Dtest=PayLegacyAccountImportServiceImplTest,PayLegacyAccountImportControllerContractTest,PayChannelServiceTest,PayAppTenantContractTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl yudao-module-pay -am \
  -Dtest=PayOrderServiceTest,PayRefundServiceTest,PayNotifyServiceTest,PayTransactionTenantContractTest \
  -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl yudao-module-pay -am \
  -Dtest=PayTransferServiceTest,PayTransferWalletTenantContractTest,PayWalletLockRedisDAOTest,PayWalletControllerTest,PayWalletServiceImplTest,PayWalletRechargeServiceImplTest,WalletPayClientTest \
  -Dsurefire.failIfNoSpecifiedTests=false test

Compilation and test compilation passed. All 46 Flyway integration tests, all 35 selected question/import tests, all 13 selected category/export tests, all 8 selected Member point / learning-operations tests, all 8 selected supervision service/controller/PostgreSQL tests, all 7 selected badge controller/service/PostgreSQL tests, all 14 selected appearance controller/policy/service/PostgreSQL tests, and all 11 selected activation-code controller/service/PostgreSQL tests passed. The prior combined regression run executed 101 tests with no failures; V4310 then added the 11 focused activation-code checks. The EDU-021 Pay selection passes 29 tests: 17 Pay channel checks, two tenant/permission contracts, nine V4330 importer checks, and one request-log/dual-permission controller contract. The EDU-022 native transaction selection passes 86 tests: 46 order, 28 refund, 11 notify, and one tenant-inheritance contract. EDU-023 adds nine focused terminal importer/controller checks. EDU-024 adds 12 focused Transfer/Wallet checks. EDU-025 adds the Product tenant contract and the 44th Flyway scenario. EDU-026 adds the Promotion coupon tenant contract and the 45th Flyway scenario. EDU-027 adds the Trade tenant contract and the 46th Flyway scenario. V4390 evidence covers five tenant-aware Trade records, cross-tenant ID reuse and Cart/Order Item reference rejection, order/config state safety, exact order/config permissions/routes, five explicit sequences, and fail-closed global/deferred Trade adoption. V4380 evidence covers tenant-owned coupon templates/instances, cross-tenant identifier reuse and template-reference rejection, issue/use counter and discount/use-state safety, exact coupon permissions/routes, explicit identity sequences, and fail-closed global Coupon adoption. V4370 evidence covers nine tenant-scoped Product tables, cross-tenant identifier reuse and reference rejection, category-parent isolation, non-negative prices/stock/sales/commission/browse counts, rating bounds, active uniqueness, exact Product permissions/routes, and fail-closed global Product adoption. V4360 evidence covers cross-tenant composite references, per-tenant identifier reuse, same-tenant wallet uniqueness, negative-balance rejection, fail-closed global-wallet adoption, exact permission mappings, tenant-qualified Redis locking, safe administrator subtraction, recharge-refund wallet identity, and correct wallet-transfer lookup. V4350 evidence covers cross-tenant references, per-target-tenant source reuse, event count/digest consistency, sensitive-column absence, fail-closed global audit tables, and exact permission mappings. V4340 evidence covers composite tenant references, same merchant identifier across tenants, notification-task uniqueness and soft-delete recreation, fail-closed global transaction tables, and exact native menu/permission mappings. V4330 evidence covers provider/config mapping, safe disable mapping, replay/checksum conflict, mode/provider/channel rejection, unsafe endpoint/key rejection, audit/log redaction, composite tenant targets, and fail-closed global-audit adoption. V4310 evidence covers independent permissions, Member-only app principals, digest/mask-only persistence, tenant isolation, atomic entitlement/event creation, same-member replay, different-member conflict, disabled batch/binding rejection, and a concurrent unique winner. The current JDK emits Mockito's forward-looking dynamic-agent warning but does not fail the tests.

The normal pnpm --filter @vben/web-antd run typecheck entry point completed successfully with the configured workspace toolchain.

EDU-028 advances the PostgreSQL total to 47 passing Flyway scenarios and adds one real Spring/MyBatis Promotion API lookup test plus the Promotion activity tenant contract. The reused Discount/Reward page set passes Vben typecheck, scoped oxlint, and scoped oxfmt checks; V4400 proves exact routes/permissions, three explicit sequences, tenant-qualified Product references, PostgreSQL scope matching, rule validation, and fail-closed global-table adoption.

EDU-029 advances the PostgreSQL total to 48 passing Flyway scenarios and adds the five-record delivery tenant contract plus a real Spring/MyBatis template-service and TradeDeliveryPriceCalculator test. The reused delivery page set passes Vben typecheck and scoped lint/format checks; V4410 proves exact routes/permissions, five explicit sequences, tenant-qualified Product/Order references, area/location/amount/state constraints, persisted freight calculation, and fail-closed global-table adoption.

EDU-030 advances the PostgreSQL total to 49 passing Flyway scenarios and adds the two-record after-sale tenant contract plus a real Spring/MyBatis service/log integration test with the production tenant SQL interceptor. The corrected after-sale page passes Vben typecheck, scoped oxlint, and scoped oxfmt checks; V4420 proves exact route/permissions, two explicit sequences, tenant-qualified Order/Order Item/Product/Pay Refund/Delivery/log references, state/JSON/audit/return/refund constraints, isolated create/page/detail/log reads, and fail-closed global-table adoption.

Remaining scope

  • Run browser/API integration against a target deployment with V4220 applied and roles explicitly granted.
  • Add management pages and backend contracts for each later capability selected from the deferred legacy families.
  • Add real check-in, mock-exam, and activity-reward domains before exposing those legacy badge triggers; V4280 intentionally supports only migrated event sources.
  • Keep domains in System Tenant websites. V4300V4330 expose operational Pay configuration and bounded account import; V4340V4360 activate native Pay transaction/Transfer/Wallet ledgers and pages; V4370 activates Product; V4380 activates coupons; V4390V4420 activate the normal Trade order, Promotion, delivery, and after-sale dependencies/pages. Explicit legacy Product/code-coupon/order/refund import, Trade brokerage and special-order Promotion families, production export/Member-map/opening-balance/runbook evidence, non-equivalent payment modes/providers, tenant PNVS, generic private secret storage/rotation, automatic Mall/Pay fulfillment, and refund-to-entitlement revocation remain dedicated decisions. V4310 resolves new activation-code ownership but not legacy-code import.
  • Add the scheduled worker adapter for DAILY/WEEKLY supervision rules; V4270 persists the schedule and supports safe manual/interactive execution, but does not claim an automatic production worker deployment.
  • Replace raw relationship IDs with searchable selectors when stable simple-list contracts are exposed by Education.
  • Commit the UI changes in the UI repository and then advance the parent gitlink as part of the normal integration workflow.