Commit Graph

134 Commits

Author SHA1 Message Date
hhs
e0dc8272a5 feat: 实现并发安全的 ULID trace ID 生成器 2026-06-21 21:59:43 +08:00
hhs
11c3955bd6 feat: 添加 ULID 依赖用于 trace ID 生成 2026-06-21 21:59:16 +08:00
hhs
51117b43f6 fix: 修复 handler_test.go 中 ServeWS 缺少参数 2026-06-21 18:44:03 +08:00
hhs
492fb06c08 merge: 合并 feat/tokentime 到 develop,解决 limiter/scenarioRepo 参数冲突 2026-06-21 17:42:48 +08:00
1079e22699 feat: 实现自建情景功能
## 功能概述
- 用户可创建、编辑、删除自定义情景
- 支持自定义情景名称、图标、描述、Prompt、首句引导
- 完整的权限隔离,用户只能管理自己的情景
- 深度集成 Eino 框架,动态加载自建情景 Prompt

## 后端实现
### 数据库
- 新增 user_scenarios 表
- 支持用户配额(最多 20 个)
- 字段验证:description 可选,prompt 最小 10 字符

### API
- GET /api/scenarios - 获取用户情景列表
- POST /api/scenarios - 创建情景
- GET /api/scenarios/:id - 获取详情
- PATCH /api/scenarios/:id - 更新情景
- DELETE /api/scenarios/:id - 删除情景

### Eino 集成
- PipelineState 添加 UserID 字段
- nodes_history 动态加载用户自建情景
- GetScenarioPrompt 支持自建情景优先级

## 前端实现
### 组件
- CreateScenarioModal - 创建情景对话框
- EditScenarioModal - 编辑情景对话框
- ConfigPanel 改造 - 分组显示系统预置和自建情景

### Hook
- useScenarios - 合并系统和自建情景,提供 CRUD 接口

### 国际化
- 中文、英文、日文翻译支持

## 问题修复
- 修复 CORS 问题:使用 Vite 代理
- 统一验证规则:description 可选,prompt 最小 10 字符
- 修复数据库约束:使用 NULLIF 处理空字符串

## 文件变更
新增文件: 13 个
修改文件: 14 个

详见文档: docs/自建情景功能完整文档.md
2026-06-21 15:38:28 +08:00
hhs
032de796c8 docs: 重构文档结构,规范编号并整合冗余内容
## 主要变更

### 文档重构(减少 1199 行,-23%)
- 01-架构设计.md: 503→369 行 (-27%),删除 DDL/配置示例,精简鉴权/存储描述
- 02-接口文档.md: 1313→570 行 (-57%),删除 Go 接口/Orchestrator 实现/配置管理
- 07-成本控制.md: 65→59 行 (-9%),代码块替换为文件引用

### 文档编号规范化
- 08-功能创意.md → 删除(内容整合到 README.md "功能扩展方向")
- 10-Eino框架与编排设计.md → 08-Eino框架与编排设计.md
- 情景切换.md → 09-情景切换.md
- 12-鉴权体系.md → 10-鉴权体系.md
- 13-令牌桶限流.md → 11-令牌桶限流.md

### 交叉引用更新
- 01-架构设计.md: 更新对鉴权体系/令牌桶限流的引用为新编号
- README.md: 更新文档索引表、推荐阅读顺序、新增功能扩展方向

### 删除过时文档
- 09-技术名词解释.md(内容已整合到 03-技术选型.md)
- 10-Eino重构方案.md(历史记录,已完成)
- 11-Eino框架技术文档.md(已合并到 08)
- 情景切换功能完整文档.md(已规范化为 09)

## 重构原则
- 架构文档聚焦系统结构,移除实现细节
- 接口文档保留纯契约,删除内部实现
- 编号连续(01-11),语义清晰
- 通过交叉引用连接相关文档,避免重复
2026-06-21 14:48:03 +08:00
hhs
99fcd6bc29 fix: 修复 Dockerfile 配置文件路径 2026-06-21 13:27:29 +08:00
hhs
d53265755a refactor: 移除生产环境配置文件中 CORS 配置 2026-06-21 12:39:33 +08:00
hhs
252cdcc8e7 refactor: 开发环境使用与生产环境相同的 AI 模型配置
- config.dev.yaml 补全 AI 服务配置(provider、model、endpoint)
- 保持开发环境超时时间较长(方便调试)
- 确保开发和生产环境使用相同的模型,避免环境差异导致的问题
2026-06-21 12:24:20 +08:00
hhs
515d7ae034 docs: 完善配置环境切换机制
- docker-compose.yml 添加 APP_ENV=prod 强制生产环境
- 更新 .env.example 的 APP_ENV 注释说明(开发/生产差异)
- CLAUDE.md 补充"配置环境切换"章节,说明本地开发和生产部署的配置切换方式
- 明确配置优先级:环境变量 > config.{env}.yaml > config.yaml > 默认值

配置差异:
- 开发环境:debug 日志、关闭限流、允许所有 CORS
- 生产环境:info/json 日志、启用限流、严格 CORS 白名单

验证通过:
- 本地开发:默认加载 config.dev.yaml(env=dev, debug 日志)
- 生产配置:APP_ENV=prod 加载 config.prod.yaml(env=prod, info 日志)
2026-06-21 01:14:29 +08:00
hhs
311330cea1 refactor: 重组配置文件到 config 目录
- 创建 backend/config/ 目录统一管理配置文件
- 移动 config.yaml 到 config/config.yaml
- 新增 config.dev.yaml 开发环境配置(debug 日志、关闭限流)
- 新增 config.prod.yaml 生产环境配置(info 日志、启用限流、严格 CORS)
- 更新配置加载逻辑,优先从 config/ 目录读取,兼容旧路径
- 更新 .gitignore,仅排除 .env,配置文件纳入版本控制
2026-06-21 01:07:47 +08:00
hhs
7adf81c6e5 feat: 集成限流器到服务
- main.go 初始化限流器(根据 Redis 可用性选择内存/Redis 实现)
- WebSocket handler 添加 query 消息限流(按 userID)
- Auth API 添加登录/注册限流(按 IP)
- refresh 和 logout 不限流(避免影响正常用户操作)
- 修复所有测试(传递 nil limiter 参数)
- 所有测试通过(包括 ws 和 api 集成测试)
2026-06-21 00:00:24 +08:00
hhs
ea00939c13 feat: 实现 Gin 限流中间件
- Middleware 函数返回 Gin 中间件
- keyFunc 参数支持灵活提取限流 key(IP/用户 ID 等)
- 限流触发时返回 HTTP 429 + Retry-After header
- 支持 nil limiter(跳过限流)和空 key(跳过限流)
- 完整单元测试(6 个测试用例,全部通过)
- 测试覆盖:允许、拒绝、nil limiter、空 key、keyFunc、Retry-After 舍入
2026-06-20 23:56:44 +08:00
hhs
b74fb3564d feat: 实现 Redis 令牌桶限流器
- RedisLimiter 基于 Lua 脚本保证原子性
- Lua 脚本实现完整令牌桶算法(填充、消耗、TTL)
- fail-open 策略:Redis 故障时允许请求通过
- FormatKey 辅助函数格式化限流 key
- 完整单元测试(10 个测试用例,使用 miniredis)
- 测试覆盖:首次请求、耗尽、不同用户、补充、容量上限、零速率、TTL、故障降级
2026-06-20 23:55:37 +08:00
hhs
3b6226394b feat: 实现内存令牌桶限流器
- Limiter 接口定义(Allow + Stop 方法)
- TokenBucket 实现(容量、填充速率、并发安全)
- MemoryLimiter 管理多用户令牌桶
- 后台 goroutine 定期清理不活跃桶(10 分钟)
- 完整单元测试覆盖(11 个测试用例,全部通过)
- 边界情况处理(rate=0、capacity=0、并发安全)
2026-06-20 23:53:36 +08:00
hhs
a6df8c9131 feat: 添加限流配置层
- Config 结构体新增 RateLimit 字段
- 新增 RateLimitConfig 和 BucketConfig 结构体定义
- config.yaml 新增 ratelimit 配置段(默认关闭)
- 设置默认值:query(10/0.2)、login(5/0.1)、register(3/0.05)
2026-06-20 23:51:45 +08:00
2fb23852ef feat: 优化情景切换功能 2026-06-20 23:24:32 +08:00
9ba4eb4825 Merge pull request 'feat: 优化对话历史功能、增加首页登录页面' (#155) from feat/historychat into develop
Reviewed-on: http://8.161.227.145:3000/XEngineers/CamTalk/pulls/155
2026-06-20 20:06:39 +08:00
ab07e01adf feat: 优化对话历史功能 2026-06-20 19:57:36 +08:00
hhs
12e2d24f37 fix: checkout 后显式 git fetch 切换触发分支,避免始终部署 main 代码;Dockerfile 添加 BuildKit cache mount 加速构建 2026-06-20 17:43:32 +08:00
hhs
ea70d2efc6 feat: main.go 注入 Redis 到 CachedUserRepository,启用 refresh token 二级缓存
- 将 rdb 变量提升到外层作用域,供 session 和 auth 共用
- Redis 启用时用 CachedUserRepository 包装 userRepo
- backfillTTL 使用 cfg.Auth.RefreshTTL 与 token 实际过期时间一致
2026-06-20 14:54:08 +08:00
hhs
a2a28a9f56 feat: CachedUserRepository — refresh token 的 Redis 缓存装饰器
- 装饰 UserRepository,仅缓存 refresh token 相关操作
- SaveRefreshToken: Write-Through,先写 DB 再写 Redis(SET + SADD)
- FindRefreshToken: Read-Through,Redis miss 时查 DB 并回填
- DeleteRefreshToken: 双删 DB + Redis
- DeleteUserRefreshTokens: 通过 Redis Set 批量清理缓存后删 DB
- Redis 操作失败时降级到纯 DB,不阻断主流程
2026-06-20 14:53:10 +08:00
hhs
898e30b526 feat: Refresh Token 复用检测 — 检测到已 rotation 的 token 被复用时吊销用户全部 refresh token
- Refresh 方法中 FindRefreshToken 返回 not found 时,检查 JWT 是否有效
- JWT 有效但 DB 不存在 → 判定为复用,调用 DeleteUserRefreshTokens 吊销该用户所有 token
- 增加 TestRefresh_ReuseDetectedRevokesAllTokens 测试覆盖复用场景
2026-06-20 14:51:34 +08:00
hhs
87c54b80c0 feat: JWT Claims 增加 TokenType 字段,ValidateAccess/ValidateRefresh 区分校验类型
- Claims 新增 TokenType 字段("access" / "refresh")
- GeneratePair 为 access/refresh token 分别设置 token_type
- ValidateAccess 校验后检查 token_type == "access"
- ValidateRefresh 校验后检查 token_type == "refresh"
- 增加 token 类型交叉校验测试
2026-06-20 14:50:19 +08:00
hhs
d53de4f33f fix: CI/CD 部署固定到 /root/camtalk 目录,确保 compose 能正确管理容器生命周期 2026-06-20 13:33:05 +08:00
hhs
b8fd6a9330 fix: 修复 TieredManager 创建 session 时 L1/L2 ID 不一致导致 Redis 写入失败的问题 2026-06-20 11:54:21 +08:00
898d1474ec Merge pull request 'feat: 重构用eino框架' (#128) from fea/newcode into develop
Reviewed-on: http://8.161.227.145:3000/XEngineers/CamTalk/pulls/128
2026-06-19 23:28:55 +08:00
eb1b90445f fix: 修复 Eino Graph 类型不匹配和多模态消息问题
- 添加 msg2str 转换节点解决 ChatModel 输出 *schema.Message 与 Splitter 期望 string 的类型不匹配
- 将多模态图片内容从 system 消息移到 user 消息(DashScope API 仅支持 user/tool 角色的多模态内容)
- 修复 Content 和 UserInputMultiContent 不能同时设置的问题
- Splitter 输出改为 StreamReader[string](单句),TTS 改为 TransformableLambda 流式消费
- 修复 .env 中 PostgreSQL DSN 和 Redis ADDR 的 http:// 前缀问题
2026-06-19 23:24:01 +08:00
hhs
e5537aaa1e fix: 修复 Dockerfile 运行阶段缺少 config.yaml 导致容器启动 panic 2026-06-19 23:23:30 +08:00
hhs
765cb34019 feat: 添加 Redis Docker 容器编排及三级存储架构
- docker-compose.yml 新增 Redis 服务及 camtalk-net 桥接网络
- 实现 TieredManager 三级存储(L1 内存 → L2 Redis → L3 PostgreSQL)
- config.go 新增 Redis/Persistence 配置类型及环境变量绑定
- 修复 CAMTALK_REDIS_ADDR 环境变量未被 Viper 绑定的问题
- .env.example 更新为三级存储配置并标注开发/部署地址差异
2026-06-19 22:53:06 +08:00
4ffd84510e refactor: 清理旧编排代码,新增 eino 包单元测试
删除旧代码:
- orchestrator/pipeline.go: 旧 STT→LLM→TTS 手写 goroutine 管道
- orchestrator/splitter.go: 旧句子切分器
- orchestrator/pipeline_test.go: 旧 Pipeline 测试
- ai/llm/openai.go: 旧 LLM OpenAI 实现(被 eino-ext ChatModel 替代)
- ai/llm/openai_test.go: 旧 LLM 测试

保留的接口和工具:
- orchestrator/orchestrator.go: Orchestrator 接口(ws/handler 依赖)
- orchestrator/sender.go: Sender 接口(eino/callback 依赖)
- ai/llm/llm.go: Request/Chunk/TokenUsage 类型定义
- ai/llm/prompt.go: BuildSystemPrompt(eino/nodes_history 依赖)
- ai/llm/scenarios.go: GetScenarioPrompt(eino/nodes_history 依赖)

新增测试:
- eino/graph_test.go: 13 个测试覆盖类型构建、State 并发安全、
  Context 注入、延迟计算、接口实现检查等

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 22:04:39 +08:00
4b731b5ac0 feat: 实现 Eino Graph 构建与 Orchestrator 适配器,切换 main.go
- graph.go: 构建 Graph 拓扑 START→STT→History→ChatModel→Splitter→TTS→Done→END
  - 创建 eino-ext ChatModel 对接 DashScope OpenAI 兼容接口
  - 统一使用值类型(PipelineInput/PipelineOutput)
  - Callback 在运行时通过 Stream option 传入
- adapter.go: EinoOrchestrator 实现 orchestrator.Orchestrator 接口
  - 解码 base64 音频/图片,注入 context 值
  - 调用 Graph.Stream() 触发惰性执行并消费输出
  - 追加用户/助手消息到历史
- main.go: 移除旧 llmService + orchestrator.New()
  替换为 eino.NewPipelineGraph() + eino.NewEinoOrchestrator()
- 各节点统一使用值类型,State 传递请求元数据

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 21:58:17 +08:00
fd5c7712f8 feat: 引入 Eino 框架并实现 AI 编排层基础设施与节点
- 引入 cloudwego/eino v0.9.9 和 eino-ext/components/model/openai v0.1.13
- 新增 internal/eino/ 包:
  - types.go: PipelineInput/Output、STTOutput、TokenUsage 类型定义
  - state.go: PipelineState 跨节点状态收集(线程安全)
  - callback.go: ChatModel OnEndWithStreamOutput 回调,逐 token 推送 llm_chunk
  - nodes_stt.go: STT Lambda,支持文本/语音输入模式
  - nodes_history.go: 历史组装 Lambda,含多模态图片支持
  - nodes_splitter.go: 句子分割 Transform Lambda
  - nodes_tts.go: TTS Lambda,逐句合成推送音频
  - nodes_done.go: Done Lambda,发送 llm_done 并追加历史

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 21:49:28 +08:00
hhs
c0b4eeda46 refactor: 统一配置文件系统
- 补全 backend/config.yaml 所有非敏感配置项并添加中文注释
- 重写 config.go:Load(workDir) 显式传参,BindEnv 绑定敏感字段,删除 AutomaticEnv
- setDefaults 默认值与 config.yaml 保持一致(mimo/dashscope)
- backend/.env.example 重写为纯敏感信息模板
- .env 固定在 /opt/camtalk/.env,docker-compose 通过绝对路径加载
- deploy.sh 统一使用 --env-file,移除硬编码 IP
- deploy.yml 删除 CI 写入 .env 的步骤
- Dockerfile 移除 COPY config.yaml
- 修复 deploy.yml 中 POSTGRES_PASSWORD 的 &{{ 拼写错误
2026-06-19 18:41:35 +08:00
6db5bef519 Merge pull request 'fix: 修复 Docker Compose 环境下 PostgreSQL 连接失败' (#114) from fix/actions into develop
Reviewed-on: http://8.161.227.145:3000/XEngineers/CamTalk/pulls/114
2026-06-14 20:48:31 +08:00
hhs
1ddd75d34f fix: 修复 Docker Compose 环境下 PostgreSQL 连接失败 2026-06-14 20:47:00 +08:00
b60e513317 feat:添加对话情景功能 2026-06-14 20:32:30 +08:00
c3118dd72a feat:语音对话优化 2026-06-14 19:54:22 +08:00
hhs
966b30218a feat: 实现 sessions 持久化,支持会话恢复 2026-06-14 18:54:18 +08:00
hhs
542720695d feat: 对接 PostgreSQL 存储层 2026-06-14 18:40:37 +08:00
hhs
c3a32ce276 feat: Phase 8.5 — ConversationSummary 查询优化,支持 SQL 聚合获取消息统计 2026-06-14 17:58:42 +08:00
hhs
7b745018c5 feat: Phase 8.4 — 实现 LoadSession 和 LoadSessionFromDB,支持从 PostgreSQL 恢复会话到内存 2026-06-14 17:56:33 +08:00
hhs
f4515ce5e4 feat: Phase 8.3 — Session Manager 注入 MessageRepository,AppendMessage 启用 Write-Through 2026-06-14 17:54:33 +08:00
hhs
96f4bc7abb feat: Phase 8.2 — 实现 PostgreSQL MessageRepository 2026-06-14 17:53:14 +08:00
hhs
dae5722945 feat: Phase 8.1 — 定义 MessageRepository 接口和消息表迁移脚本 2026-06-14 17:52:21 +08:00
hhs
3c5c4943e8 feat: Phase 7.7 — 编写 WS 认证测试
- TestWS_AuthMissingToken: 无 token 返回 401
- TestWS_AuthInvalidToken: 无效 token 返回 401
- TestWS_AuthExpiredToken: 过期 token 返回 401
- TestWS_AuthValidToken: 有效 token 成功连接
- TestWS_AuthConversationIDResume: conversation_id 恢复已有对话
- TestWS_AuthConversationIDNotFound: 不存在的 conversation_id 返回 401
- TestWS_AuthConversationIDOwnership: 非 owner 访问返回 401
2026-06-14 17:48:47 +08:00
hhs
80c6b1b56e feat: Phase 7.3 — WS conversation_id 处理
- ?conversation_id=xxx 存在时校验 session 归属(UserID 匹配)
- 校验失败返回 401 SESSION_NOT_FOUND
- 校验通过则复用已有 session;否则创建新 session
2026-06-14 17:47:34 +08:00
hhs
905b56640e feat: Phase 7.2 — WS 连接 JWT 认证
- 从 ?token=xxx 查询参数提取 access_token
- 校验失败返回 401(missing token / invalid token)
- 校验成功后将 userID 用于创建会话
- 更新现有测试:setupTestServer 自动生成有效 token
2026-06-14 17:47:05 +08:00
hhs
2aa3c98ab6 feat: Phase 7.1 — 修改 ServeWS 签名,新增 tokenMgr 参数
- ServeWS 和 serveWS 函数新增 *auth.TokenManager 参数
- main.go 传入 tokenMgr 到 ServeWS
- handler_test.go 适配新签名
2026-06-14 17:46:11 +08:00
hhs
d01aeaba68 feat: 编写 ConversationHandler API 测试(httptest + mock SessionManager)
- List: 成功、分页参数、未认证 401
- Create: 成功、自定义配置
- Get: 成功、404 未找到、404 权限不足(隐藏信息)
- UpdateTitle: 成功、空标题校验、超长标题校验
- Delete: 成功、权限不足
- GetMessages: 成功、limit 分页、before 偏移分页、权限不足
- 共 17 个 Conversation 测试用例,全部通过
2026-06-14 17:41:28 +08:00