Files
90CNServer/server/internal/platform/adminspec/openapi.go
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

552 lines
21 KiB
Go

package adminspec
import (
"encoding/json"
"sort"
)
type Spec struct {
OpenAPI string `json:"openapi"`
Info Info `json:"info"`
Servers []Server `json:"servers,omitempty"`
Tags []Tag `json:"tags,omitempty"`
Paths map[string]PathItem `json:"paths"`
Components Components `json:"components,omitempty"`
}
type Info struct {
Title string `json:"title"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
}
type Server struct {
URL string `json:"url"`
Description string `json:"description,omitempty"`
}
type Tag struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
type Components struct {
SecuritySchemes map[string]SecurityScheme `json:"securitySchemes,omitempty"`
Schemas map[string]Schema `json:"schemas,omitempty"`
}
type SecurityScheme struct {
Type string `json:"type"`
In string `json:"in,omitempty"`
Name string `json:"name,omitempty"`
}
type PathItem struct {
Get *Operation `json:"get,omitempty"`
Post *Operation `json:"post,omitempty"`
Delete *Operation `json:"delete,omitempty"`
}
type Operation struct {
OperationID string `json:"operationId,omitempty"`
Summary string `json:"summary"`
Description string `json:"description,omitempty"`
Tags []string `json:"tags,omitempty"`
Security []map[string][]any `json:"security,omitempty"`
RequestBody *RequestBody `json:"requestBody,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
Responses map[string]Response `json:"responses"`
}
type RequestBody struct {
Required bool `json:"required,omitempty"`
Content map[string]MediaType `json:"content"`
}
type MediaType struct {
Schema SchemaRef `json:"schema"`
}
type Parameter struct {
Name string `json:"name"`
In string `json:"in"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
Schema SchemaRef `json:"schema"`
}
type Response struct {
Description string `json:"description"`
Content map[string]MediaType `json:"content,omitempty"`
}
type SchemaRef struct {
Ref string `json:"$ref,omitempty"`
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Items *SchemaRef `json:"items,omitempty"`
Properties map[string]any `json:"properties,omitempty"`
}
type Schema struct {
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
AdditionalProperties bool `json:"additionalProperties,omitempty"`
Properties map[string]SchemaRef `json:"properties,omitempty"`
Items *SchemaRef `json:"items,omitempty"`
}
func Default() Spec {
spec := Spec{
OpenAPI: "3.0.3",
Info: Info{
Title: "LongHeng 龙恒 Admin API",
Version: "v1",
Description: "LongHeng 龙恒的 Admin 和 debug API 规范。项目模块可以扩展自己的运营接口。",
},
Servers: []Server{{URL: "http://127.0.0.1:18101", Description: "local gateway admin"}},
Tags: []Tag{
{Name: "admin", Description: "Admin console and OpenAPI document"},
{Name: "health", Description: "Health checks"},
{Name: "platform", Description: "Platform runtime snapshots"},
{Name: "gateway", Description: "Gateway operations"},
{Name: "logic", Description: "Logic service operations"},
{Name: "ops", Description: "Minimal operator backend workflows"},
{Name: "adminworkflow", Description: "Operator workflow records and rollback notes"},
{Name: "servergroup", Description: "Server group routing and merge evidence"},
{Name: "eventlog", Description: "Outbox and event log"},
{Name: "onlinepush", Description: "Online push, offline fallback, and push receipts"},
{Name: "accountcenter", Description: "Account center and GetGate control plane"},
{Name: "storageobject", Description: "Generic lightweight object storage"},
{Name: "moderation", Description: "Moderation runtime"},
{Name: "notice", Description: "Notice and announcement runtime"},
{Name: "redeem", Description: "Redeem code runtime"},
},
Paths: make(map[string]PathItem),
Components: Components{
SecuritySchemes: map[string]SecurityScheme{
"AdminToken": {Type: "apiKey", In: "header", Name: "X-Admin-Token"},
},
Schemas: defaultSchemas(),
},
}
addGET(&spec, "/healthz/live", "health", "Live check", false)
addGET(&spec, "/healthz/ready", "health", "Ready check", false)
addGET(&spec, "/healthz", "health", "Health snapshot", false)
addGET(&spec, "/admin", "admin", "Admin console", false)
addGET(&spec, "/admin/openapi.json", "admin", "OpenAPI document", false)
for _, path := range []string{
"/debug/config",
"/debug/metrics",
"/debug/rate_limit",
"/debug/discovery",
"/debug/rpc",
"/debug/topology",
"/debug/framework",
"/debug/adapters",
"/debug/session-contract",
"/debug/lifecycle",
"/debug/readiness",
"/debug/scheduler",
"/debug/traces",
"/debug/pvf",
"/debug/dungeon_runtime",
"/debug/datatables",
"/debug/datatable_views",
"/debug/i18n",
"/debug/audit",
"/debug/logiclog",
"/debug/bilog",
"/debug/log_level",
} {
addGET(&spec, path, "platform", summaryFromPath(path), true)
}
addGET(&spec, "/debug/reload", "platform", "Reload snapshot", true)
addPOST(&spec, "/debug/reload", "platform", "Trigger runtime reload", true, "")
addPOST(&spec, "/debug/log_level", "platform", "Change runtime log level", true, "")
for _, path := range []string{"/gateway/sessions", "/gateway/presence", "/gateway/backends", "/gateway/transports", "/gateway/protocol"} {
addGET(&spec, path, "gateway", summaryFromPath(path), true)
}
addPOST(&spec, "/gateway/push", "gateway", "Push message to gateway session or account", true, "GatewayPushRequest")
addPOST(&spec, "/gateway/session/kick", "gateway", "Kick gateway session", true, "GatewayKickRequest")
addPOST(&spec, "/gateway/drain", "gateway", "Enable or disable gateway drain", true, "GatewayDrainRequest")
addGET(&spec, "/eventlog", "eventlog", "EventLog snapshot", true)
addGET(&spec, "/eventlog/snapshot", "eventlog", "EventLog snapshot", true)
addGET(&spec, "/eventlog/deadletters", "eventlog", "List event dead letters", true)
addPOST(&spec, "/eventlog/deadletters/requeue", "eventlog", "Requeue dead letter", true, "")
addGET(&spec, "/onlinepush", "onlinepush", "Online push snapshot", true)
addPOST(&spec, "/onlinepush/send", "onlinepush", "Send online push with idempotent receipt", true, "OnlinePushRequest")
addGET(&spec, "/onlinepush/offline", "onlinepush", "List offline push messages", true)
addDELETE(&spec, "/onlinepush/offline", "onlinepush", "Delete offline push message", true)
addGET(&spec, "/accountcenter", "accountcenter", "Account center snapshot", true)
addGET(&spec, "/accountcenter/account", "accountcenter", "Query account by id or identity", true)
addPOST(&spec, "/accountcenter/login", "accountcenter", "Create login and GetGate result", true, "")
addPOST(&spec, "/accountcenter/bind", "accountcenter", "Bind identity to account", true, "")
addPOST(&spec, "/accountcenter/ban", "accountcenter", "Create or update account ban", true, "")
addPOST(&spec, "/accountcenter/allow", "accountcenter", "Create or update account allowlist", true, "")
addPOST(&spec, "/accountcenter/shards", "accountcenter", "Replace account center shard list", true, "")
addPOST(&spec, "/accountcenter/gates", "accountcenter", "Replace account center gate list", true, "")
addGET(&spec, "/storage/objects", "storageobject", "List storage objects", true)
addPOST(&spec, "/storage/objects/read", "storageobject", "Batch read storage objects", true, "StorageObjectBatchReadRequest")
addPOST(&spec, "/storage/objects/write", "storageobject", "Batch write storage objects", true, "StorageObjectBatchWriteRequest")
addPOST(&spec, "/storage/objects/delete", "storageobject", "Batch delete storage objects", true, "StorageObjectBatchDeleteRequest")
addGET(&spec, "/storage/object", "storageobject", "Read storage object", true)
addPOST(&spec, "/storage/object", "storageobject", "Write storage object", true, "")
addDELETE(&spec, "/storage/object", "storageobject", "Delete storage object", true)
addGET(&spec, "/debug/moderation", "moderation", "Moderation snapshot", true)
addGET(&spec, "/debug/moderation/snapshot", "moderation", "Moderation snapshot", true)
addPOST(&spec, "/debug/moderation/evaluate", "moderation", "Evaluate moderation request", true, "ModerationRequest")
addGET(&spec, "/debug/moderation/sanctions", "moderation", "List moderation sanctions", true)
addPOST(&spec, "/debug/moderation/sanctions", "moderation", "Upsert moderation sanction", true, "ModerationSanction")
addDELETE(&spec, "/debug/moderation/sanctions", "moderation", "Delete moderation sanction", true)
for _, path := range []string{
"/logic/players",
"/logic/accounts",
"/logic/playerstore",
"/logic/playerstore/deadletters",
"/logic/world",
"/logic/playerloops",
"/logic/idempotency",
"/logic/handlers",
"/logic/profiledb",
} {
addGET(&spec, path, "logic", summaryFromPath(path), true)
}
addPOST(&spec, "/logic/playerstore/deadletters/requeue", "logic", "Requeue player store dead letter", true, "")
addGET(&spec, "/logic/ops", "ops", "Operator workflow index", true)
addGET(&spec, "/logic/ops/players", "ops", "Query players and account summaries", true)
addGET(&spec, "/logic/ops/profile", "ops", "Read Profile without creating it", true)
addPOST(&spec, "/logic/ops/profile/repair", "ops", "Repair selected Profile fields", true, "ProfileRepairRequest")
addPOST(&spec, "/logic/ops/events/replay", "ops", "Replay an EventLog dead letter", true, "EventReplayRequest")
addGET(&spec, "/logic/ops/leaderboard", "ops", "Query leaderboard state", true)
addPOST(&spec, "/logic/ops/leaderboard/repair", "ops", "Repair leaderboard definition or records", true, "LeaderboardRepairRequest")
addGET(&spec, "/logic/ops/bans", "ops", "List bans and sanctions", true)
addPOST(&spec, "/logic/ops/bans", "ops", "Create or update ban", true, "OpsSanctionRequest")
addDELETE(&spec, "/logic/ops/bans", "ops", "Remove ban", true)
addGET(&spec, "/debug/admin/workflows", "adminworkflow", "List operator workflow records", true)
addGET(&spec, "/debug/admin/workflows/{id}", "adminworkflow", "Read operator workflow record", true)
addGET(&spec, "/debug/admin/workflows/{id}/rollback-note", "adminworkflow", "Read operator workflow rollback note", true)
addGET(&spec, "/debug/servergroup/merge/workflow/archives", "servergroup", "List merge workflow archives", true)
addGET(&spec, "/debug/servergroup/merge/workflow/archives/{archive_id}", "servergroup", "Read merge workflow archive", true)
addGET(&spec, "/debug/servergroup/merge/workflow/archives/{archive_id}/export", "servergroup", "Export merge workflow archive JSON", true)
addGET(&spec, "/logic/notice", "notice", "Notice runtime snapshot", true)
addGET(&spec, "/logic/notice/snapshot", "notice", "Notice runtime snapshot", true)
addPOST(&spec, "/logic/notice/publish", "notice", "Publish direct notice or announcement", true, "NoticePublishRequest")
addPOST(&spec, "/logic/notice/acknowledge", "notice", "Acknowledge notice delivery", true, "NoticeAcknowledgeRequest")
addGET(&spec, "/logic/notice/account", "notice", "List account visible notice deliveries", true)
addGET(&spec, "/logic/notice/announcements", "notice", "List active announcements", true)
addGET(&spec, "/logic/redeem", "redeem", "Redeem runtime snapshot", true)
addGET(&spec, "/logic/redeem/snapshot", "redeem", "Redeem runtime snapshot", true)
addPOST(&spec, "/logic/redeem/codes", "redeem", "Create or update redeem code", true, "RedeemCode")
addPOST(&spec, "/logic/redeem/claim", "redeem", "Repair or issue redeem claim", true, "RedeemClaimRequest")
return spec
}
func MarshalJSON(spec Spec, pretty bool) ([]byte, error) {
if pretty {
return json.MarshalIndent(spec, "", " ")
}
return json.Marshal(spec)
}
func SortedPaths(spec Spec) []string {
paths := make([]string, 0, len(spec.Paths))
for path := range spec.Paths {
paths = append(paths, path)
}
sort.Strings(paths)
return paths
}
func addGET(spec *Spec, path, tag, summary string, protected bool) {
item := spec.Paths[path]
item.Get = operation("get", path, tag, summary, protected, "")
spec.Paths[path] = item
}
func addPOST(spec *Spec, path, tag, summary string, protected bool, requestSchema string) {
item := spec.Paths[path]
item.Post = operation("post", path, tag, summary, protected, requestSchema)
spec.Paths[path] = item
}
func addDELETE(spec *Spec, path, tag, summary string, protected bool) {
item := spec.Paths[path]
item.Delete = operation("delete", path, tag, summary, protected, "")
item.Delete.Parameters = append(item.Delete.Parameters, Parameter{
Name: "id",
In: "query",
Description: "resource id",
Schema: SchemaRef{Type: "string"},
})
spec.Paths[path] = item
}
func operation(method, path, tag, summary string, protected bool, requestSchema string) *Operation {
op := &Operation{
OperationID: operationID(method, path),
Summary: summary,
Tags: []string{tag},
Responses: map[string]Response{
"200": {Description: "OK", Content: jsonObjectContent()},
"400": {Description: "Bad request", Content: jsonObjectContent()},
"401": {Description: "Unauthorized", Content: jsonObjectContent()},
"403": {Description: "Forbidden", Content: jsonObjectContent()},
"428": {Description: "Dangerous operation confirmation required", Content: jsonObjectContent()},
"503": {Description: "Service unavailable", Content: jsonObjectContent()},
},
}
if protected {
op.Security = []map[string][]any{{"AdminToken": []any{}}}
}
if protected && (method == "post" || method == "delete") {
op.Parameters = append(op.Parameters,
Parameter{
Name: "X-Admin-Actor",
In: "header",
Description: "operator or automation identity for audit logs",
Schema: SchemaRef{Type: "string"},
},
Parameter{
Name: "X-Admin-Trace-ID",
In: "header",
Description: "optional operation trace id",
Schema: SchemaRef{Type: "string"},
},
Parameter{
Name: "X-Admin-Confirm",
In: "header",
Description: "required for dangerous operations when admin.dangerous_confirm is enabled; value is the operation id such as POST /gateway/drain",
Schema: SchemaRef{Type: "string"},
},
)
}
if requestSchema != "" {
op.RequestBody = &RequestBody{
Required: true,
Content: map[string]MediaType{
"application/json": {Schema: SchemaRef{Ref: "#/components/schemas/" + requestSchema}},
},
}
}
return op
}
func jsonObjectContent() map[string]MediaType {
return map[string]MediaType{
"application/json": {Schema: SchemaRef{Type: "object"}},
}
}
func operationID(method, path string) string {
out := method
for _, r := range path {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') {
out += string(r)
continue
}
out += "_"
}
return out
}
func summaryFromPath(path string) string {
switch path {
case "/debug/config":
return "Redacted config snapshot"
case "/debug/metrics":
return "Metrics snapshot"
case "/debug/reload":
return "Config reload"
case "/debug/log_level":
return "Log level"
case "/debug/pvf":
return "PVF cache snapshot"
case "/debug/dungeon_runtime":
return "Dungeon runtime memory snapshot"
default:
return path
}
}
func defaultSchemas() map[string]Schema {
return map[string]Schema{
"GatewayPushRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"account_id": {Type: "string"},
"session_id": {Type: "string"},
"msg_id": {Type: "integer", Format: "int64"},
"body": {Type: "string"},
},
},
"GatewayKickRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"session_id": {Type: "string"},
"account_id": {Type: "string"},
"reason": {Type: "string"},
},
},
"GatewayDrainRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"enabled": {Type: "boolean"},
"reason": {Type: "string"},
"close_existing": {Type: "boolean"},
},
},
"OnlinePushRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"idempotency_key": {Type: "string"},
"account_id": {Type: "string"},
"account_ids": {Type: "array", Items: &SchemaRef{Type: "string"}},
"session_id": {Type: "string"},
"target_gateway_node_id": {Type: "string"},
"broadcast": {Type: "boolean"},
"packet_id": {Type: "integer", Format: "int32"},
"msg_id": {Type: "integer", Format: "int64"},
"wire_format": {Type: "string"},
"body": {Type: "string"},
"offline_policy": {Type: "string"},
"note": {Type: "string"},
},
},
"StorageObjectBatchReadRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"keys": {Type: "array", Items: &SchemaRef{Type: "object"}},
},
},
"StorageObjectBatchWriteRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"writes": {Type: "array", Items: &SchemaRef{Type: "object"}},
},
},
"StorageObjectBatchDeleteRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"deletes": {Type: "array", Items: &SchemaRef{Type: "object"}},
},
},
"ModerationRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"subject": {Type: "string"},
"scope": {Type: "string"},
"text": {Type: "string"},
},
},
"ModerationSanction": {
Type: "object",
Properties: map[string]SchemaRef{
"id": {Type: "string"},
"subject": {Type: "string"},
"scope": {Type: "string"},
"kind": {Type: "string"},
"reason": {Type: "string"},
"source": {Type: "string"},
"until": {Type: "string", Format: "date-time"},
},
},
"ProfileRepairRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"account_id": {Type: "string"},
"state": {Type: "string"},
"name": {Type: "string"},
"level": {Type: "integer", Format: "int32"},
"reason": {Type: "string"},
},
},
"EventReplayRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"id": {Type: "string"},
},
},
"LeaderboardRepairRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"action": {Type: "string"},
"leaderboard_id": {Type: "string"},
"owner_id": {Type: "string"},
"title": {Type: "string"},
"sort_order": {Type: "string"},
"operator": {Type: "string"},
"max_size": {Type: "integer", Format: "int32"},
"score": {Type: "integer", Format: "int64"},
"subscore": {Type: "integer", Format: "int64"},
},
},
"OpsSanctionRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"id": {Type: "string"},
"subject": {Type: "string"},
"scope": {Type: "string"},
"kind": {Type: "string"},
"reason": {Type: "string"},
"source": {Type: "string"},
"until": {Type: "string", Format: "date-time"},
"ttl_seconds": {Type: "integer", Format: "int64"},
},
},
"NoticePublishRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"notice": {Type: "object"},
"recipients": {Type: "array", Items: &SchemaRef{Type: "string"}},
"idempotency_key": {Type: "string"},
"meta": {Type: "object"},
"admin_command": {Type: "object"},
},
},
"NoticeAcknowledgeRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"delivery_id": {Type: "string"},
"notice_id": {Type: "string"},
"account_id": {Type: "string"},
"idempotency_key": {Type: "string"},
},
},
"RedeemCode": {
Type: "object",
Properties: map[string]SchemaRef{
"code": {Type: "string"},
"campaign": {Type: "string"},
"reward_ref": {Type: "string"},
"max_uses": {Type: "integer", Format: "int32"},
"per_account_limit": {Type: "integer", Format: "int32"},
"starts_at": {Type: "string", Format: "date-time"},
"ends_at": {Type: "string", Format: "date-time"},
"disabled": {Type: "boolean"},
"meta": {Type: "object"},
},
},
"RedeemClaimRequest": {
Type: "object",
Properties: map[string]SchemaRef{
"code": {Type: "string"},
"account_id": {Type: "string"},
"shard_id": {Type: "string"},
"source": {Type: "string"},
"idempotency_key": {Type: "string"},
"meta": {Type: "object"},
"admin_command": {Type: "object"},
},
},
}
}