Skip to content
个人作品推荐
栾媛爱动物
播放动物叫声趣味微信小程序
栾媛爱动物微信小程序
微信扫码体验

usage_log 扩展:非 ext 目录源码侵入与合并标记

文档地图(usage_log 相关)

文档读它当你需要…
日志插件.md业务:扩展做什么、环境变量、附件、API、流程与限制。
插件开发.md机制:ext 包、目录树、新建插件、表初始化、联调、附录。
本文合并:主仓库(非 ext)须保留的代码块、i18n、go.mod 要点。
与上游-Gitee-手动合并步骤.mdGit:与 Gitee 无共同祖先时的 merge 命令与自检。

推荐顺序: 理解业务 → 日志插件;日常开发 ext/插件开发拉上游时若遇 unrelated historiesGitee 步骤,合并完再按本文恢复侵入点。


文档分工: 本文只解决「合并上游时要在哪些非 ext 文件里保留什么代码」。业务与配置以 日志插件.md 为准;通用扩展机制以 插件开发.md 为准。

历史上曾以基线提交 b94945e755aa1ef271cbac16e088c37538e5f6d1 为参照;合并时以本文〔合并段〕与当前仓库源码为准,不必拘泥于旧提交号。

说明ext/ext/table_init/web/src/ext/ 下的扩展实现不写入本文;仅记录「上游同名文件」中的侵入点。

源码标注(与仓库一致):主仓库与 web/src/ext/usage-logs/* 中侵入点使用如下注释包裹,合并时建议整段保留

  • Go// ==============================// 开始// 扩展:…、侵入代码、再 // 结束// ==============================(具体见各节代码块)。
  • JS/JSX:同一语义,使用 // … 或块注释 /* … */
  • locales/*.json:标准 JSON 不支持注释,扩展文案以本文 〔合并段〕 内说明与 key 表为准;勿在 JSON 内写 //

grep -E '开始|结束|扩展:' 在源码中快速定位。


非 ext 侵入文件一览(合并后逐项核对)

文件侵入内容摘要
main.goimport github.com/QuantumNous/new-api/ext;在 router.SetRouter 之前调用 ext.MustRegister(server)(见下节)。
router/relay-router.goimport ext;在 DecompressRequestMiddlewareBodyStorageCleanup 之后第一个 /v1 等 relay 子路由之前调用 ext.RegisterRelayMiddleware(router);其后仍有 StatsMiddleware 等(顺序见下节)。
middleware/auth.go提供 EnforceUserAuth(c *gin.Context) bool(附件下载路由未挂中间件时使用),见专节。
go.mod / go.sum须包含 github.com/xuri/excelize/v2(表格附件),见专节。
web/src/components/table/usage-logs/index.jsx按环境变量选用 UsageLogsTableWithContent 或默认 UsageLogsTable,见专节。
web/src/i18n/locales/zh-CN.json扩展 UI 所需 key(见 i18n 键表);至少维护 zh-CNen,其它语种按需同步。

不在此列表中的文件router/api-router.goweb/src/helpers/api.js无需为 usage_log 单独改路由或封装;扩展 API 由后端 ext.MustRegisterusage_log.RegisterRouter 注册 /api/ext/*,前端扩展组件内直接使用 API.get('/api/ext/...') 即可。


后端 Go

main.go

〔合并段 · 开始〕

  1. import
    与其它 github.com/QuantumNous/new-api/* 包放在同一 import 块即可,增加一行

    go
    "github.com/QuantumNous/new-api/ext"

    不要求单独拆成第二个 import (...);合并冲突时只要保证 ext 包被导入

  2. 调用时机
    router.SetRouter(server, buildFS, indexPage) 之前调用 ext.MustRegister(server),并用注释块标出(与仓库一致):

go
	// ==============================
	// 开始
	// 扩展:usage_log 等 Register(配置、迁移、/api/ext 路由);必须在 router.SetRouter 之前。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
	// ==============================
	ext.MustRegister(server)
	// ==============================
	// 结束
	// ==============================

	// 设置路由
	router.SetRouter(server, buildFS, indexPage)

顺序约束(与 Gin、扩展路由的关系)

  • cookie.NewStore + sessions.SessionsInjectUmamiAnalytics / InjectGoogleAnalytics 等为主干原有逻辑,不是 usage_log 专用「侵入块」;但必须保证 Session 已挂载ext.MustRegisterrouter.SetRouter 之前,以便扩展里 UserAuth / TokenOrUserAuth 能读会话。合并上游后若顺序被改,应恢复为:Session →(可选注入脚本)→ ext.MustRegisterSetRouter

〔合并段 · 结束〕


router/relay-router.go

〔合并段 · 开始〕

SetRelayRouter 内,relay 全局中间件须满足:

  1. import 增加 "github.com/QuantumNous/new-api/ext"(与其它 import 同块即可)。
  2. DecompressRequestMiddlewareBodyStorageCleanup 之后立刻调用 ext.RegisterRelayMiddleware(router)StatsMiddleware()注册 /v1/models 等子路由。

当前典型顺序如下(中间件名以上游为准,插入点在解压与 Body 清理之后、任何 router.Group("/v1"...) 之前):

go
	router.Use(middleware.DecompressRequestMiddleware())
	router.Use(middleware.BodyStorageCleanup()) // 清理请求体存储
	// ==============================
	// 开始
	// 扩展:usage_log 捕获 relay 请求体;须在解压与 BodyStorageCleanup 之后、/v1 等子路由注册之前。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
	// ==============================
	ext.RegisterRelayMiddleware(router)
	// ==============================
	// 结束
	// ==============================
	router.Use(middleware.StatsMiddleware())
	// … 随后 modelsRouter := router.Group("/v1/models") 等

为何必须卡在这一位置:Gin 在注册子路由之后再 Use 的中间件进不了已匹配子路由的请求链;捕获必须在第一个 relay 子路由注册前完成。若上游把 StatsMiddleware 挪到解压之前,须自行评估是否仍满足「解压后即可读 body」;一般保持本文插入点不变

〔合并段 · 结束〕


middleware/auth.go

〔合并段 · 说明〕

扩展 GET /api/ext/attachment/downloadext/usage_log/router_ext.go未挂 UserAuth / TokenOrUserAuth(链接型附件需先查库再决定是否 302,本地文件再在 handler 内鉴权)。本地文件分支依赖主仓提供的 EnforceUserAuth(c *gin.Context) bool:在未走全局中间件时,用会话Authorization: Bearer sk- 写入 idrole 等上下文;失败则写 JSON 并返回 false

  • 须保留func EnforceUserAuth(c *gin.Context) bool 及其完整实现(当前位于 middleware/auth.goTokenOrUserAuthTokenAuthReadOnly 之间)。合并上游后若编译报 undefined: middleware.EnforceUserAuth,说明该段被覆盖,需从本分支恢复。
  • ext/usage_log/resolve_user.go 的分工tryResolveUserIDFromSkBearer / tryResolveUserIDFromSession 仍供扩展内部仅解析 ID、不修改 middleware附件下载路径必须以 EnforceUserAuth 为准,与主干令牌解析习惯对齐。

〔合并段 · 结束〕


go.mod / go.sum

〔合并段 · 说明〕

ext/usage_log/attachment_xlsx.go 依赖 github.com/xuri/excelize/v2(解析 .xlsx / .xlsm 附件)。合并上游后若出现:

no required module provides package github.com/xuri/excelize/v2

须在模块根目录执行:

bash
go get github.com/xuri/excelize/v2
go mod tidy

并在 go.mod 中保留类似:

text
github.com/xuri/excelize/v2 v2.10.1

(具体版本号以 go get / go mod tidy 结果为准;可能连带升级 golang.org/x/* 等间接依赖。)

〔合并段 · 结束〕


前端(web)

web/src/components/table/usage-logs/index.jsx

〔合并段 · 开始〕

使用日志页默认改用带「问题 / 对话详情」列的扩展表;可通过 VITE_EXT_USAGE_LOG_CONTENT=false 回退为原 UsageLogsTable

jsx
// ==============================
// 开始
// 扩展:问题/回答列(usage_log_content),默认开启,设 VITE_EXT_USAGE_LOG_CONTENT=false 可关闭。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
// ==============================
import UsageLogsTableWithContent from '../../../ext/usage-logs/UsageLogsTableWithContent';
// ==============================
// 结束
// ==============================

全部 import 语句之后定义:

jsx
const LogsTableComponent =
  import.meta.env.VITE_EXT_USAGE_LOG_CONTENT !== 'false'
    ? UsageLogsTableWithContent
    : LogsTable;

表格处使用 <LogsTableComponent {...logsData} />不是直接写 <LogsTable {...} />,除非关闭扩展)。

〔合并段 · 结束〕

web/src/ext/usage-logs/*.jsx(扩展组件)

文件头部为 /* … 开始 … 扩展:… */,在 export default 前为 /* … 结束 … */,与主页面 // 块语义一致。扩展内请求 /api/ext/log-content/api/ext/attachment/download 等,无需helpers/api.js


web/src/i18n/locales/zh-CN.json(及 en.json 等需同步的语种)

〔合并段 · 开始〕

使用日志扩展 UI 文案(中文 key 与项目约定一致);其它语种在对应 locales/*.json 增加同 key 的翻译。

translation 对象中增加或保留(示例值与中文一致;英文见 en.json 翻译):

json
    "提示词、问题与回答": "提示词、问题与回答",
    "关联附件": "关联附件",
    "点击放大": "点击放大",
    "内嵌 Base64 图片": "内嵌 Base64 图片",
    "附件类型链接": "链接",
    "附件类型文件": "文件",
    "对话记录": "对话记录",
    "对话详情": "对话详情",
    "提示词": "提示词",
    "问题": "问题",
    "回答": "回答",
    "无扩展内容": "无扩展内容",
    "搜索无结果": "搜索无结果"

扩展组件实际调用的 key 与用途 等主干已有 key 若已存在则勿重复定义):

key用途
对话记录按钮与弹窗标题(LogContentCell
对话详情表格列标题(UsageLogsTableWithContent
问题「问题」列表头
提示词、问题与回答展开区标题
提示词 / 问题 / 回答 / 关联附件对话记录区块标题
无扩展内容无数据提示
PromptCell 无问题摘要(上游常见已有)
点击放大 / 内嵌 Base64 图片内嵌图与缩略图
附件类型链接 / 附件类型文件附件列表类型前缀
搜索无结果扩展表空状态(与全局用语一致时可依赖已有翻译)

〔合并段 · 结束〕


依赖与数据(备忘)

说明
github.com/xuri/excelize/v2ext/usage_log/attachment_xlsx.go 必需;见上文 go.mod / go.sum 节。
model.LOG_DB与扩展表读写的关系、运行前提等见 日志插件.md · 第 1 节
环境变量 / 表结构 / 附件一律见 日志插件.md 对应章节与 ext/README.md;表可由 AutoMigrateext/table_init 初始化(亦见 插件开发.md · 第 5 节)。

合并工作流与自检

  1. 与 Gitee 无共同祖先时:先按 与上游-Gitee-手动合并步骤.md 完成 merge 与依赖整理;-X theirs 可能覆盖主仓定制,合并后必须按本文恢复侵入点。
  2. 逐文件恢复:顺序建议 main.gorouter/relay-router.gomiddleware/auth.gogo.mod/go.sum → 前端 index.jsx → i18n;细节以本文 〔合并段〕 为准(与 插件开发 · 第 8 节 清单一致)。
  3. ext/:随本仓库维护;冲突以 ext/ 目录内为准,一般不需按上游覆盖。
  4. 合并后自检(仓库根目录):
bash
go build .
rg 'ext\.MustRegister|ext\.RegisterRelayMiddleware' main.go router/relay-router.go
rg 'EnforceUserAuth' middleware/auth.go
rg 'excelize' go.mod