mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 12:14:56 +08:00
fix(tm):建立 Trusted 唯一性与 Supersede 治理
This commit is contained in:
+116
-4
@@ -56,6 +56,7 @@ func (s *Server) handleAdminIndex(w http.ResponseWriter, r *http.Request) {
|
||||
"/admin/translation/handoff",
|
||||
"/admin/translation/memory/summary",
|
||||
"/admin/translation/memory/query",
|
||||
"/admin/translation/memory/conflicts",
|
||||
"/admin/translation/glossary/summary",
|
||||
"/admin/translation/glossary/query",
|
||||
"/admin/translation/glossary/diagnose",
|
||||
@@ -78,6 +79,7 @@ func (s *Server) handleAdminIndex(w http.ResponseWriter, r *http.Request) {
|
||||
"/admin/control/translation-worker-run",
|
||||
"/admin/control/translation-proofread",
|
||||
"/admin/control/translation-memory-confirm",
|
||||
"/admin/control/translation-memory-resolve-conflict",
|
||||
"/admin/control/translation-glossary-add",
|
||||
"/admin/control/translation-glossary-update",
|
||||
"/admin/control/translation-glossary-approve",
|
||||
@@ -133,6 +135,10 @@ func (s *Server) handleAdminControl(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleAdminTranslationMemoryConfirm(w, r)
|
||||
return
|
||||
}
|
||||
if action == "translation-memory-resolve-conflict" {
|
||||
s.handleAdminTranslationMemoryResolveConflict(w, r)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(action, "translation-glossary-") {
|
||||
s.handleAdminGlossaryControl(w, r, action)
|
||||
return
|
||||
@@ -222,6 +228,34 @@ func (s *Server) handleAdminControl(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationMemoryResolveConflict(w http.ResponseWriter, r *http.Request) {
|
||||
backend, ok := s.backend.(TranslationMemoryBackend)
|
||||
if !ok || backend == nil {
|
||||
writeErrorJSON(w, http.StatusServiceUnavailable, "translation_memory_backend_unavailable", "Rust bat Translation Memory backend is unavailable")
|
||||
return
|
||||
}
|
||||
var params backendrpc.TranslationMemoryResolveConflictParams
|
||||
if !decodeAdminTranslationJSON(w, r, ¶ms) {
|
||||
return
|
||||
}
|
||||
if err := validateTranslationMemoryResolveConflictParams(params); err != nil {
|
||||
writeErrorJSON(w, http.StatusBadRequest, "invalid_translation_memory_params", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := backend.TranslationMemoryResolveConflict(r.Context(), params)
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, "translation-memory-resolve-conflict", err)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusAccepted, AdminControlResponse{
|
||||
Service: "bat-api",
|
||||
Action: "translation-memory-resolve-conflict",
|
||||
RPCMethod: "translation.memory.resolve_conflict",
|
||||
Status: "accepted",
|
||||
Result: result,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationTaskUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
backend, ok := s.backend.(TranslationBackend)
|
||||
if !ok || backend == nil {
|
||||
@@ -1016,6 +1050,37 @@ func (s *Server) handleAdminTranslationMemoryQuery(w http.ResponseWriter, r *htt
|
||||
writeNoStoreJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationMemoryConflicts(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
||||
return
|
||||
}
|
||||
if !s.requireAdminToken(w, r) {
|
||||
return
|
||||
}
|
||||
backend, ok := s.backend.(TranslationMemoryBackend)
|
||||
if !ok || backend == nil {
|
||||
writeErrorJSON(w, http.StatusServiceUnavailable, "translation_memory_backend_unavailable", "Rust bat Translation Memory backend is unavailable")
|
||||
return
|
||||
}
|
||||
params, err := translationMemoryConflictsParams(r)
|
||||
if err != nil {
|
||||
writeErrorJSON(w, http.StatusBadRequest, "invalid_translation_memory_query", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := backend.TranslationMemoryConflicts(r.Context(), params)
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, "translation-memory-conflicts", err)
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodHead {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminSchedules(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
||||
@@ -1139,6 +1204,20 @@ func translationMemoryQueryParams(r *http.Request) (backendrpc.TranslationMemory
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func translationMemoryConflictsParams(r *http.Request) (backendrpc.TranslationMemoryConflictsParams, error) {
|
||||
params := backendrpc.TranslationMemoryConflictsParams{
|
||||
TranslationMemoryPath: firstTrimmedQuery(r.URL.Query(), "translation_memory_path", "tm_path"),
|
||||
}
|
||||
if raw := strings.TrimSpace(r.URL.Query().Get("limit")); raw != "" {
|
||||
limit, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil || limit == 0 || limit > 1000 {
|
||||
return backendrpc.TranslationMemoryConflictsParams{}, errors.New("limit must be in 1..=1000")
|
||||
}
|
||||
params.Limit = &limit
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func parseTextUnitQueryParams(r *http.Request) (backendrpc.TextUnitQueryParams, error) {
|
||||
query := r.URL.Query()
|
||||
params := backendrpc.TextUnitQueryParams{
|
||||
@@ -1334,6 +1413,32 @@ func validateTranslationMemoryConfirmParams(params backendrpc.TranslationMemoryC
|
||||
if strings.TrimSpace(params.RecordID) == "" || strings.TrimSpace(params.Reviewer) == "" {
|
||||
return errors.New("translation memory confirm requires record_id and reviewer")
|
||||
}
|
||||
if strings.TrimSpace(params.SupersedeRecordID) != "" && strings.TrimSpace(params.Reason) == "" {
|
||||
return errors.New("translation memory supersede requires reason")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateTranslationMemoryResolveConflictParams(params backendrpc.TranslationMemoryResolveConflictParams) error {
|
||||
if strings.TrimSpace(params.WinnerRecordID) == "" ||
|
||||
strings.TrimSpace(params.Reviewer) == "" ||
|
||||
strings.TrimSpace(params.Reason) == "" {
|
||||
return errors.New("translation memory conflict resolution requires winner_record_id, reviewer, and reason")
|
||||
}
|
||||
if len(params.ExpectedTrustedRecordIDs) == 0 {
|
||||
return errors.New("translation memory conflict resolution requires expected_trusted_record_ids")
|
||||
}
|
||||
seen := make(map[string]struct{}, len(params.ExpectedTrustedRecordIDs))
|
||||
for _, id := range params.ExpectedTrustedRecordIDs {
|
||||
id = strings.TrimSpace(id)
|
||||
if id == "" {
|
||||
return errors.New("expected_trusted_record_ids cannot contain empty record IDs")
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
return errors.New("expected_trusted_record_ids must be unique")
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1412,10 +1517,17 @@ func (s *Server) writeControlBackendError(w http.ResponseWriter, action string,
|
||||
message = "control request was canceled"
|
||||
default:
|
||||
var apiErr *backendrpc.APIError
|
||||
if errors.As(err, &apiErr) && apiErr.Kind == "not_implemented" {
|
||||
status = http.StatusNotImplemented
|
||||
code = "control_not_implemented"
|
||||
message = "Rust bat does not implement this control action"
|
||||
if errors.As(err, &apiErr) {
|
||||
switch apiErr.Kind {
|
||||
case "not_implemented":
|
||||
status = http.StatusNotImplemented
|
||||
code = "control_not_implemented"
|
||||
message = "Rust bat does not implement this control action"
|
||||
case "rpc_invalid_params":
|
||||
status = http.StatusBadRequest
|
||||
code = "invalid_control_params"
|
||||
message = apiErr.Message
|
||||
}
|
||||
}
|
||||
}
|
||||
s.logger.Printf("bat-api control action=%s error=%v", action, err)
|
||||
|
||||
+76
-24
@@ -1023,22 +1023,24 @@ func (f *fakeBackend) TaskCancel(ctx context.Context, taskID string) (*backendrp
|
||||
|
||||
type controlBackend struct {
|
||||
*fakeBackend
|
||||
calls []string
|
||||
parseTextUnitQueries []backendrpc.TextUnitQueryParams
|
||||
parseErrorQueries []backendrpc.TextUnitQueryParams
|
||||
translationTaskUpdates []backendrpc.TranslationTaskUpdateParams
|
||||
translationTaskListParams []backendrpc.TranslationTaskListParams
|
||||
translationMemorySummaryParams []backendrpc.TranslationMemorySummaryParams
|
||||
translationMemoryQueryParams []backendrpc.TranslationMemoryQueryParams
|
||||
translationMemoryConfirmParams []backendrpc.TranslationMemoryConfirmParams
|
||||
glossarySummaryParams []backendrpc.GlossarySummaryParams
|
||||
glossaryQueryParams []backendrpc.GlossaryQueryParams
|
||||
glossaryDiagnoseParams []backendrpc.GlossaryDiagnoseParams
|
||||
glossaryMutationParams []backendrpc.GlossaryTermMutationParams
|
||||
glossaryReviewParams []backendrpc.GlossaryReviewParams
|
||||
glossaryDeleteParams []backendrpc.GlossaryDeleteParams
|
||||
localizedPublishParams []backendrpc.LocalizedPublishParams
|
||||
localizedRollbackParams []backendrpc.LocalizedRollbackParams
|
||||
calls []string
|
||||
parseTextUnitQueries []backendrpc.TextUnitQueryParams
|
||||
parseErrorQueries []backendrpc.TextUnitQueryParams
|
||||
translationTaskUpdates []backendrpc.TranslationTaskUpdateParams
|
||||
translationTaskListParams []backendrpc.TranslationTaskListParams
|
||||
translationMemorySummaryParams []backendrpc.TranslationMemorySummaryParams
|
||||
translationMemoryQueryParams []backendrpc.TranslationMemoryQueryParams
|
||||
translationMemoryConfirmParams []backendrpc.TranslationMemoryConfirmParams
|
||||
translationMemoryConflictsParams []backendrpc.TranslationMemoryConflictsParams
|
||||
translationMemoryResolveParams []backendrpc.TranslationMemoryResolveConflictParams
|
||||
glossarySummaryParams []backendrpc.GlossarySummaryParams
|
||||
glossaryQueryParams []backendrpc.GlossaryQueryParams
|
||||
glossaryDiagnoseParams []backendrpc.GlossaryDiagnoseParams
|
||||
glossaryMutationParams []backendrpc.GlossaryTermMutationParams
|
||||
glossaryReviewParams []backendrpc.GlossaryReviewParams
|
||||
glossaryDeleteParams []backendrpc.GlossaryDeleteParams
|
||||
localizedPublishParams []backendrpc.LocalizedPublishParams
|
||||
localizedRollbackParams []backendrpc.LocalizedRollbackParams
|
||||
}
|
||||
|
||||
func (b *controlBackend) DaemonReload(ctx context.Context) (*backendrpc.Ack, error) {
|
||||
@@ -1183,18 +1185,19 @@ func (b *controlBackend) TranslationProofread(ctx context.Context) (json.RawMess
|
||||
func (b *controlBackend) TranslationMemorySummary(ctx context.Context, params backendrpc.TranslationMemorySummaryParams) (*backendrpc.TranslationMemorySummaryReport, error) {
|
||||
b.calls = append(b.calls, "translation.memory.summary")
|
||||
b.translationMemorySummaryParams = append(b.translationMemorySummaryParams, params)
|
||||
schemaVersion := uint64(1)
|
||||
schemaVersion := uint64(2)
|
||||
return &backendrpc.TranslationMemorySummaryReport{
|
||||
Available: true,
|
||||
Path: params.TranslationMemoryPath,
|
||||
SchemaVersion: &schemaVersion,
|
||||
Summary: &backendrpc.TranslationMemorySummary{
|
||||
SchemaVersion: schemaVersion,
|
||||
RecordCount: 2,
|
||||
TrustedCount: 1,
|
||||
CandidateCount: 1,
|
||||
SupersededCount: 0,
|
||||
RejectedCount: 0,
|
||||
SchemaVersion: schemaVersion,
|
||||
RecordCount: 2,
|
||||
TrustedCount: 1,
|
||||
CandidateCount: 1,
|
||||
SupersededCount: 0,
|
||||
RejectedCount: 0,
|
||||
CurrentTrustedCount: 1,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -1225,10 +1228,33 @@ func (b *controlBackend) TranslationMemoryConfirm(ctx context.Context, params ba
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *controlBackend) TranslationMemoryConflicts(ctx context.Context, params backendrpc.TranslationMemoryConflictsParams) (*backendrpc.TranslationMemoryConflictsReport, error) {
|
||||
b.calls = append(b.calls, "translation.memory.conflicts")
|
||||
b.translationMemoryConflictsParams = append(b.translationMemoryConflictsParams, params)
|
||||
return &backendrpc.TranslationMemoryConflictsReport{
|
||||
Available: true,
|
||||
Path: params.TranslationMemoryPath,
|
||||
Conflicts: []backendrpc.TranslationMemoryConflict{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *controlBackend) TranslationMemoryResolveConflict(ctx context.Context, params backendrpc.TranslationMemoryResolveConflictParams) (*backendrpc.TranslationMemoryResolveConflictReport, error) {
|
||||
b.calls = append(b.calls, "translation.memory.resolve_conflict")
|
||||
b.translationMemoryResolveParams = append(b.translationMemoryResolveParams, params)
|
||||
return &backendrpc.TranslationMemoryResolveConflictReport{
|
||||
Available: true,
|
||||
Path: params.TranslationMemoryPath,
|
||||
Entry: backendrpc.TranslationMemoryEntry{
|
||||
RecordID: params.WinnerRecordID,
|
||||
TrustStatus: backendrpc.TranslationMemoryStatusTrusted,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *controlBackend) GlossarySummary(ctx context.Context, params backendrpc.GlossarySummaryParams) (*backendrpc.GlossarySummaryReport, error) {
|
||||
b.calls = append(b.calls, "translation.glossary.summary")
|
||||
b.glossarySummaryParams = append(b.glossarySummaryParams, params)
|
||||
schemaVersion := uint64(1)
|
||||
schemaVersion := uint64(2)
|
||||
return &backendrpc.GlossarySummaryReport{
|
||||
Available: true,
|
||||
Path: params.GlossaryPath,
|
||||
@@ -1525,6 +1551,18 @@ func TestAdminTranslationQueryEndpointsProxyAuthenticatedRequests(t *testing.T)
|
||||
t.Fatalf("missing TM query source status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/admin/translation/memory/conflicts?translation_memory_path=%2Fvar%2Flib%2Fbat%2Ftranslation-memory.sqlite&limit=7", nil)
|
||||
request.Header.Set("Authorization", "Bearer translation-token")
|
||||
recorder = httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK ||
|
||||
len(backend.translationMemoryConflictsParams) != 1 ||
|
||||
backend.translationMemoryConflictsParams[0].TranslationMemoryPath != "/var/lib/bat/translation-memory.sqlite" ||
|
||||
backend.translationMemoryConflictsParams[0].Limit == nil ||
|
||||
*backend.translationMemoryConflictsParams[0].Limit != 7 {
|
||||
t.Fatalf("TM conflicts status=%d body=%s params=%#v", recorder.Code, recorder.Body.String(), backend.translationMemoryConflictsParams)
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/admin/translation/glossary/summary?glossary_path=%2Fvar%2Flib%2Fbat%2Fglossary.sqlite", nil)
|
||||
request.Header.Set("Authorization", "Bearer translation-token")
|
||||
recorder = httptest.NewRecorder()
|
||||
@@ -2345,6 +2383,7 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
{name: "translation worker run", action: "translation-worker-run", body: `{"provider":"mock","concurrency":8,"max_tasks":2,"retry_backoff_seconds":0,"worker_id":"dashboard-worker"}`, rpcMethod: "translation.worker.run", call: "translation.worker.run"},
|
||||
{name: "translation proofread", action: "translation-proofread", rpcMethod: "translation.proofread", call: "translation.proofread"},
|
||||
{name: "translation memory confirm", action: "translation-memory-confirm", body: `{"record_id":"tm-record-1","reviewer":"reviewer","reason":"reviewed"}`, rpcMethod: "translation.memory.confirm", call: "translation.memory.confirm"},
|
||||
{name: "translation memory resolve conflict", action: "translation-memory-resolve-conflict", body: `{"winner_record_id":"tm-record-1","expected_trusted_record_ids":["tm-record-1","tm-record-2"],"reviewer":"reviewer","reason":"selected"}`, rpcMethod: "translation.memory.resolve_conflict", call: "translation.memory.resolve_conflict"},
|
||||
{name: "translation glossary add", action: "translation-glossary-add", body: `{"term_id":"term-sensei","source_term":"Sensei","recommended_translation":"老师","review_status":"draft","source":{"source_kind":"manual","observed_unix_seconds":100}}`, rpcMethod: "translation.glossary.add", call: "translation.glossary.add"},
|
||||
{name: "translation glossary update", action: "translation-glossary-update", body: `{"term_id":"term-sensei","source_term":"Sensei","recommended_translation":"老师","review_status":"draft","reviewer":"reviewer","source":{"source_kind":"manual","observed_unix_seconds":100}}`, rpcMethod: "translation.glossary.update", call: "translation.glossary.update"},
|
||||
{name: "translation glossary approve", action: "translation-glossary-approve", body: `{"term_id":"term-sensei","reviewer":"reviewer","reason":"approved"}`, rpcMethod: "translation.glossary.approve", call: "translation.glossary.approve"},
|
||||
@@ -2390,6 +2429,11 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
backend.translationMemoryConfirmParams[0].Reviewer != "reviewer" {
|
||||
t.Fatalf("TM confirm params=%#v", backend.translationMemoryConfirmParams)
|
||||
}
|
||||
if len(backend.translationMemoryResolveParams) != 1 ||
|
||||
backend.translationMemoryResolveParams[0].WinnerRecordID != "tm-record-1" ||
|
||||
len(backend.translationMemoryResolveParams[0].ExpectedTrustedRecordIDs) != 2 {
|
||||
t.Fatalf("TM resolve params=%#v", backend.translationMemoryResolveParams)
|
||||
}
|
||||
if len(backend.glossaryMutationParams) != 2 ||
|
||||
backend.glossaryMutationParams[0].TermID != "term-sensei" ||
|
||||
backend.glossaryMutationParams[1].Reviewer != "reviewer" ||
|
||||
@@ -2432,6 +2476,14 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
t.Fatalf("invalid TM confirm status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodPost, "/admin/control/translation-memory-resolve-conflict", strings.NewReader(`{"winner_record_id":"tm-record-1","expected_trusted_record_ids":["tm-record-1","tm-record-1"],"reviewer":"reviewer","reason":"selected"}`))
|
||||
request.Header.Set("Authorization", "Bearer control-token")
|
||||
recorder = httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusBadRequest {
|
||||
t.Fatalf("invalid TM resolve status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodPost, "/admin/control/localized-publish", strings.NewReader(`{"from_worker":true,"translation_file":"/tmp/workbench.json"}`))
|
||||
request.Header.Set("Authorization", "Bearer control-token")
|
||||
recorder = httptest.NewRecorder()
|
||||
|
||||
+38
-1
@@ -512,6 +512,31 @@ paths:
|
||||
description: Missing or invalid admin token.
|
||||
"503":
|
||||
description: Rust bat Translation Memory backend is unavailable.
|
||||
/admin/translation/memory/conflicts:
|
||||
get:
|
||||
summary: List Rust-owned Translation Memory Trusted conflicts
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
default: 100
|
||||
- name: translation_memory_path
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Translation Memory exact-identity Trusted conflict groups.
|
||||
"400":
|
||||
description: Invalid conflict list limit.
|
||||
"401":
|
||||
description: Missing or invalid admin token.
|
||||
"503":
|
||||
description: Rust bat Translation Memory backend is unavailable.
|
||||
/admin/translation/glossary/summary:
|
||||
get:
|
||||
summary: Read Rust-owned Glossary summary
|
||||
@@ -637,7 +662,7 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [reload, refresh, restart, sync, verify, repair, catalog-refresh, schedule-add, schedule-update, schedule-remove, schedule-run, task-cancel, translation-task-update, translation-worker-run, translation-proofread, translation-memory-confirm, translation-glossary-add, translation-glossary-update, translation-glossary-approve, translation-glossary-deprecate, translation-glossary-delete, localized-publish, localized-rollback, release-cleanup]
|
||||
enum: [reload, refresh, restart, sync, verify, repair, catalog-refresh, schedule-add, schedule-update, schedule-remove, schedule-run, task-cancel, translation-task-update, translation-worker-run, translation-proofread, translation-memory-confirm, translation-memory-resolve-conflict, translation-glossary-add, translation-glossary-update, translation-glossary-approve, translation-glossary-deprecate, translation-glossary-delete, localized-publish, localized-rollback, release-cleanup]
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -752,6 +777,18 @@ paths:
|
||||
type: string
|
||||
record_id:
|
||||
type: string
|
||||
winner_record_id:
|
||||
type: string
|
||||
expected_trusted_record_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
supersede_record_id:
|
||||
type: string
|
||||
reviewer:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
term_id:
|
||||
type: string
|
||||
source_term:
|
||||
|
||||
@@ -93,6 +93,8 @@ type TranslationMemoryBackend interface {
|
||||
TranslationMemorySummary(ctx context.Context, params backendrpc.TranslationMemorySummaryParams) (*backendrpc.TranslationMemorySummaryReport, error)
|
||||
TranslationMemoryQuery(ctx context.Context, params backendrpc.TranslationMemoryQueryParams) (*backendrpc.TranslationMemoryQueryReport, error)
|
||||
TranslationMemoryConfirm(ctx context.Context, params backendrpc.TranslationMemoryConfirmParams) (*backendrpc.TranslationMemoryConfirmReport, error)
|
||||
TranslationMemoryConflicts(ctx context.Context, params backendrpc.TranslationMemoryConflictsParams) (*backendrpc.TranslationMemoryConflictsReport, error)
|
||||
TranslationMemoryResolveConflict(ctx context.Context, params backendrpc.TranslationMemoryResolveConflictParams) (*backendrpc.TranslationMemoryResolveConflictReport, error)
|
||||
}
|
||||
|
||||
// GlossaryBackend exposes Rust-owned Glossary management and diagnostics.
|
||||
@@ -238,6 +240,14 @@ func (r RPCClient) TranslationMemoryConfirm(ctx context.Context, params backendr
|
||||
return r.Client.TranslationMemoryConfirm(ctx, params)
|
||||
}
|
||||
|
||||
func (r RPCClient) TranslationMemoryConflicts(ctx context.Context, params backendrpc.TranslationMemoryConflictsParams) (*backendrpc.TranslationMemoryConflictsReport, error) {
|
||||
return r.Client.TranslationMemoryConflicts(ctx, params)
|
||||
}
|
||||
|
||||
func (r RPCClient) TranslationMemoryResolveConflict(ctx context.Context, params backendrpc.TranslationMemoryResolveConflictParams) (*backendrpc.TranslationMemoryResolveConflictReport, error) {
|
||||
return r.Client.TranslationMemoryResolveConflict(ctx, params)
|
||||
}
|
||||
|
||||
func (r RPCClient) GlossarySummary(ctx context.Context, params backendrpc.GlossarySummaryParams) (*backendrpc.GlossarySummaryReport, error) {
|
||||
return r.Client.GlossarySummary(ctx, params)
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("/admin/translation/handoff", s.handleAdminTranslationHandoff)
|
||||
mux.HandleFunc("/admin/translation/memory/summary", s.handleAdminTranslationMemorySummary)
|
||||
mux.HandleFunc("/admin/translation/memory/query", s.handleAdminTranslationMemoryQuery)
|
||||
mux.HandleFunc("/admin/translation/memory/conflicts", s.handleAdminTranslationMemoryConflicts)
|
||||
mux.HandleFunc("/admin/translation/glossary/summary", s.handleAdminGlossarySummary)
|
||||
mux.HandleFunc("/admin/translation/glossary/query", s.handleAdminGlossaryQuery)
|
||||
mux.HandleFunc("/admin/translation/glossary/diagnose", s.handleAdminGlossaryDiagnose)
|
||||
@@ -202,6 +203,7 @@ func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||
"/admin/translation/handoff",
|
||||
"/admin/translation/memory/summary",
|
||||
"/admin/translation/memory/query",
|
||||
"/admin/translation/memory/conflicts",
|
||||
"/admin/translation/glossary/summary",
|
||||
"/admin/translation/glossary/query",
|
||||
"/admin/translation/glossary/diagnose",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ contract fixture。JSON 由 Rust 代码路径产出后归一化,只替换本
|
||||
window 为 7260 秒(默认 3600 秒验证周期和 60 秒失败重试周期)。
|
||||
- 对应 release 的 `official-sync-snapshot.json`。
|
||||
- `launcher_metadata` 与 `game_main_config_bootstrap` 的 Go mirror 解码。
|
||||
- Rust Glossary V2 query 响应,覆盖 alias、approved review、source provenance 和完整 history。
|
||||
- Rust Glossary domain/feature contract V1、SQLite persistence schema V2 的 query 响应,覆盖 alias、approved review、source provenance 和完整 history。
|
||||
|
||||
这些 fixture 只用于 schema / mirror 回归,不代表真实资源版本,也不替代 live
|
||||
daemon socket 或完整发布切换验证。
|
||||
|
||||
Reference in New Issue
Block a user