统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/apperror"
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/model"
|
||||
"github.com/google/uuid"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TradePlanRepository interface {
|
||||
Preview(context.Context, string, string, string, int64, int64, int64) (model.PlanPreview, error)
|
||||
Confirm(context.Context, string, string, string) (model.TradePlan, error)
|
||||
List(context.Context, string) ([]model.TradePlan, error)
|
||||
Cancel(context.Context, string, string, int64, string) (model.TradePlan, error)
|
||||
Budget(context.Context, string, string) (model.PlanBudget, error)
|
||||
}
|
||||
type TradePlanService struct {
|
||||
repo TradePlanRepository
|
||||
development bool
|
||||
}
|
||||
|
||||
func NewTradePlanService(repo TradePlanRepository, development bool) *TradePlanService {
|
||||
return &TradePlanService{repo, development}
|
||||
}
|
||||
func (s *TradePlanService) authorize(actor model.User) error {
|
||||
if actor.Role != "USER" || actor.Status != "ACTIVE" {
|
||||
return apperror.ErrForbidden
|
||||
}
|
||||
if s == nil || s.repo == nil || !s.development {
|
||||
return apperror.ErrUnavailable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var quantityPattern = regexp.MustCompile(`^[1-9][0-9]{0,6}$`)
|
||||
|
||||
func ParseQuantity(value string) (int64, error) {
|
||||
if !quantityPattern.MatchString(value) {
|
||||
return 0, apperror.ErrInvalidInput
|
||||
}
|
||||
qty, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil || qty > 1000000 {
|
||||
return 0, apperror.ErrInvalidInput
|
||||
}
|
||||
return qty, nil
|
||||
}
|
||||
|
||||
type PlanPreviewInput struct {
|
||||
AccountID string `json:"account_id"`
|
||||
CandidateRevisionID string `json:"candidate_revision_id"`
|
||||
ExpectedCandidateVersion int64 `json:"expected_candidate_version"`
|
||||
Quantity string `json:"quantity"`
|
||||
FeeBudget string `json:"fee_budget"`
|
||||
}
|
||||
|
||||
func (s *TradePlanService) Preview(ctx context.Context, actor model.User, input PlanPreviewInput) (model.PlanPreview, error) {
|
||||
if err := s.authorize(actor); err != nil {
|
||||
return model.PlanPreview{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(input.AccountID); err != nil {
|
||||
return model.PlanPreview{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if _, err := uuid.Parse(input.CandidateRevisionID); err != nil {
|
||||
return model.PlanPreview{}, apperror.ErrInvalidInput
|
||||
}
|
||||
qty, err := ParseQuantity(input.Quantity)
|
||||
if err != nil || qty%100 != 0 || input.ExpectedCandidateVersion < 1 {
|
||||
return model.PlanPreview{}, apperror.ErrInvalidInput
|
||||
}
|
||||
fee, err := model.ParseMoney(input.FeeBudget)
|
||||
if err != nil {
|
||||
return model.PlanPreview{}, err
|
||||
}
|
||||
return s.repo.Preview(ctx, actor.ID, input.AccountID, input.CandidateRevisionID, input.ExpectedCandidateVersion, qty, fee)
|
||||
}
|
||||
func (s *TradePlanService) Confirm(ctx context.Context, actor model.User, id, requestID string) (model.TradePlan, error) {
|
||||
if err := s.authorize(actor); err != nil {
|
||||
return model.TradePlan{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
return model.TradePlan{}, apperror.ErrInvalidInput
|
||||
}
|
||||
return s.repo.Confirm(ctx, actor.ID, id, requestID)
|
||||
}
|
||||
func (s *TradePlanService) List(ctx context.Context, actor model.User) ([]model.TradePlan, error) {
|
||||
if err := s.authorize(actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.List(ctx, actor.ID)
|
||||
}
|
||||
func (s *TradePlanService) Cancel(ctx context.Context, actor model.User, id string, expected int64, requestID string) (model.TradePlan, error) {
|
||||
if err := s.authorize(actor); err != nil {
|
||||
return model.TradePlan{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(id); err != nil || expected < 1 {
|
||||
return model.TradePlan{}, apperror.ErrInvalidInput
|
||||
}
|
||||
return s.repo.Cancel(ctx, actor.ID, id, expected, requestID)
|
||||
}
|
||||
func (s *TradePlanService) Budget(ctx context.Context, actor model.User, id string) (model.PlanBudget, error) {
|
||||
if err := s.authorize(actor); err != nil {
|
||||
return model.PlanBudget{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
return model.PlanBudget{}, apperror.ErrInvalidInput
|
||||
}
|
||||
return s.repo.Budget(ctx, actor.ID, id)
|
||||
}
|
||||
Reference in New Issue
Block a user