- notification.go: containsAny 改用 strings.Contains 简化 - docs/openapi.go: buildOpenAPISpec 透传 ctx,消除 nil context - batch_writer.go: 缓冲区溢出丢弃记录时增加计数与告警日志 - billing/wallet.go: 删除无调用方的死代码 CheckBalance - tenant/open_platform.go: WebhookConfigUpdate 校验受影响行数 - tenant/member_import.go: 后台导入改用 WithoutCancel 保留 request_id - session.go: GetSessionByID/RevokeSession 增加 user_type 隔离, 防止 admin/tenant 跨控制台按 ID 吊销他人会话
349 lines
12 KiB
Go
349 lines
12 KiB
Go
package docs
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
|
|
v1 "github.com/qianfree/team-api/api/docs/v1"
|
|
"github.com/qianfree/team-api/internal/logic/common"
|
|
"github.com/qianfree/team-api/internal/service"
|
|
)
|
|
|
|
func init() {
|
|
service.RegisterDocs(New())
|
|
}
|
|
|
|
type sDocs struct{}
|
|
|
|
func New() *sDocs {
|
|
return &sDocs{}
|
|
}
|
|
|
|
func (s *sDocs) OpenAPISpec(ctx context.Context, _ *v1.OpenAPISpecReq) (json.RawMessage, error) {
|
|
// Try cache first
|
|
cached, err := g.Redis().Do(ctx, "GET", "docs:openapi:spec")
|
|
if err == nil && !cached.IsNil() && !cached.IsEmpty() {
|
|
return json.RawMessage(cached.String()), nil
|
|
}
|
|
|
|
spec := buildOpenAPISpec(ctx)
|
|
raw, err := json.Marshal(spec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Cache for 1 hour
|
|
_, _ = g.Redis().Do(ctx, "SET", "docs:openapi:spec", string(raw), "EX", 3600)
|
|
|
|
return raw, nil
|
|
}
|
|
|
|
func buildOpenAPISpec(ctx context.Context) map[string]any {
|
|
baseURL := common.Config().GetString(ctx, "api_base_url")
|
|
if baseURL == "" {
|
|
baseURL = "https://api.team-api.example.com"
|
|
}
|
|
return map[string]any{
|
|
"openapi": "3.0.3",
|
|
"info": map[string]any{
|
|
"title": "Team API",
|
|
"description": "多租户 大模型 API 网关,兼容 OpenAI API 格式",
|
|
"version": "1.0.0",
|
|
},
|
|
"servers": []map[string]any{
|
|
{"url": baseURL, "description": "Production"},
|
|
},
|
|
"paths": map[string]any{
|
|
"/v1/chat/completions": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Chat Completions",
|
|
"description": "创建聊天补全,兼容 OpenAI API 格式,支持流式和非流式响应。",
|
|
"operationId": "createChatCompletion",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{"$ref": "#/components/schemas/ChatCompletionRequest"},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{
|
|
"description": "Successful response",
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{"$ref": "#/components/schemas/ChatCompletionResponse"},
|
|
},
|
|
"text/event-stream": map[string]any{
|
|
"schema": map[string]any{"type": "string", "description": "SSE stream"},
|
|
},
|
|
},
|
|
},
|
|
"401": map[string]any{"description": "Authentication failed"},
|
|
"402": map[string]any{"description": "Insufficient quota"},
|
|
"429": map[string]any{"description": "Rate limit exceeded"},
|
|
"500": map[string]any{"description": "Internal error"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/completions": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Text Completions",
|
|
"description": "文本补全,兼容 OpenAI API 格式。",
|
|
"operationId": "createCompletion",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{"$ref": "#/components/schemas/CompletionRequest"},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/embeddings": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Embeddings",
|
|
"description": "创建文本向量嵌入,兼容 OpenAI API 格式。",
|
|
"operationId": "createEmbedding",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{"$ref": "#/components/schemas/EmbeddingRequest"},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/images/generations": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Image Generations",
|
|
"description": "AI 图像生成,兼容 OpenAI API 格式。",
|
|
"operationId": "createImage",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "prompt"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "dall-e-3"},
|
|
"prompt": map[string]any{"type": "string"},
|
|
"n": map[string]any{"type": "integer", "default": 1},
|
|
"size": map[string]any{"type": "string", "enum": []string{"256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/images/generations/async": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Image Generations Async (Submit)",
|
|
"description": "异步图像生成任务提交。阿里云百炼(DashScope)等异步图像上游无法经同步接口一次性返回,需提交任务拿到 task_id 后轮询 /v1/images/generations/async/{task_id} 取图。",
|
|
"operationId": "createImageAsync",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "prompt"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "wanx2.1-t2i-turbo"},
|
|
"prompt": map[string]any{"type": "string"},
|
|
"n": map[string]any{"type": "integer", "default": 1},
|
|
"size": map[string]any{"type": "string", "example": "1024x1024"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "任务已提交,返回 task_id 与初始状态"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/images/generations/async/{task_id}": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "Image Generations Async (Fetch)",
|
|
"description": "查询异步图像生成任务结果。轮询直到 status 为 SUCCESS(含图片 url)或 FAILURE(含 error),建议间隔 2~3 秒。",
|
|
"operationId": "getImageAsync",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"parameters": []map[string]any{
|
|
{
|
|
"name": "task_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "任务状态与结果"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/messages": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Claude Messages",
|
|
"description": "Claude Messages API,兼容 Anthropic API 格式。",
|
|
"operationId": "createClaudeMessage",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{
|
|
"schema": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "messages"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "claude-sonnet-4-20250514"},
|
|
"messages": map[string]any{"type": "array"},
|
|
"max_tokens": map[string]any{"type": "integer", "default": 4096},
|
|
"stream": map[string]any{"type": "boolean", "default": false},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/models": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "List Models",
|
|
"description": "获取可用模型列表。",
|
|
"operationId": "listModels",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
"/v1/models/{model_id}": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "Get Model",
|
|
"description": "获取模型详情。",
|
|
"operationId": "getModel",
|
|
"security": []map[string]any{{"BearerAuth": []any{}}},
|
|
"parameters": []map[string]any{
|
|
{"name": "model_id", "in": "path", "required": true, "schema": map[string]any{"type": "string"}},
|
|
},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{"description": "Successful response"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"components": map[string]any{
|
|
"securitySchemes": map[string]any{
|
|
"BearerAuth": map[string]any{
|
|
"type": "http",
|
|
"scheme": "bearer",
|
|
"bearerFormat": "API Key",
|
|
},
|
|
},
|
|
"schemas": map[string]any{
|
|
"ChatCompletionRequest": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "messages"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "gpt-4o", "description": "模型 ID"},
|
|
"messages": map[string]any{
|
|
"type": "array",
|
|
"items": map[string]any{"$ref": "#/components/schemas/ChatMessage"},
|
|
},
|
|
"temperature": map[string]any{"type": "number", "minimum": 0, "maximum": 2, "default": 1},
|
|
"top_p": map[string]any{"type": "number", "minimum": 0, "maximum": 1, "default": 1},
|
|
"max_tokens": map[string]any{"type": "integer", "minimum": 1},
|
|
"stream": map[string]any{"type": "boolean", "default": false},
|
|
"frequency_penalty": map[string]any{"type": "number", "minimum": -2, "maximum": 2},
|
|
"presence_penalty": map[string]any{"type": "number", "minimum": -2, "maximum": 2},
|
|
"stop": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
|
|
},
|
|
},
|
|
"ChatMessage": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"role", "content"},
|
|
"properties": map[string]any{
|
|
"role": map[string]any{"type": "string", "enum": []string{"system", "user", "assistant"}},
|
|
"content": map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
"ChatCompletionResponse": map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"id": map[string]any{"type": "string"},
|
|
"object": map[string]any{"type": "string"},
|
|
"created": map[string]any{"type": "integer"},
|
|
"model": map[string]any{"type": "string"},
|
|
"choices": map[string]any{
|
|
"type": "array",
|
|
"items": map[string]any{"$ref": "#/components/schemas/ChatChoice"},
|
|
},
|
|
"usage": map[string]any{"$ref": "#/components/schemas/Usage"},
|
|
},
|
|
},
|
|
"ChatChoice": map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"index": map[string]any{"type": "integer"},
|
|
"message": map[string]any{"$ref": "#/components/schemas/ChatMessage"},
|
|
"finish_reason": map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
"Usage": map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"prompt_tokens": map[string]any{"type": "integer"},
|
|
"completion_tokens": map[string]any{"type": "integer"},
|
|
"total_tokens": map[string]any{"type": "integer"},
|
|
},
|
|
},
|
|
"CompletionRequest": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "prompt"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "gpt-4o"},
|
|
"prompt": map[string]any{"type": "string"},
|
|
"max_tokens": map[string]any{"type": "integer", "default": 16},
|
|
"stream": map[string]any{"type": "boolean", "default": false},
|
|
},
|
|
},
|
|
"EmbeddingRequest": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"model", "input"},
|
|
"properties": map[string]any{
|
|
"model": map[string]any{"type": "string", "example": "text-embedding-3-small"},
|
|
"input": map[string]any{"oneOf": []any{
|
|
map[string]any{"type": "string"},
|
|
map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|