feat(compliance): add content compliance detection across tenant, ops, and clients
Implements the Revision 8/9 compliance design: tenant + ops backends, ops-web content-safety pages, admin-web editor/publish gate integration, desktop publish block surfacing, and supporting migrations / shared types / config plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,13 @@ import type {
|
||||
CreateArticleRequest,
|
||||
CreateKolPackageRequest,
|
||||
CreateKolPromptRequest,
|
||||
CreateAckRequest,
|
||||
ComplianceAckRecord,
|
||||
ComplianceCheckArticleRequest,
|
||||
ComplianceCheckPlainRequest,
|
||||
ComplianceCheckResult,
|
||||
ComplianceManualReview,
|
||||
ComplianceRuntimeStatus,
|
||||
CreatePluginSessionRequest,
|
||||
CreatePluginSessionResponse,
|
||||
CreatePublishBatchRequest,
|
||||
@@ -1081,6 +1088,53 @@ export const publishJobsApi = {
|
||||
},
|
||||
}
|
||||
|
||||
export const complianceApi = {
|
||||
runtimeStatus() {
|
||||
return apiClient.get<ComplianceRuntimeStatus>('/api/tenant/compliance/runtime-status')
|
||||
},
|
||||
checkPlain(payload: ComplianceCheckPlainRequest) {
|
||||
return apiClient.post<ComplianceCheckResult, ComplianceCheckPlainRequest>(
|
||||
'/api/tenant/compliance/check',
|
||||
payload,
|
||||
)
|
||||
},
|
||||
checkArticle(articleId: number, payload: ComplianceCheckArticleRequest) {
|
||||
return apiClient.post<ComplianceCheckResult, ComplianceCheckArticleRequest>(
|
||||
`/api/tenant/articles/${articleId}/compliance/check`,
|
||||
payload,
|
||||
)
|
||||
},
|
||||
latestArticleCheck(articleId: number) {
|
||||
return apiClient.get<ComplianceCheckResult>(`/api/tenant/articles/${articleId}/compliance/latest`)
|
||||
},
|
||||
createAck(recordId: number, payload: CreateAckRequest) {
|
||||
return apiClient.post<ComplianceAckRecord, CreateAckRequest>(
|
||||
`/api/tenant/compliance/check-records/${recordId}/ack`,
|
||||
payload,
|
||||
)
|
||||
},
|
||||
submitManualReview(articleId: number, payload: { check_record_id: number; note: string }) {
|
||||
return apiClient.post<ComplianceManualReview, typeof payload>(
|
||||
`/api/tenant/articles/${articleId}/compliance/manual-reviews`,
|
||||
payload,
|
||||
)
|
||||
},
|
||||
latestManualReview(articleId: number, articleVersionId?: number | null) {
|
||||
return apiClient.get<ComplianceManualReview>(
|
||||
`/api/tenant/articles/${articleId}/compliance/manual-reviews/latest`,
|
||||
{
|
||||
params: articleVersionId ? { article_version_id: articleVersionId } : undefined,
|
||||
},
|
||||
)
|
||||
},
|
||||
cancelManualReview(articleId: number, reviewId: number, payload: { reason?: string }) {
|
||||
return apiClient.post<ComplianceManualReview, typeof payload>(
|
||||
`/api/tenant/articles/${articleId}/compliance/manual-reviews/${reviewId}/cancel`,
|
||||
payload,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
function removedMediaApi<T>(action: string): Promise<T> {
|
||||
return Promise.reject(
|
||||
new ApiClientError({
|
||||
|
||||
@@ -20,7 +20,8 @@ const publishStatusMap: Record<string, { label: string; color: string }> = {
|
||||
published: { label: 'status.publish.published', color: 'success' },
|
||||
publish_success: { label: 'status.publish.publish_success', color: 'success' },
|
||||
publish_failed: { label: 'status.publish.publish_failed', color: 'error' },
|
||||
pending_review: { label: 'status.publish.failed', color: 'error' },
|
||||
pending_review: { label: 'status.publish.pending_review', color: 'warning' },
|
||||
blocked_by_compliance: { label: 'status.publish.blocked_by_compliance', color: 'error' },
|
||||
}
|
||||
|
||||
export function formatDateTime(value?: string | null, pattern = 'YYYY-MM-DD HH:mm'): string {
|
||||
|
||||
@@ -61,6 +61,14 @@ const errorMessageMap: Record<string, string> = {
|
||||
desktop_account_not_found: '所选桌面账号不存在,请刷新后重试',
|
||||
desktop_client_offline: '当前登录账号的桌面客户端未在线,请先打开 desktop-client',
|
||||
desktop_publish_job_store_unavailable: '发布队列暂时不可用,请稍后重试',
|
||||
compliance_blocked: '内容合规检测已阻断本次发布',
|
||||
compliance_needs_ack: '内容合规检测需要确认风险后才能发布',
|
||||
compliance_ack_mismatch: '合规确认已失效,请重新检测并确认',
|
||||
compliance_engine_unavailable: '合规检测服务暂时不可用',
|
||||
compliance_manual_review_pending: '该版本正在等待运营审阅',
|
||||
compliance_manual_review_rejected: '该版本已被运营驳回,请修改后重新保存',
|
||||
compliance_manual_review_terminal: '该版本已有终态审阅结果',
|
||||
compliance_manual_review_not_cancellable: '当前审阅状态不能撤回',
|
||||
desktop_media_api_removed: '旧版媒体接口已下线,请刷新页面后重试',
|
||||
publisher_plugin_timeout: '浏览器插件响应超时,请刷新当前页面后重试',
|
||||
publisher_plugin_empty_response: '浏览器插件未返回数据,请刷新当前页面后重试',
|
||||
|
||||
Reference in New Issue
Block a user