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
+50 -3
View File
@@ -5,6 +5,7 @@ import (
"errors"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
@@ -45,9 +46,25 @@ func (r *IdentityRepository) FindCredential(ctx context.Context, login string) (
return u, c, mapError(err)
}
func (r *IdentityRepository) CreateSession(ctx context.Context, s model.Session) error {
_, err := r.pool.Exec(ctx, `INSERT INTO veyra_session(id,user_id,token_hash,audience,expires_at) VALUES($1,$2,$3,$4,$5)`, s.ID, s.UserID, s.TokenHash, s.Audience, s.ExpiresAt)
return mapError(err)
func (r *IdentityRepository) CreateSession(ctx context.Context, s model.Session, verifiedPasswordHash string) error {
tx, err := r.pool.Begin(ctx)
if err != nil {
return mapError(err)
}
defer tx.Rollback(context.Background())
var userID string
err = tx.QueryRow(ctx, `SELECT u.id FROM veyra_user u JOIN veyra_credential c ON c.user_id=u.id WHERE u.id=$1 AND c.password_hash=$2 AND u.status='ACTIVE' AND (($3='admin' AND u.role='ADMIN') OR ($3='app' AND u.role='USER')) FOR SHARE OF u,c`, s.UserID, verifiedPasswordHash, s.Audience).Scan(&userID)
if errors.Is(err, pgx.ErrNoRows) {
return apperror.ErrUnauthenticated
}
if err != nil {
return mapError(err)
}
_, err = tx.Exec(ctx, `INSERT INTO veyra_session(id,user_id,token_hash,audience,expires_at) VALUES($1,$2,$3,$4,$5)`, s.ID, s.UserID, s.TokenHash, s.Audience, s.ExpiresAt)
if err != nil {
return mapError(err)
}
return mapError(tx.Commit(ctx))
}
func (r *IdentityRepository) FindSessionUser(ctx context.Context, hash, audience string, now time.Time) (model.User, error) {
@@ -80,3 +97,33 @@ func (r *IdentityRepository) ListUsers(ctx context.Context, limit int) ([]model.
}
return users, mapError(rows.Err())
}
func (r *IdentityRepository) ChangeAdminCredential(ctx context.Context, previousLogin, login, passwordHash string, now time.Time) (string, error) {
tx, err := r.pool.Begin(ctx)
if err != nil {
return "", mapError(err)
}
defer tx.Rollback(context.Background())
var userID, role, status string
err = tx.QueryRow(ctx, `SELECT u.id,u.role,u.status FROM veyra_user u JOIN veyra_credential c ON c.user_id=u.id WHERE c.login_name=$1 FOR UPDATE OF u,c`, previousLogin).Scan(&userID, &role, &status)
if err != nil {
return "", mapError(err)
}
if role != "ADMIN" || status != "ACTIVE" {
return "", apperror.ErrForbidden
}
if _, err := tx.Exec(ctx, `UPDATE veyra_credential SET login_name=$2,password_hash=$3 WHERE user_id=$1`, userID, login, passwordHash); err != nil {
return "", mapError(err)
}
tag, err := tx.Exec(ctx, `UPDATE veyra_session SET revoked_at=$2 WHERE user_id=$1 AND revoked_at IS NULL`, userID, now)
if err != nil {
return "", mapError(err)
}
if err := appendAudit(ctx, tx, userID, "ADMIN_CREDENTIAL_CHANGED", "user", userID, uuid.NewString(), map[string]any{"sessions_revoked": tag.RowsAffected()}); err != nil {
return "", err
}
if err := tx.Commit(ctx); err != nil {
return "", mapError(err)
}
return userID, nil
}