diff --git a/internal/logic/admin/help_center.go b/internal/logic/admin/help_center.go index e9dcc14..f940437 100644 --- a/internal/logic/admin/help_center.go +++ b/internal/logic/admin/help_center.go @@ -288,6 +288,11 @@ func (s *sAdmin) UpdateHelpArticle(ctx context.Context, req *v1.HelpArticleUpdat s.refreshCategoryArticleCount(ctx, req.CategoryId) } + // 状态变更(草稿↔已发布)也需要刷新计数 + if article.Status != req.Status { + s.refreshCategoryArticleCount(ctx, req.CategoryId) + } + return &v1.HelpArticleUpdateRes{}, nil } @@ -364,6 +369,8 @@ func (s *sAdmin) refreshCategoryArticleCount(ctx context.Context, categoryId int count, err := dao.SptArticles.Ctx(ctx). Where("category_id", categoryId). Where("status", "published"). + Where("published_at IS NOT NULL"). + Where("published_at <=", gtime.Now()). Count() if err != nil { g.Log().Warningf(ctx, "refreshCategoryArticleCount count failed for category %d: %v", categoryId, err) diff --git a/internal/logic/common/help_center.go b/internal/logic/common/help_center.go index b50de41..cd38bfa 100644 --- a/internal/logic/common/help_center.go +++ b/internal/logic/common/help_center.go @@ -26,14 +26,13 @@ type HelpPublicCategoryItem struct { // ListPublicCategories 返回可见分类的树结构 func ListPublicCategories(ctx context.Context) ([]*HelpPublicCategoryItem, error) { type categoryRow struct { - Id int64 `json:"id" orm:"id"` - ParentId int64 `json:"parent_id" orm:"parent_id"` - Name string `json:"name" orm:"name"` - Slug string `json:"slug" orm:"slug"` - Description string `json:"description" orm:"description"` - Icon string `json:"icon" orm:"icon"` - IsVisible bool `json:"is_visible" orm:"is_visible"` - ArticleCount int `json:"article_count" orm:"article_count"` + Id int64 `json:"id" orm:"id"` + ParentId int64 `json:"parent_id" orm:"parent_id"` + Name string `json:"name" orm:"name"` + Slug string `json:"slug" orm:"slug"` + Description string `json:"description" orm:"description"` + Icon string `json:"icon" orm:"icon"` + IsVisible bool `json:"is_visible" orm:"is_visible"` } var rows []categoryRow @@ -45,6 +44,27 @@ func ListPublicCategories(ctx context.Context) ([]*HelpPublicCategoryItem, error return nil, err } + // 实时统计每个分类的已发布文章数(不依赖冗余字段 article_count) + type countRow struct { + CategoryId int64 `json:"category_id" orm:"category_id"` + ArticleCount int `json:"count" orm:"count"` + } + var countRows []countRow + err = dao.SptArticles.Ctx(ctx). + Where("status", "published"). + Where("published_at IS NOT NULL"). + Where("published_at <=", gtime.Now()). + Fields("category_id, COUNT(*) as count"). + Group("category_id"). + Scan(&countRows) + if err != nil { + return nil, err + } + countMap := make(map[int64]int, len(countRows)) + for _, cr := range countRows { + countMap[cr.CategoryId] = cr.ArticleCount + } + // 构建所有节点 nodeMap := make(map[int64]*HelpPublicCategoryItem) for _, r := range rows { @@ -55,7 +75,7 @@ func ListPublicCategories(ctx context.Context) ([]*HelpPublicCategoryItem, error Slug: r.Slug, Description: r.Description, Icon: r.Icon, - ArticleCount: r.ArticleCount, + ArticleCount: countMap[r.Id], Children: make([]*HelpPublicCategoryItem, 0), } } diff --git a/web/tenant/src/views/service/DocsPage.vue b/web/tenant/src/views/service/DocsPage.vue index 0dd4361..62a19cb 100644 --- a/web/tenant/src/views/service/DocsPage.vue +++ b/web/tenant/src/views/service/DocsPage.vue @@ -1,17 +1,22 @@ - - - - - API 文档 - 集成 Team API 所需的一切 - - - - 在线测试 - - - - - - - {{ section.label }} - - - - - - - - - 1 - 获取 API Key - - - - 在控制台中进入 API 密钥 页面创建新密钥。每个密钥可限定特定模型并配置速率限制。请妥善保管您的密钥——不要分享或提交到版本控制中。 - + + + + + + + API 文档 + 集成 Team API 所需的一切 — 兼容 OpenAI / Claude / Gemini 格式 + + + 在线测试 + - - - - 2 - 发起第一次请求 - - - - Team API 完全兼容 OpenAI API 格式。只需将 base_url 改为我们的端点并使用您的 API Key 即可: - + + + + {{ section.label }} + + - - - - - {{ tabLabels[tab] }} + + + + + + 1 + 获取 API Key + + + + 在控制台中进入 API 密钥 页面创建新密钥。每个密钥可限定特定模型并配置速率限制。请妥善保管您的密钥——不要分享或提交到版本控制中。 + + + + + + + + 2 + 发起第一次请求 + + + + Team API 完全兼容 OpenAI API 格式。只需将 base_url 改为我们的端点并使用您的 API Key 即可: + + + + + + + {{ tabLabels[tab] }} + + + + {{ copiedTab === activeCodeTab ? '已复制' : '复制' }} + + + {{ getExampleCode(activeCodeTab) }} + + + + + + + + 3 + 探索与测试 + + + + Team API 通过统一接口提供 40+ 个 AI 模型的访问,同时兼容 OpenAI、Claude、Gemini 三大 API 格式。您可以使用 Playground 在线调试不同参数组合,或通过 /v1/models 接口查询完整模型列表。 + + + + + 打开 Playground - - - {{ copiedTab === activeCodeTab ? '已复制' : '复制' }} + + 查看 API 参考 - {{ getExampleCode(activeCodeTab) }} + + + + + + + 兼容性说明 + + Team API 设计为与主流大模型 API 完全兼容,您可以直接使用各语言官方 SDK: + + + + OpenAI 兼容 + Chat / Embeddings / Images / Audio + base_url = ".../v1" + + + Claude 兼容 + Anthropic Messages API + base_url = ".../v1" + + + Gemini 兼容 + Google GenerateContent API + 端点: /v1beta/models/... + + - - - - 3 - 探索与测试 + + + + + + {{ cat.label }} + + + + + + {{ ep.method }} + {{ ep.path }} + + {{ ep.title }} + {{ ep.desc }} + + + + + + + 请求参数 + + + + + 字段 + 类型 + 必填 + 说明 + + + + + {{ p.field }} + {{ p.type }} + + 必填 + 可选 + + {{ p.desc }} + + + + + + + + + + + + 所有请求需携带 Authorization: Bearer sk-your-api-key 头 + + + + + + 请求示例 + + {{ copiedBlock === 'req-' + ep.id ? '✓ 已复制' : '复制' }} + + + {{ ep.example.req }} + + + + + + 响应示例 + + {{ copiedBlock === 'resp-' + ep.id ? '✓ 已复制' : '复制' }} + + + {{ ep.example.resp }} + + + + + + + + 在 Playground 中测试 + + + + + - - - Team API 通过统一接口提供 40+ 个 AI 模型的访问。您可以使用 Playground 在线调试不同参数组合,或通过 /v1/models 接口查询完整模型列表。 - - - - - 打开 Playground - - - 查看 API 参考 + + + + + Team API 兼容 OpenAI / Anthropic SDK,只需修改 base_url 即可无缝切换。 + + + + {{ ex.title }} + {{ ex.lang }} + + + + {{ copiedExample === idx ? '已复制' : '复制' }} + {{ ex.code }} + + + + + + + API 返回两种错误格式:AI 代理端点(/v1/*)使用 OpenAI 兼容格式;管理类端点(/api/*)使用统一响应格式。 + + + + + + + AI 代理端点错误格式 + /v1/* 端点使用 OpenAI 兼容错误格式 + + + {{ relayErrorResponse }} + + {{ copiedBlock === 'relay-err' ? '✓' : '复制' }} + + + + + + 管理类端点错误格式 + /api/* 端点使用统一响应格式 + + + {{ mgmtErrorResponse }} + + {{ copiedBlock === 'mgmt-err' ? '✓' : '复制' }} + + + + + + + + + 错误码一览 + + + + + + + HTTP 状态码 + 业务错误码 + 说明 + 示例消息 + + + + + + {{ err.status }} + + {{ err.code }} + {{ err.desc }} + {{ err.example }} + + + + + - - - - - - {{ ep.method }} - {{ ep.path }} - - {{ ep.title }} - {{ ep.desc }} - - - 请求参数 - - - - - 字段 - 类型 - 必填 - 说明 - - - - - {{ p.field }} - {{ p.type }} - - 必填 - 可选 - - {{ p.desc }} - - - - - - - - 在 Playground 中测试 - - + + - - - - - - - {{ ex.title }} - {{ ex.lang }} - - - - {{ copiedExample === idx ? '已复制' : '复制' }} - - - {{ ex.code }} - - +
集成 Team API 所需的一切
- 在控制台中进入 API 密钥 页面创建新密钥。每个密钥可限定特定模型并配置速率限制。请妥善保管您的密钥——不要分享或提交到版本控制中。 -
集成 Team API 所需的一切 — 兼容 OpenAI / Claude / Gemini 格式
- Team API 完全兼容 OpenAI API 格式。只需将 base_url 改为我们的端点并使用您的 API Key 即可: -
base_url
+ 在控制台中进入 API 密钥 页面创建新密钥。每个密钥可限定特定模型并配置速率限制。请妥善保管您的密钥——不要分享或提交到版本控制中。 +
+ Team API 完全兼容 OpenAI API 格式。只需将 base_url 改为我们的端点并使用您的 API Key 即可: +
{{ getExampleCode(activeCodeTab) }}
+ Team API 通过统一接口提供 40+ 个 AI 模型的访问,同时兼容 OpenAI、Claude、Gemini 三大 API 格式。您可以使用 Playground 在线调试不同参数组合,或通过 /v1/models 接口查询完整模型列表。 +
/v1/models
+ Team API 设计为与主流大模型 API 完全兼容,您可以直接使用各语言官方 SDK: +
base_url = ".../v1"
端点: /v1beta/models/...
{{ ep.path }}
{{ ep.desc }}
Authorization: Bearer sk-your-api-key
{{ ep.example.req }}
{{ ep.example.resp }}
- Team API 通过统一接口提供 40+ 个 AI 模型的访问。您可以使用 Playground 在线调试不同参数组合,或通过 /v1/models 接口查询完整模型列表。 -
Team API 兼容 OpenAI / Anthropic SDK,只需修改 base_url 即可无缝切换。
{{ ex.code }}
+ API 返回两种错误格式:AI 代理端点(/v1/*)使用 OpenAI 兼容格式;管理类端点(/api/*)使用统一响应格式。 +
/v1/*
/api/*
/v1/* 端点使用 OpenAI 兼容错误格式
{{ relayErrorResponse }}
/api/* 端点使用统一响应格式
{{ mgmtErrorResponse }}