feat: 官网 + 管理端 + Gin/GORM 后端首个完整版本

- web/: 产品官网(Hero 事件流终端、功能矩阵、架构、快速开始、下载、博客 + Markdown 详情页),青瓷绿双主题
- admin/: 内容管理(JWT 登录、文章分页/搜索/CRUD、草稿与发布),embed 挂 /admin
- server/: Gin + GORM,MySQL(DSN 走 .env,SQLite 兜底);规范落地:sundynix_ 表前缀、字符串雪花主键、snake_case 列名、统一响应信封、公共分页参数
- 双 SPA embed 单二进制部署,make build 一键出包

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-17 11:03:31 +08:00
parent d46267dde1
commit 5abc705eae
53 changed files with 5774 additions and 38 deletions
+128
View File
@@ -0,0 +1,128 @@
package store
import (
"strings"
"time"
"github.com/glebarez/sqlite"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"git.sundynix.cn/Blizzard/sundynix-site/server/internal/model"
)
// Open 打开数据库并完成迁移与种子数据。
// DSN 含 "@tcp(" 走 MySQL,否则按 SQLite 文件路径处理(本地开发兜底)。
// 命名规范:表前缀 sundynix_、单数表名、snake_case 列名。
func Open(dsn string) (*gorm.DB, error) {
var dialector gorm.Dialector
if strings.Contains(dsn, "@tcp(") {
dialector = mysql.Open(dsn)
} else {
dialector = sqlite.Open(dsn)
}
db, err := gorm.Open(dialector, &gorm.Config{
Logger: logger.Default.LogMode(logger.Warn),
TranslateError: true, // 唯一索引冲突 → gorm.ErrDuplicatedKey
NamingStrategy: schema.NamingStrategy{
TablePrefix: "sundynix_",
SingularTable: true,
},
})
if err != nil {
return nil, err
}
if err := db.AutoMigrate(&model.Post{}); err != nil {
return nil, err
}
if err := seed(db); err != nil {
return nil, err
}
return db, nil
}
// seed 空库时写入示例文章,方便前端联调。
func seed(db *gorm.DB) error {
var count int64
if err := db.Model(&model.Post{}).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return nil
}
at := func(s string) *time.Time {
t, _ := time.Parse("2006-01-02", s)
return &t
}
posts := []model.Post{
{
Slug: "v0-1-2-release",
Title: "sundynix agentix v0.1.2 发布:应用内更新与团队视图",
Category: "RELEASE",
Summary: "v0.1.2 带来应用内更新横幅、卡通办公室团队视图,以及发布流水线自动出包。",
PublishedAt: at("2026-07-02"),
Content: `## 亮点
- **应用内更新**:新版本发布后,工作台顶部出现更新横幅,一键升级。
- **团队视图**:多智能体协作时,AI 角色会在卡通办公室里走动干活。
- **发布流水线**GitHub Actions 自动产出 macOS universal .app 与 Windows .exe。
## 升级方式
桌面版直接点更新横幅;自托管用户:
` + "```bash\ndocker compose pull && docker compose up -d\n```" + `
完整变更见 Releases 页面。`,
},
{
Slug: "hybrid-retrieval",
Title: "三路混合检索是怎么工作的:vector + fulltext + graph",
Category: "ENGINEERING",
Summary: "向量召回语义、全文召回关键词、图谱召回关系,RRF 把三路结果融成一路。",
PublishedAt: at("2026-06-18"),
Content: `## 为什么一路不够
单靠向量检索,专有名词和精确匹配经常翻车;单靠全文检索,又抓不到语义近邻。
我们的做法是三路并发:
| 通路 | 引擎 | 擅长 |
|------|------|------|
| 向量 | Milvus | 语义相似 |
| 全文 | Bleve | 关键词精确匹配 |
| 图谱 | Neo4j | 实体关系跳跃 |
三路结果用 **RRFReciprocal Rank Fusion** 融合,再过一遍 rerank。
## 调试
检索控制台会展示每一路的召回与得分,坏 case 一眼定位。`,
},
{
Slug: "why-event-driven",
Title: "为什么我们选择事件驱动:NATS 零拷贝骨干网设计记",
Category: "ARCHITECTURE",
Summary: "Agent 的一切都是流:token、执行轨迹、工具调用。事件总线是最自然的骨架。",
PublishedAt: at("2026-05-30"),
Content: `## 流式优先
LLM 的输出天生是 token 流,Agent 的执行天生是事件序列。与其在 HTTP 请求-响应模型上硬凑,不如让整个系统跑在消息总线上。
## 主题设计
` + "```\nsundynix.tasks.* # 任务派发\nsundynix.streams.<id> # token 流\nsundynix.tools.go.* # Go 工具调用\nsundynix.tools.py.* # Python 工具调用\n```" + `
网关订阅流主题直接转 SSE/WS,中间零拷贝。
## 演进
单体先行(Monolith First):现在所有模块跑在一个进程里,但彼此只通过总线说话——拆微服务时只需要把订阅者搬走(Morph B)。`,
},
}
return db.Create(&posts).Error
}