mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
fix(glossary): 补齐术语删除和审核门禁
This commit is contained in:
@@ -82,6 +82,7 @@ func (s *Server) handleAdminIndex(w http.ResponseWriter, r *http.Request) {
|
||||
"/admin/control/translation-glossary-update",
|
||||
"/admin/control/translation-glossary-approve",
|
||||
"/admin/control/translation-glossary-deprecate",
|
||||
"/admin/control/translation-glossary-delete",
|
||||
"/admin/control/localized-publish",
|
||||
"/admin/control/localized-rollback",
|
||||
},
|
||||
@@ -495,6 +496,29 @@ func (s *Server) handleAdminGlossaryControl(w http.ResponseWriter, r *http.Reque
|
||||
Status: "accepted",
|
||||
Result: result,
|
||||
})
|
||||
case "translation-glossary-delete":
|
||||
var params backendrpc.GlossaryDeleteParams
|
||||
if !decodeAdminTranslationJSON(w, r, ¶ms) {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(params.TermID) == "" ||
|
||||
strings.TrimSpace(params.Reviewer) == "" ||
|
||||
strings.TrimSpace(params.Reason) == "" {
|
||||
writeErrorJSON(w, http.StatusBadRequest, "invalid_glossary_params", "term_id, reviewer and reason are required for delete")
|
||||
return
|
||||
}
|
||||
result, err := backend.GlossaryDelete(r.Context(), params)
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, action, err)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusAccepted, AdminControlResponse{
|
||||
Service: "bat-api",
|
||||
Action: action,
|
||||
RPCMethod: glossaryControlRPCMethod(action),
|
||||
Status: "accepted",
|
||||
Result: result,
|
||||
})
|
||||
default:
|
||||
writeErrorJSON(w, http.StatusNotFound, "control_not_found", "unknown glossary control action")
|
||||
}
|
||||
@@ -510,6 +534,8 @@ func glossaryControlRPCMethod(action string) string {
|
||||
return "translation.glossary.approve"
|
||||
case "translation-glossary-deprecate":
|
||||
return "translation.glossary.deprecate"
|
||||
case "translation-glossary-delete":
|
||||
return "translation.glossary.delete"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -642,6 +642,7 @@ type controlBackend struct {
|
||||
glossaryDiagnoseParams []backendrpc.GlossaryDiagnoseParams
|
||||
glossaryMutationParams []backendrpc.GlossaryTermMutationParams
|
||||
glossaryReviewParams []backendrpc.GlossaryReviewParams
|
||||
glossaryDeleteParams []backendrpc.GlossaryDeleteParams
|
||||
localizedPublishParams []backendrpc.LocalizedPublishParams
|
||||
localizedRollbackParams []backendrpc.LocalizedRollbackParams
|
||||
}
|
||||
@@ -905,6 +906,12 @@ func (b *controlBackend) GlossaryDeprecate(ctx context.Context, params backendrp
|
||||
return &backendrpc.GlossaryMutationReport{Available: true, Path: params.GlossaryPath}, nil
|
||||
}
|
||||
|
||||
func (b *controlBackend) GlossaryDelete(ctx context.Context, params backendrpc.GlossaryDeleteParams) (*backendrpc.GlossaryMutationReport, error) {
|
||||
b.calls = append(b.calls, "translation.glossary.delete")
|
||||
b.glossaryDeleteParams = append(b.glossaryDeleteParams, params)
|
||||
return &backendrpc.GlossaryMutationReport{Available: true, Path: params.GlossaryPath}, nil
|
||||
}
|
||||
|
||||
func (b *controlBackend) LocalizedStatus(ctx context.Context) (json.RawMessage, error) {
|
||||
b.calls = append(b.calls, "localized.status")
|
||||
return json.RawMessage(`{"localized_release_status":"localized","status_code":"localized.published"}`), nil
|
||||
@@ -1708,6 +1715,7 @@ func TestOpenAPIAndAdminReservedEndpoints(t *testing.T) {
|
||||
!strings.Contains(rr.Body.String(), "/admin/tasks") ||
|
||||
!strings.Contains(rr.Body.String(), "/admin/parse/text-units") ||
|
||||
!strings.Contains(rr.Body.String(), "translation_results") ||
|
||||
!strings.Contains(rr.Body.String(), "glossary_override") ||
|
||||
!strings.Contains(rr.Body.String(), "task-cancel") ||
|
||||
!strings.Contains(rr.Body.String(), "/admin/translation/memory/query") ||
|
||||
!strings.Contains(rr.Body.String(), "translation-memory-confirm") {
|
||||
@@ -1936,7 +1944,7 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
{name: "repair", action: "repair", rpcMethod: "resource.repair", call: "resource.repair"},
|
||||
{name: "catalog refresh", action: "catalog-refresh", rpcMethod: "catalog.refresh", call: "catalog.refresh"},
|
||||
{name: "task cancel", action: "task-cancel", body: `{"task_id":"task-sync-1"}`, rpcMethod: "task.cancel", call: "task.cancel"},
|
||||
{name: "translation task update", action: "translation-task-update", body: `{"task_id":"textunit/v-current/Scenario","status":"completed","provider":"manual","provider_run_id":"manual-run-1","translation_results":[{"unit_id":"direct:a#unit:0","source_text":"source","translated_text":"译文"}]}`, rpcMethod: "translation.task.update", call: "translation.task.update"},
|
||||
{name: "translation task update", action: "translation-task-update", body: `{"task_id":"textunit/v-current/Scenario","status":"completed","provider":"manual","provider_run_id":"manual-run-1","translation_results":[{"unit_id":"direct:a#unit:0","source_text":"source","translated_text":"译文","glossary_override":{"reviewer":"reviewer","reason":"approved deviation","provenance":"manual-review","confirmed_unix_seconds":100}}]}`, rpcMethod: "translation.task.update", call: "translation.task.update"},
|
||||
{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"},
|
||||
@@ -1944,6 +1952,7 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
{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"},
|
||||
{name: "translation glossary deprecate", action: "translation-glossary-deprecate", body: `{"term_id":"term-sensei","reviewer":"reviewer","reason":"retired"}`, rpcMethod: "translation.glossary.deprecate", call: "translation.glossary.deprecate"},
|
||||
{name: "translation glossary delete", action: "translation-glossary-delete", body: `{"term_id":"term-sensei","reviewer":"reviewer","reason":"duplicate"}`, rpcMethod: "translation.glossary.delete", call: "translation.glossary.delete"},
|
||||
{name: "localized publish", action: "localized-publish", body: `{"from_worker":true,"localized_release_id":"localized-1","force":true}`, rpcMethod: "localized.publish", call: "localized.publish"},
|
||||
{name: "localized rollback", action: "localized-rollback", body: `{"localized_release_id":"localized-1"}`, rpcMethod: "localized.rollback", call: "localized.rollback"},
|
||||
}
|
||||
@@ -1972,7 +1981,9 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
if len(backend.translationTaskUpdates) != 1 ||
|
||||
backend.translationTaskUpdates[0].Provider != "manual" ||
|
||||
len(backend.translationTaskUpdates[0].TranslationResults) != 1 ||
|
||||
backend.translationTaskUpdates[0].TranslationResults[0].TranslatedText != "译文" {
|
||||
backend.translationTaskUpdates[0].TranslationResults[0].TranslatedText != "译文" ||
|
||||
backend.translationTaskUpdates[0].TranslationResults[0].GlossaryOverride == nil ||
|
||||
backend.translationTaskUpdates[0].TranslationResults[0].GlossaryOverride.Reviewer != "reviewer" {
|
||||
t.Fatalf("translation task updates=%#v", backend.translationTaskUpdates)
|
||||
}
|
||||
if len(backend.translationMemoryConfirmParams) != 1 ||
|
||||
@@ -1984,8 +1995,10 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
|
||||
backend.glossaryMutationParams[0].TermID != "term-sensei" ||
|
||||
backend.glossaryMutationParams[1].Reviewer != "reviewer" ||
|
||||
len(backend.glossaryReviewParams) != 2 ||
|
||||
backend.glossaryReviewParams[0].TermID != "term-sensei" {
|
||||
t.Fatalf("Glossary params mutation=%#v review=%#v", backend.glossaryMutationParams, backend.glossaryReviewParams)
|
||||
backend.glossaryReviewParams[0].TermID != "term-sensei" ||
|
||||
len(backend.glossaryDeleteParams) != 1 ||
|
||||
backend.glossaryDeleteParams[0].Reason != "duplicate" {
|
||||
t.Fatalf("Glossary params mutation=%#v review=%#v delete=%#v", backend.glossaryMutationParams, backend.glossaryReviewParams, backend.glossaryDeleteParams)
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/control/translation-task-update", strings.NewReader(`{"task_id":""}`))
|
||||
|
||||
+16
-1
@@ -560,7 +560,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, localized-publish, localized-rollback]
|
||||
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]
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -626,6 +626,21 @@ paths:
|
||||
type: string
|
||||
translated_text:
|
||||
type: string
|
||||
glossary_override:
|
||||
type: object
|
||||
required: [reviewer, reason, provenance, confirmed_unix_seconds]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
reviewer:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
provenance:
|
||||
type: string
|
||||
confirmed_unix_seconds:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
fixture_path:
|
||||
type: string
|
||||
concurrency:
|
||||
|
||||
@@ -102,6 +102,7 @@ type GlossaryBackend interface {
|
||||
GlossaryUpdate(ctx context.Context, params backendrpc.GlossaryTermMutationParams) (*backendrpc.GlossaryMutationReport, error)
|
||||
GlossaryApprove(ctx context.Context, params backendrpc.GlossaryReviewParams) (*backendrpc.GlossaryMutationReport, error)
|
||||
GlossaryDeprecate(ctx context.Context, params backendrpc.GlossaryReviewParams) (*backendrpc.GlossaryMutationReport, error)
|
||||
GlossaryDelete(ctx context.Context, params backendrpc.GlossaryDeleteParams) (*backendrpc.GlossaryMutationReport, error)
|
||||
}
|
||||
|
||||
// LocalizedBackend exposes localized release status and the explicit
|
||||
@@ -236,6 +237,9 @@ func (r RPCClient) GlossaryApprove(ctx context.Context, params backendrpc.Glossa
|
||||
func (r RPCClient) GlossaryDeprecate(ctx context.Context, params backendrpc.GlossaryReviewParams) (*backendrpc.GlossaryMutationReport, error) {
|
||||
return r.Client.GlossaryDeprecate(ctx, params)
|
||||
}
|
||||
func (r RPCClient) GlossaryDelete(ctx context.Context, params backendrpc.GlossaryDeleteParams) (*backendrpc.GlossaryMutationReport, error) {
|
||||
return r.Client.GlossaryDelete(ctx, params)
|
||||
}
|
||||
|
||||
func (r RPCClient) LocalizedStatus(ctx context.Context) (json.RawMessage, error) {
|
||||
return r.Client.LocalizedStatus(ctx)
|
||||
|
||||
Reference in New Issue
Block a user