forked from wangziqi/gongxue-base
feat: build legacy practice navigation
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -274,6 +274,232 @@ async function validationChecks(): Promise<CheckResult[]> {
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_practice_navigation_missing_entries',
|
||||
await scalar(
|
||||
`
|
||||
select case
|
||||
when exists (
|
||||
select 1 from public.questions
|
||||
where tenant_id = $1 and legacy_id is not null
|
||||
)
|
||||
and not exists (
|
||||
select 1 from public.content_entries
|
||||
where tenant_id = $1
|
||||
and entry_type = 'question_practice'
|
||||
and legacy_id is not null
|
||||
)
|
||||
then 1 else 0 end
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Imported legacy questions exist but no question_practice content entries were generated.',
|
||||
'Legacy question practice content entries are present when imported questions exist.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_practice_navigation_missing_nodes',
|
||||
await scalar(
|
||||
`
|
||||
select case
|
||||
when exists (
|
||||
select 1 from public.questions
|
||||
where tenant_id = $1 and legacy_id is not null
|
||||
)
|
||||
and not exists (
|
||||
select 1 from public.content_nodes
|
||||
where tenant_id = $1
|
||||
and legacy_id is not null
|
||||
and metadata->>'source' in (
|
||||
'pocketbase.module_nodes',
|
||||
'pocketbase.subjects',
|
||||
'pocketbase.categories',
|
||||
'pocketbase.migration_review.unresolved_category'
|
||||
)
|
||||
)
|
||||
then 1 else 0 end
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Imported legacy questions exist but no content nodes were generated.',
|
||||
'Legacy content nodes are present when imported questions exist.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_practice_collections_missing_items',
|
||||
await scalar(
|
||||
`
|
||||
select count(*)
|
||||
from public.question_collections qc
|
||||
where qc.tenant_id = $1
|
||||
and qc.legacy_id is not null
|
||||
and qc.status = 'active'
|
||||
and qc.question_count = 0
|
||||
and exists (
|
||||
select 1
|
||||
from public.questions q
|
||||
left join public.content_nodes cn
|
||||
on cn.tenant_id = q.tenant_id
|
||||
and cn.id = q.content_node_id
|
||||
where q.tenant_id = qc.tenant_id
|
||||
and q.status = 'published'
|
||||
and (
|
||||
q.content_node_id = qc.node_id
|
||||
or q.category_id = qc.category_id
|
||||
or (qc.category_id is null and q.subject_id = qc.subject_id)
|
||||
)
|
||||
)
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Active legacy question collections exist without collection items even though matching questions exist.',
|
||||
'Active legacy question collections have item counts when matching questions exist.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_questions_without_navigation',
|
||||
await scalar(
|
||||
`
|
||||
select count(*)
|
||||
from public.questions
|
||||
where tenant_id = $1
|
||||
and legacy_id is not null
|
||||
and status = 'published'
|
||||
and (
|
||||
entry_id is null
|
||||
or content_node_id is null
|
||||
or primary_collection_id is null
|
||||
)
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Some imported published questions are not attached to entry/node/collection navigation.',
|
||||
'Imported published questions are attached to entry/node/collection navigation.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_unresolved_categories_not_isolated',
|
||||
await scalar(
|
||||
`
|
||||
select count(*)
|
||||
from public.questions q
|
||||
left join public.question_collections qc
|
||||
on qc.tenant_id = q.tenant_id
|
||||
and qc.id = q.primary_collection_id
|
||||
left join public.content_nodes cn
|
||||
on cn.tenant_id = q.tenant_id
|
||||
and cn.id = q.content_node_id
|
||||
where q.tenant_id = $1
|
||||
and q.legacy_id is not null
|
||||
and q.legacy_category_id is not null
|
||||
and q.category_id is null
|
||||
and q.node_id is null
|
||||
and (
|
||||
qc.id is null
|
||||
or qc.status <> 'draft'
|
||||
or qc.access_rules->>'requiresReview' <> 'true'
|
||||
or cn.id is null
|
||||
or cn.is_active <> false
|
||||
or cn.access_rules->>'requiresReview' <> 'true'
|
||||
)
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Questions with unresolved legacy categories are not isolated into draft review collections/inactive nodes.',
|
||||
'Questions with unresolved legacy categories are isolated for tenant-admin review.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_orphan_node_questions_not_isolated',
|
||||
await scalar(
|
||||
`
|
||||
select count(*)
|
||||
from public.questions q
|
||||
left join public.question_collections qc
|
||||
on qc.tenant_id = q.tenant_id
|
||||
and qc.id = q.primary_collection_id
|
||||
left join public.content_nodes cn
|
||||
on cn.tenant_id = q.tenant_id
|
||||
and cn.id = q.content_node_id
|
||||
left join public.content_entries ce
|
||||
on ce.tenant_id = q.tenant_id
|
||||
and ce.id = q.entry_id
|
||||
where q.tenant_id = $1
|
||||
and q.legacy_id is not null
|
||||
and q.legacy_node_id is not null
|
||||
and q.node_id is null
|
||||
and q.category_id is null
|
||||
and q.legacy_category_id is null
|
||||
and not (
|
||||
qc.status = 'draft'
|
||||
and qc.access_rules->>'requiresReview' = 'true'
|
||||
and qc.metadata->>'source' = 'pocketbase.migration_review.orphan_subject'
|
||||
and cn.is_active = false
|
||||
and cn.access_rules->>'requiresReview' = 'true'
|
||||
and ce.visibility = 'hidden'
|
||||
and ce.access_rules->>'requiresReview' = 'true'
|
||||
)
|
||||
and not (
|
||||
qc.status = 'active'
|
||||
and qc.metadata->>'source' = 'pocketbase.subjects'
|
||||
and qc.metadata->>'scope' = 'subject_all'
|
||||
and cn.node_type = 'subject'
|
||||
and cn.marker_type = 'subject'
|
||||
and cn.is_active = true
|
||||
and ce.visibility <> 'hidden'
|
||||
)
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Questions with unresolved legacy nodeId are neither isolated into hidden review navigation nor attached to a public subject fallback.',
|
||||
'Questions with unresolved legacy nodeId are isolated or attached to a public subject fallback.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_node_questions_wrong_navigation',
|
||||
await scalar(
|
||||
`
|
||||
select count(*)
|
||||
from public.questions q
|
||||
join public.module_nodes mn
|
||||
on mn.tenant_id = q.tenant_id
|
||||
and mn.id = q.node_id
|
||||
left join public.content_nodes cn
|
||||
on cn.tenant_id = q.tenant_id
|
||||
and cn.legacy_id = 'module_node:' || mn.legacy_id
|
||||
left join public.question_collections qc
|
||||
on qc.tenant_id = q.tenant_id
|
||||
and qc.legacy_id = 'module_node:' || mn.legacy_id || ':direct'
|
||||
where q.tenant_id = $1
|
||||
and q.legacy_id is not null
|
||||
and q.node_id is not null
|
||||
and (
|
||||
cn.id is null
|
||||
or q.content_node_id is distinct from cn.id
|
||||
or qc.id is null
|
||||
or q.primary_collection_id is distinct from qc.id
|
||||
)
|
||||
`,
|
||||
[tenantId],
|
||||
),
|
||||
'Some imported questions with legacy nodeId are not attached to the matching content node/direct collection.',
|
||||
'Imported questions with legacy nodeId are attached to matching content nodes and direct collections.',
|
||||
),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
result(
|
||||
'legacy_mock_blueprints_incomplete',
|
||||
|
||||
Reference in New Issue
Block a user