feat: 完成模拟交易闭环、App 与 A 股行情接入
Checks / verify (push) Failing after 9m52s

统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
2026-10-08 22:58:52 +08:00
parent bffdb3858b
commit 92329ddce2
145 changed files with 18593 additions and 587 deletions
+29 -4
View File
@@ -3,7 +3,7 @@ package handler
import (
"context"
"log/slog"
"go.uber.org/zap"
"net/http"
"strings"
"time"
@@ -15,7 +15,7 @@ import (
"git.sundynix.cn/Blizzard/Veyra/internal/service"
)
func NewRouter(health *service.HealthService, logger *slog.Logger, identity IdentityOptions) http.Handler {
func NewRouter(health *service.HealthService, logger *zap.Logger, identity IdentityOptions) http.Handler {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(func(c *gin.Context) {
@@ -29,10 +29,27 @@ func NewRouter(health *service.HealthService, logger *slog.Logger, identity Iden
start := time.Now()
defer func() {
if recover() != nil {
logger.Error("request panic", "request_id", requestID)
logger.Error("request panic", zap.String("request_id", requestID))
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": gin.H{"code": "INTERNAL_ERROR", "message": "Internal server error", "request_id": requestID}})
}
logger.Info("http request", "method", c.Request.Method, "route", c.FullPath(), "status", c.Writer.Status(), "duration_ms", time.Since(start).Milliseconds(), "request_id", requestID)
route := c.FullPath()
if route == "" {
route = "/<unmatched>"
if c.Request.URL.Path == "/" {
route = "/"
} else if strings.HasPrefix(c.Request.URL.Path, "/assets/") {
route = "/assets/*"
}
}
fields := []zap.Field{zap.String("method", c.Request.Method), zap.String("route", route), zap.Int("status", c.Writer.Status()), zap.Int64("duration_ms", time.Since(start).Milliseconds()), zap.String("request_id", requestID)}
switch {
case c.Writer.Status() >= 500:
logger.Error("HTTP request", fields...)
case c.Writer.Status() >= 400:
logger.Warn("HTTP request", fields...)
default:
logger.Info("HTTP request", fields...)
}
}()
c.Next()
})
@@ -48,6 +65,14 @@ func NewRouter(health *service.HealthService, logger *slog.Logger, identity Iden
r.GET("/readyz", check)
r.GET("/api/v1/system/health", check)
bindIdentity(r, identity)
bindProviders(r, identity)
bindCandidates(r, identity)
bindTradePlans(r, identity)
bindTradeFills(r, identity)
bindNotifications(r, identity)
bindStrategies(r, identity)
bindBacktests(r, identity)
bindMarket(r, identity)
console := adminui.Handler()
r.NoRoute(func(c *gin.Context) {
if !strings.HasPrefix(c.Request.URL.Path, "/api/") && c.Request.URL.Path != "/api" {