把工作区里其余在制品一并入库,主要是工作台产品化的推进:
后端:新增 capability_definition / project / my_app_center / office_skill
接口与 action_definition / skill_definition / project / user_app_center
模型,config 加路由健康上报。
前端:新增 frontend/src/skills(Office 技能与 workbuddy 复刻)、
项目管理、应用中心、能力目录页,以及配套 api / store / config;
聊天侧新增 SpecialistChip / SpecialistPanel / SkillStrip / AppChatRail
等组件。
清理:移除旧 views/tools 下的单页工具(已并入工作台)、_frozen 冻结组件、
cmd/inspect_oa_debug 调试入口,以及两份调试笔记。
其它:文档与启动脚本同步。
(这批改动与上一提交的 SY23 工作并行进行,此前已在同一工作区内交织。)
Co-Authored-By: Claude Code <noreply@anthropic.com>
7.1 KiB
7.1 KiB
AR01 — 后端架构设计
版本:V2.0 | 框架:Go + Gin + GORM + SQLite(modernc) 参考:eai_agentplatform/backend-go/internal/api/ + internal/model/ + internal/store/
当前实现:Go 单二进制 + systemd + Clonezilla 整盘克隆,
backend-go/internal/为标准源码布局。
1. 架构分层
┌─────────────────────────────────────────────┐
│ API 路由层 (api/) │
│ auth / company_train / product / course │
│ exam / media / ai_chat / system │
├─────────────────────────────────────────────┤
│ Gin Context + 请求/响应结构体 │
│ web.OK / web.Fail 统一响应 │
├─────────────────────────────────────────────┤
│ 业务逻辑 (api/ 内函数) │
│ exam.go / media.go / ai_chat.go │
├─────────────────────────────────────────────┤
│ GORM 模型层 (model/) │
│ User / Product / Course / MediaFile / ... │
├─────────────────────────────────────────────┤
│ 数据库层 (store/) │
│ db.go: SQLite Init + AutoMigrate │
├─────────────────────────────────────────────┤
│ SQLite + data/media │
└─────────────────────────────────────────────┘
2. 目录结构
backend-go/
├── cmd/
│ └── server.go # 入口:gin.Engine + systemd 裸进程
├── internal/
│ ├── api/ # 路由层(Gin handlers)
│ │ ├── auth.go # /api/auth/* — 登录/注册/me
│ │ ├── company_train.go # /api/company-train/*
│ │ ├── product.go # /api/products/*
│ │ ├── exam.go # /api/exam/* — 题库/组卷/考试/记录
│ │ ├── media.go # /api/media/* — 上传/预览/审批
│ │ ├── ai_chat.go # /api/ai-chat/* — PathCoach SSE
│ │ ├── system.go # /api/system/* — 用户/成绩/配置
│ │ ├── router.go # RegisterRoutes 总路由
│ │ └── (其余为功能模块)
│ ├── model/ # GORM 模型
│ │ ├── user.go
│ │ ├── product.go
│ │ ├── course.go
│ │ ├── media_file.go
│ │ ├── knowledge_chunk.go
│ │ ├── question.go
│ │ ├── exam_paper.go
│ │ └── exam_record.go
│ ├── store/ # 数据库层
│ │ └── db.go # SQLite Init + AutoMigrate
│ ├── ai/ # AI 客户端与检索
│ │ ├── llm.go # OpenAI 兼容 LLM 调用
│ │ └── retrieve.go # 知识检索
│ ├── config/ # 配置加载
│ └── middleware/ # 中间件(JWT 认证等)
├── knowledge_source/ # 知识源 Markdown(运行时)
├── data/ # 运行时数据(db+media,git 忽略)
└── deploy/ # systemd 单元 + DELIVERY.md
3. 应用装配模式(RegisterRoutes)
入口 cmd/server.go 创建 gin.Engine,调用 api.RegisterRoutes 注册全部路由:
// backend-go/cmd/server.go
package main
import (
"github.com/gin-gonic/gin"
"eai_agentplatform/backend/internal/api"
"eai_agentplatform/backend/internal/config"
)
func main() {
cfg := config.Load()
r := gin.Default()
// 公开静态文件(已审批素材)
r.Static("/media", cfg.KBDataDir+"/approved")
// 注册全部路由
api.RegisterRoutes(r, cfg)
r.Run(":8080")
}
路由注册(api/router.go):
// api/router.go
func RegisterRoutes(r *gin.Engine, cfg *config.Config) {
// 健康检查
r.GET("/api/health", HealthCheck)
// 公开 + 员工
r.GET("/api/company-train", middleware.Auth(cfg), GetCompanyTrain)
r.GET("/api/specialists", middleware.Auth(cfg), ListSpecialists)
// 员工端考试
r.GET("/api/exam/list", middleware.Auth(cfg), ExamList)
r.GET("/api/exam/cover", middleware.Auth(cfg), ExamCover)
r.POST("/api/exam/start", middleware.Auth(cfg), ExamStart)
r.POST("/api/exam/submit", middleware.Auth(cfg), ExamSubmit)
// 管理员
admin := r.Group("/api").Use(middleware.Auth(cfg), middleware.RequireAdmin())
{
admin.POST("/products", CreateProduct)
admin.POST("/courses", CreateCourse)
admin.POST("/media/audit/:mediaId", AuditMedia)
admin.POST("/knowledge/scan", KnowledgeScan)
admin.GET("/system/users", ListUsers)
admin.GET("/system/config", GetConfig)
admin.GET("/system/exam-records", ListExamRecords)
}
// AI 对话
r.POST("/api/ai-chat/message", middleware.Auth(cfg), ChatMessage)
r.GET("/api/ai-chat/quick-actions", middleware.Auth(cfg), QuickActions)
}
4. 中间件模式
// internal/middleware/auth.go
func Auth(cfg *config.Config) gin.HandlerFunc {
return func(c *gin.Context) {
claims, err := auth.ParseToken(
c.GetHeader("Authorization"), cfg.JWTSecret,
)
if err != nil {
c.JSON(401, gin.H{"error": "unauthorized"})
c.Abort()
return
}
c.Set("user", claims)
c.Next()
}
}
func RequireAdmin() gin.HandlerFunc {
return func(c *gin.Context) {
user := c.MustGet("user").(jwt.MapClaims)
if role, ok := user["role"].(string); !ok || role != "admin" {
c.JSON(403, gin.H{"error": "forbidden"})
c.Abort()
return
}
c.Next()
}
}
5. API 路由前缀
| 路由前缀 | 模块 | 说明 |
|---|---|---|
/api/auth/* |
auth.go | 登录/注册/当前用户 |
/api/company-train/* |
company_train.go | 公司介绍内容 |
/api/products/* |
product.go | 产品 CRUD + 导入 |
/api/courses/* |
courses.go | 课程 CRUD + 绑定产品 |
/api/exam/* |
exam.go | 题库/组卷/考试/记录 |
/api/media/* |
media.go | 上传/预览/审批/状态 |
/api/ai-chat/* |
ai_chat.go | PathCoach 流式对话 |
/api/system/* |
system.go | 用户/成绩/配置 |
/api/health |
— | 健康检查 |
6. 单二进制交付
CGO_ENABLED=0 go build -o bin/eai_agentplatform-server ./cmd/server
# 输出:statically linked,无运行时依赖
部署:systemd 直接拉起单二进制,data/ 目录下 SQLite 数据库自动建表。