feat: 知识治理平台(含已验证发布/回滚契约)

全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环):

- 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口)
- 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入
  parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截
- 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过):
  * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id)
  * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配
  * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数
  * 回滚 job payload 支持 prior_execution_id(strict 解码)
- 三通道发布+回滚演练实测:publish 4/4 verified,检索命中,
  rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false
- 前端 A·Data-console 风格全站统一,120 用例通过
- 门禁:go vet / go test 全包通过,gofmt 干净
This commit is contained in:
2026-09-13 12:02:58 +08:00
committed by 姚章浩
parent ed48c4ceda
commit dcdc8d1ddd
410 changed files with 56661 additions and 1 deletions
+169
View File
@@ -0,0 +1,169 @@
package postgres
const enqueueJobSQL = `
INSERT INTO jobs (
id, type, payload, state, priority, available_at,
max_attempts, idempotency_key, created_at, updated_at
) VALUES ($1, $2, $3, 'pending', $4, $5, $6, $7, $8, $8)
ON CONFLICT (idempotency_key)
DO UPDATE SET idempotency_key = EXCLUDED.idempotency_key
RETURNING id, type, payload, state, priority, available_at,
lease_owner, lease_until, attempt, max_attempts,
last_error, idempotency_key, created_at, updated_at
`
const claimJobSQL = `
WITH candidate AS (
SELECT id
FROM jobs
WHERE (
(state IN ('pending', 'retry') AND available_at <= $2)
OR (state = 'running' AND lease_until < $2)
)
AND attempt < max_attempts
AND type = ANY($4::text[])
ORDER BY priority DESC, available_at ASC, created_at ASC, id ASC
FOR UPDATE SKIP LOCKED
LIMIT 1
)
UPDATE jobs AS j
SET state = 'running',
started_at = $2,
lease_owner = $1,
lease_until = $3,
attempt = j.attempt + 1,
updated_at = $2
FROM candidate
WHERE j.id = candidate.id
RETURNING j.id, j.type, j.payload, j.state, j.priority, j.available_at,
j.lease_owner, j.lease_until, j.attempt, j.max_attempts,
j.last_error, j.idempotency_key, j.created_at, j.updated_at
`
const completeJobSQL = `
UPDATE jobs
SET state = 'completed',
completed_at = $3,
lease_owner = NULL,
lease_until = NULL,
updated_at = $3
WHERE id = $1
AND lease_owner = $2
AND state = 'running'
`
const heartbeatJobSQL = `
UPDATE jobs
SET lease_until = $4,
updated_at = $3
WHERE id = $1
AND lease_owner = $2
AND state = 'running'
`
const failJobSQL = `
WITH updated AS (
UPDATE jobs
SET state = CASE WHEN attempt >= max_attempts THEN 'failed' ELSE 'retry' END,
available_at = $6,
last_error = $3,
last_error_code = CASE WHEN $5 <> '' THEN $5 ELSE last_error_code END,
lease_owner = NULL,
lease_until = NULL,
updated_at = $4
WHERE id = $1
AND lease_owner = $2
AND state = 'running'
RETURNING id, type, payload, last_error,
CASE WHEN last_error_code = '' THEN 'handler_error' ELSE last_error_code END AS error_code,
attempt, max_attempts,
COALESCE(business_change_set_id,'') AS bc, COALESCE(business_version_id,'') AS bv,
COALESCE(business_action,'') AS ba, COALESCE(business_target_kb_id,'') AS bt,
state
)
INSERT INTO job_failures (
job_id, job_type, publication_id, error_code, last_error, attempt, max_attempts_at_failure,
business_change_set_id, business_version_id, business_action, business_target_kb_id,
created_at
)
SELECT updated.id, updated.type, COALESCE(updated.payload->>'publication_id',''), updated.error_code,
updated.last_error, updated.attempt, updated.max_attempts,
updated.bc, updated.bv, updated.ba, updated.bt, $4
FROM updated
WHERE updated.state = 'failed' AND updated.attempt > 0
ON CONFLICT (job_id, attempt) DO UPDATE
SET publication_id = EXCLUDED.publication_id,
error_code = EXCLUDED.error_code,
last_error = EXCLUDED.last_error,
max_attempts_at_failure = EXCLUDED.max_attempts_at_failure,
business_change_set_id = EXCLUDED.business_change_set_id,
business_version_id = EXCLUDED.business_version_id,
business_action = EXCLUDED.business_action,
business_target_kb_id = EXCLUDED.business_target_kb_id,
state = 'open',
resolved_at = NULL,
resolution_actor_ref = '',
resolution_reason = '',
created_at = EXCLUDED.created_at
`
const listJobsSQL = `
SELECT id, type, payload, state, priority, available_at,
lease_owner, lease_until, attempt, max_attempts,
last_error, idempotency_key, created_at, updated_at
FROM jobs
ORDER BY created_at DESC, id DESC
LIMIT $1
`
// P6: a permanent failure parks the job in 'failed' immediately and projects
// the terminal attempt into the dead-letter table.
const failTerminalJobSQL = `
WITH parked AS (
UPDATE jobs
SET state = 'failed',
available_at = $5,
last_error = $3,
last_error_code = CASE WHEN $4 = '' THEN 'handler_error' ELSE $4 END,
lease_owner = NULL,
lease_until = NULL,
updated_at = $5
WHERE id = $1
AND lease_owner = $2
AND lease_owner IS NOT NULL
AND state = 'running'
RETURNING id, type, payload, last_error,
CASE WHEN $4 = '' THEN 'handler_error' ELSE $4 END AS error_code,
attempt, max_attempts,
COALESCE(business_change_set_id,'') AS bc, COALESCE(business_version_id,'') AS bv,
COALESCE(business_action,'') AS ba, COALESCE(business_target_kb_id,'') AS bt
)
INSERT INTO job_failures (
job_id, job_type, publication_id, error_code, last_error, attempt, max_attempts_at_failure,
business_change_set_id, business_version_id, business_action, business_target_kb_id,
created_at
)
SELECT parked.id, parked.type, COALESCE(parked.payload->>'publication_id',''), parked.error_code,
parked.last_error, GREATEST(parked.attempt,1), parked.max_attempts,
parked.bc, parked.bv, parked.ba, parked.bt, $5
FROM parked
ON CONFLICT (job_id, attempt) DO UPDATE
SET publication_id = EXCLUDED.publication_id,
error_code = EXCLUDED.error_code,
last_error = EXCLUDED.last_error,
max_attempts_at_failure = EXCLUDED.max_attempts_at_failure,
state = 'open',
resolved_at = NULL,
resolution_actor_ref = '',
resolution_reason = '',
created_at = EXCLUDED.created_at
`
// P10 liveness: every claim attempt refreshes the worker row so the API can
// distinguish "worker loop alive" from "process dead" without an HTTP port.
const workerHeartbeatSQL = `
INSERT INTO worker_heartbeats(worker_id, job_types, started_at, last_seen_at)
VALUES($1, $2, $3, $3)
ON CONFLICT (worker_id) DO UPDATE
SET job_types = EXCLUDED.job_types, last_seen_at = EXCLUDED.last_seen_at
`