Blizzard
|
ec9431bfa4
|
fix(report): 报告源/产物落 MinIO,去掉 gateway↔mcp-go 本地盘耦合
多机/容器化第一时间断裂的隐患(ARCHITECTURE_REVIEW §7 #1):此前报告
源(reportStore)与 .docx 产物(reportExport/reportRender)都写 mcp-go
本地 SUNDYNIX_REPORTS_DIR,gateway 再按同一路径 c.File 回流——mcp-go
与 gateway 不共享磁盘即断。
- blob 包从 gateway/internal 提到 sundynix-shared/blob,gateway 与 mcp-go
共用同一 MinIO;新增 PutBytes/GetBytes 走二进制(.docx)。
- mcp-go 注入 blob:report 源/产物优先 Put 到对象存储(键 reports/<id>.{json,docx}),
导出结果返回 minio://<key>;MinIO 未就绪回退本地盘(单机降级,getSource 兼容旧本地报告)。
- gateway ExportReport 识别 minio:// → GetBytes 流式下载,否则 c.File 本地降级。
- 测试:mcp 本地回落往返单测 + shared/blob 真 MinIO 二进制往返(BLOB_TEST_ENDPOINT 门控)。
四模块 build 绿,受影响测试全过。
- 附带:go mod tidy 引入 genproto 拆包冲突,四模块统一钉单体 genproto 新版解决
(注:勿 go work sync,会剪掉 indirect require 回落旧版重现冲突)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:29:31 +08:00 |
|
Blizzard
|
bff6e5c7fd
|
test(gateway): 补上「钱路径」与租户隔离的回归测试 —— P0 最高优先级缺口
完成度审计(见记忆 completion-audit)最尖的一条:涉及钱的路径此前 0 测试,
支付一上线 bug=真实错账;租户数据层隔离也只测了角色门禁没测数据层真隔离。
把本会话 live 手验过的断言固化成回归测试。
测试基建:纯 Go sqlite(glebarez,无 CGO)内存库,迁同款模型+建部分唯一索引+
挂租户作用域回调,复用生产 store 方法测真逻辑。CI ubuntu 无 Postgres 也能跑
(此前 store 测试全是纯逻辑,DB 事务逻辑从没进过关卡)。
钱路径不变量(6):
- GrantCredits 记分录+增余额,余额恒等于 SUM(ledger)
- 兑换码核销一次性(CAS)+原子入账,重复核销余额纹丝不动
- 账本(kind,ref)部分唯一索引:重复 grant 被兜底拦下、usage 不受约束
- MarkOrderPaid CAS 幂等:重复回调 changed=false 不重复入账
- SaveUsageEvent task_id 幂等:同任务重投不重复扣费
- ReconcileOrders 抓出 order_without_ledger(钱到了积分没给)
租户隔离(3):创建自动填 tenant_id、查询按 ctx 租户过滤、跨租户改/删命不中、
WithoutTenant 系统视角全可见。
go build/vet + 全 gateway 测试全绿;sqlite 仅测试引用,不进生产二进制。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:39:36 +08:00 |
|
Blizzard
|
efd185b779
|
feat(billing): 支付 P5.2 —— 微信支付 Native 渠道(扫码充值)
wechatpay-go v0.2.21。凭据全 env 注入(WECHAT_MCHID/MCH_CERT_SERIAL/
MCH_PRIVATE_KEY/APIV3_KEY/APPID/NOTIFY_URL),缺一渠道即隐藏——半配置/假凭据
只打日志不拖垮 gateway(用假私钥实测过降级)。
- internal/payment:Native 下单出 code_url、APIv3 回调验签解密、主动查单,
三者统一收敛为 QueryResult。
- 下单 POST /billing/orders {pack_id}(≥member+审计):金额/积分按在售包服务端
锁定进订单行,不信任客户端;渠道下单失败即作废,不留付不了的 pending。
- 到账两条路汇入同一个 MarkOrderPaid 幂等闸(CAS+唯一索引双闸,同 P5.1):
①公开回调路由(验签是唯一的门;金额与订单不符不入账);②前端轮询的
GET /billing/orders/:id 在 pending 时顺路主动查单——本地/内网收不到公网
回调也能确认到账,回调只是生产更快的通道。pending 超 30 分钟置 expired。
- Web 面:在售包卡片(渠道亮才出现)→扫码弹窗(qrcode 画 code_url,二维码底色
固定纯白——暗色主题下低对比码扫不出来)→2.5s 轮询→到账 toast+刷余额。
验证:go/tsc/vitest 全绿;无凭据+假凭据两种降级 live 四连
(channels 只剩 redeem/下单 400 引导兑换码/回调 503/兑换码闭环不受影响)。
⚠️ 真通道(prepay→扫码→回调/查单→入账)需真实商户号,未 live——用户配好
env 后用小额包实测,建议先配 ¥0.01 测试包走一单。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:39:24 +08:00 |
|
Blizzard
|
a2d184b7ec
|
feat(observability): OpenTelemetry 全链路追踪(Jaeger + NATS 跨总线传播)
新增 sundynix-shared/otelx:otelx.Init(ctx,服务名) 注册 W3C 传播器 + OTLP/HTTP
批量导出(默认 localhost:4318,docker 里的 Jaeger);OTEL_SDK_DISABLED=true 关导出。
Jaeger 不在线 / 导出器构建失败都不阻断启动(可观测性是增益而非依赖)。
- NATS 跨进程传播(无现成中间件):bus/trace.go 的 natsHeaderCarrier + inject/extract,
PublishTask/CallTool 注入 traceparent,ConsumeTasks/ServeTool 抽出续上 → 链路跨总线连成一棵树。
- 埋点:gateway 挂 otelgin(HTTP server span,链路根);dispatcher task.execute →
node.<kind>(每节点,nctx 下传使工具/LLM 挂到节点下)→ llm.stream/llm.generate;
bus 自动出 tool.call(client)↔tool.serve(server) 成对跨服务 span。
- docker-compose 加 jaeger all-in-one(UI :16686,OTLP :4318)。
- 依赖修复:otlptracehttp 触发 genproto 单体(旧)vs 拆分模块 ambiguous import(milvus 拉旧版),
pin genproto 至后拆分版(go.work 工作区全局生效)。
- production_readiness.md 1.1 更新为「已实现」。
验证:真实 input→retriever→agent 任务在 Jaeger 出 14 span / 3 服务的完整树,
跨 NATS(publish→consume)、跨服务(tool.call→tool.serve)均连通,
瓶颈 kb_search 692ms、llm 1597ms 一眼可见;四模块 build+vet+test 全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-06-24 11:35:08 +08:00 |
|
Blizzard
|
b6a6875795
|
feat(gateway): 可观测性 —— Prometheus 指标 + 结构化日志 + 探针
往"生产可运维"推一步(网关前门):
- Prometheus /metrics:sundynix_http_requests_total{method,route,status}、
request_duration_seconds 直方图、requests_in_flight。route 用 c.FullPath()
路由模板(/tasks/:id/...)避免按真实路径高基数。
- 结构化访问日志:slog JSON 到 stderr(request_id/method/route/status/latency_ms/
ip/uid/bytes),替代 gin 默认文本日志;gin.New()+Recovery 自管中间件链。
- RequestID 中间件:生成/透传 X-Request-ID,写上下文+响应头,供日志关联。
- 探针:/healthz(liveness,不查依赖)、/readyz(readiness,DB+Redis 就绪才 200,
否则 503),供 k8s 等导流判断;/api/v1/health 深度聚合保留。
- 三个根端点不挂业务鉴权(/metrics 生产应由网络层限制抓取来源)。
验证:单测(计数 +1 / X-Request-ID 生成与透传);实跑 /healthz 200、/readyz 200
(db,redis ready)、/metrics 输出真实指标、访问日志 JSON 正常、X-Request-ID 回写。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 10:38:31 +08:00 |
|
Blizzard
|
149c35c21b
|
feat(gateway): 真实鉴权片1 —— JWT 注册/登录 + 校验中间件(后端核心)
替掉"裸 X-User-ID 头当身份"的临时方案,落地无状态 JWT 鉴权后端:
- internal/auth:JWT 签发/校验(HS256,密钥 env JWT_SECRET,仅接受 HMAC 防 alg 混淆)
+ bcrypt 密码哈希/校验。纯包,含单测。
- User 模型加 Name + PasswordHash(json:"-" 不外泄);store 加 CreateUser/GetUserByEmail/
GetUserByID(邮箱唯一冲突 → ErrUserExists)。
- handler/auth:POST /auth/register(建用户+签发)· POST /auth/login(校验+签发,
用户不存在与密码错同一文案防枚举)· GET /auth/me。
- middleware/auth:解析 Bearer JWT,校验通过把已验证 userID 注入上下文(非阻断)。
- userID(c) 改为优先取 JWT 注入的 uid,兜底 X-User-ID 头(前端尚未接登录,保持可用)。
验证:
- 单测:JWT 签发/解析往返、过期拒绝、篡改/非法拒绝、bcrypt 哈希校验。
- 实跑(nats+pg+gateway):注册→token+user(无密码)、重复注册 409、错密码 401、
/auth/me 带 token 200 / 无 token 401;owner 隔离改用已验证 uid —— 带 token 建的库
匿名/伪造 header 都看不到(JWT 用户数据归于雪花 id,header 无法臆测)。
片 2 待做:前端登录页 + 存令牌带 Bearer + 处理 401 + 去掉 header 兜底 + 保护路由。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 16:14:21 +08:00 |
|
Blizzard
|
f610d8d2da
|
feat(kb): 大文档正文存 MinIO(PG 只留元数据+预览+对象键)
超过阈值(8000 字)的正文落对象存储,彻底解决十几万字文件塞 PG 的问题。
- internal/blob:minio-go 封装 Store(Open/Put/Get/Delete + Ready 降级);连不上则降级内联。
- docker-compose:milvus-minio 暴露 9000 端口供网关用作文档对象存储(bucket sundynix-docs)。
- main/router/handler:注入 blob.Store(env MINIO_*,默认 localhost:9000 minioadmin)。
- runIngest:size>8000 且 MinIO 可用 → 正文 Put 到 owner/kb/name,PG content 置空仅存
object_key+preview+size;否则内联。SaveDoc 改为按全文显式传 preview(offload 后内联为空也有预览)。
- KbDoc:object_key 非空时从 MinIO 取回全文。
验证:入 12182 字笔记 → PG content_len=0、object_key=wt/default/超大文件测试、preview 非空、
size=12182;/kb/doc 取回完整 12182 字(来自 MinIO);6321 字的仍内联(object_key 空)。
列表只读元数据+预览。gateway build 通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-13 17:02:44 +08:00 |
|
Blizzard
|
4fd44380aa
|
refactor(store): DB 规约统一 —— 雪花字符串 id + created/updated/deleted_at 软删
所有数据库映射结构体收敛到统一基类,清理混乱。
- store/base.go:BaseModel{ID string(雪花,bwmarrin/snowflake) PK, CreatedAt, UpdatedAt,
DeletedAt gorm软删index} + BeforeCreate 生成 id + NewID()。
- 全模型嵌入 BaseModel:User/Task/LLMModel/KB/Doc/Agent(去掉各自 ID uint/CreatedAt);
Task 业务 id(task_xxx)挪到 TaskID 唯一列,主键统一雪花。
- 模型 id uint→string:admin :id 路由、SetActiveModel/DeleteModel/SaveModel、modelBody.ID。
- 一次性迁移 migrateLegacyIntIDs:检测旧整型 id(AutoMigrate 不改主键类型)→ 备份
sundynix_model 行(唯一不可再生的 API 密钥)→ 删旧表 → 按新规约重建 → 回灌模型(新雪花 id)。
其它表(User/Task/KB/Doc/Agent)为可重建测试数据,重置。
- Doc 预埋 Size/Preview/ObjectKey 字段,DocLink 表(为后续 B/C)。
验证:重启 gateway → 日志"回灌 2 条模型配置";PG sundynix_model.id=varchar、有
created_at/updated_at/deleted_at;DeepSeek/百炼 密钥保留(keylen 35);admin 列表返回
雪花 string id + 脱敏 key;健康五灯全绿。gateway build 通过。
注:mcp-go 的 sundynix_user_profile(Profile) 模型尚未套同规约,待跟进对齐。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-13 16:45:52 +08:00 |
|
Blizzard
|
21e5f6620d
|
chore: fresh-clone 可直接运行审查与修复
确保 git clone 后零配置(除 LLM key)即可跑通后端链路。
- go.work: 移出 sundynix-desktop(Wails,//go:embed frontend/dist 需先 npm build,
且 main 为桩),避免污染后端工作区构建;保留后端 4 模块
- mcp-py pyproject: 注释未 import 的 mcp/docker 重依赖(对应功能仍为桩),
fresh pip install 只装实际用到的 nats-py + docx/openpyxl/pypdf,更快更稳
- README: 完整环境改为 fresh-clone 零配置运行指引(infra→4后端→2前端→控制台配模型)
- 审查结论: 无硬编码绝对路径; 各服务 env 默认全对齐 docker-compose; go.sum/
package-lock 齐全; /tmp clone 实测: 4 后端模块 go build ✓、admin npm ci+build ✓、
mcp-py 全新 venv 安装+导入 ✓; e2e PASS
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-11 17:13:55 +08:00 |
|
Blizzard
|
e5fa0ae36c
|
feat: Gateway store 桩换真实 GORM/Postgres + go-redis (含自动迁移与优雅降级)
第 2 层网关持久层落地,遵守 sundynix_ 表名前缀 + AutoMigrate 约定。
- store: GORM(NamingStrategy 前缀 sundynix_/单数) → User=sundynix_user, Task=sundynix_task
启动 AutoMigrate;go-redis/v9 滑动窗口限流(Incr+Expire,按 IP)
- 优雅降级:连不上库则 warn 继续(不 fatal),保证无 Docker 的 make demo 仍跑通
- handler: SubmitTask 持久化任务(best-effort),Billing 真实读库返回 tasks_submitted
- main: OpenPostgres/OpenRedis 读 POSTGRES_DSN/REDIS_ADDR 环境变量
- 验证: 4 模块 build ✓;e2e 3 测试 PASS;live 双路径(真实库持久化 + 坏DSN降级)实测通过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-10 11:43:53 +08:00 |
|
Blizzard
|
c7a02c3905
|
feat: 初始化 sundynix-agentix 分层式 AI Agent 平台脚手架
5 层 + 1 条 NATS 零拷贝消息总线的 monorepo(Monolith First → Microservices Morph B)。
纵向主干(任务流 + Token 流回流)已真实跑通,横向各层能力为带注释的桩。
已贯通(real code):
- sundynix-shared: 共享契约 + JetStream/core NATS 真实收发(bus) + 内嵌 NATS(devnats) + e2e 测试
- sundynix-gateway: Gin 接入 + DSL 解析组装 + NATS Publish + SSE 流式输出
- sundynix-dispatcher: NATS 消费 + Eino Orchestrator 流式回流 + 熔断器 + LLM Pool 占位流式
- 链路: HTTP POST → DSL → sundynix.tasks.* → Dispatcher → Token 经 sundynix.streams.<id> 回流 → SSE
- 基础设施: docker-compose(nats/postgres/redis/neo4j/milvus) + Makefile(make demo/e2e)
待填(桩):
- Eino 图编排 compose.NewGraph、LLM Pool 接 vLLM/Ollama
- Gateway store 换真实 pgx/redis
- sundynix-mcp-go: Bleve+Milvus+Neo4j 混合检索 / UniOffice / 外部 API
- sundynix-mcp-py: gVisor 沙箱 / MinerU(PaddleOCR) / Docker 解释器
- sundynix-desktop: React Flow 画布 → DSL 导出 → SSE 展示
|
2026-06-10 11:00:29 +08:00 |
|