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
+27 -3
View File
@@ -21,7 +21,15 @@ type LoginLimiter interface {
Login(context.Context, string, string) (bool, error)
}
type IdentityOptions struct {
Market *service.MarketService
Backtests *service.BacktestService
Auth *service.AuthService
Providers *service.ProviderService
Candidates *service.CandidateService
Plans *service.TradePlanService
Fills *service.TradeFillService
Notifications *service.NotificationService
Strategies *service.StrategyService
Accounts *service.AccountService
Limiter LoginLimiter
DevelopmentLogin bool
@@ -209,7 +217,11 @@ func sameOrigin(r *http.Request) bool {
}
func decode(c *gin.Context, value any) bool {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 16<<10)
return decodeLimit(c, value, 16<<10)
}
func decodeLimit(c *gin.Context, value any, limit int64) bool {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, limit)
if !strings.HasPrefix(strings.ToLower(c.GetHeader("Content-Type")), "application/json") {
failure(c, http.StatusUnsupportedMediaType, "INVALID_CONTENT_TYPE", "请使用 application/json。", false)
return false
@@ -237,13 +249,25 @@ func failure(c *gin.Context, status int, code, message string, retryable bool) {
func fail(c *gin.Context, err error) {
switch {
case errors.Is(err, apperror.ErrInvalidInput):
failure(c, http.StatusUnprocessableEntity, "INVALID_INPUT", "请检查输入内容;资金最多两位小数,密码为 12–72 字节。", false)
failure(c, http.StatusUnprocessableEntity, "INVALID_INPUT", "请检查输入字段、必填项与格式。", false)
case errors.Is(err, apperror.ErrUnauthenticated):
failure(c, http.StatusUnauthorized, "UNAUTHENTICATED", "登录信息无效或会话已过期。", false)
case errors.Is(err, apperror.ErrForbidden):
failure(c, http.StatusForbidden, "FORBIDDEN", "无权执行此操作。", false)
case errors.Is(err, apperror.ErrInsufficientCash):
failure(c, http.StatusConflict, "INSUFFICIENT_CASH", "可用现金不足,请检查其他计划保留的预算。", false)
case errors.Is(err, apperror.ErrInsufficientQuantity):
failure(c, http.StatusConflict, "INSUFFICIENT_SELLABLE_QUANTITY", "可卖数量不足;请检查模拟可卖时间与剩余持仓。", false)
case errors.Is(err, apperror.ErrCandidateUnavailable):
failure(c, http.StatusConflict, "CANDIDATE_UNAVAILABLE", "候选已失效或版本已更新,请重新查看依据。", false)
case errors.Is(err, apperror.ErrPreviewExpired):
failure(c, http.StatusConflict, "PREVIEW_EXPIRED", "预算预览已过期,请重新预览后确认。", false)
case errors.Is(err, apperror.ErrChronology):
failure(c, http.StatusConflict, "FILL_CHRONOLOGY", "请按成交发生时间顺序录入;更早记录需先纠正后续成交。", false)
case errors.Is(err, apperror.ErrDependentFill):
failure(c, http.StatusConflict, "DEPENDENT_FILL", "后续成交依赖此记录,请先冲正最新成交。", false)
case errors.Is(err, apperror.ErrConflict):
failure(c, http.StatusConflict, "CONFLICT", "该登录名或账户类型已存在。", false)
failure(c, http.StatusConflict, "CONFLICT", "记录已存在或版本已更新,请刷新后重试。", false)
case errors.Is(err, apperror.ErrNotFound):
failure(c, http.StatusNotFound, "NOT_FOUND", "记录不存在。", false)
default: