统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/apperror"
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/model"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CandidateRepository interface {
|
||||
List(context.Context, bool, string, int) ([]model.CandidateRevision, error)
|
||||
Detail(context.Context, string, bool) (model.CandidateDetail, error)
|
||||
Create(context.Context, model.CandidateDraftInput, string, string, string, int64) (model.CandidateRevision, error)
|
||||
Transition(context.Context, string, string, model.CandidateActionInput, string, string) (model.CandidateRevision, error)
|
||||
}
|
||||
type CandidateService struct {
|
||||
repo CandidateRepository
|
||||
development bool
|
||||
}
|
||||
|
||||
func NewCandidateService(repo CandidateRepository, development bool) *CandidateService {
|
||||
return &CandidateService{repo: repo, development: development}
|
||||
}
|
||||
func (s *CandidateService) authorize(actor model.User, admin bool) error {
|
||||
if actor.Status != "ACTIVE" || (admin && actor.Role != "ADMIN") || (!admin && actor.Role != "USER") {
|
||||
return apperror.ErrForbidden
|
||||
}
|
||||
if s == nil || s.repo == nil {
|
||||
return apperror.ErrUnavailable
|
||||
}
|
||||
// Manual simulation publishing is a development capability, not production authorization.
|
||||
if !s.development {
|
||||
return apperror.ErrUnavailable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *CandidateService) List(ctx context.Context, actor model.User, admin bool, status string, limit int) ([]model.CandidateRevision, error) {
|
||||
if err := s.authorize(actor, admin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if limit < 1 || limit > 100 {
|
||||
return nil, apperror.ErrInvalidInput
|
||||
}
|
||||
switch status {
|
||||
case "", "DRAFT", "PENDING_REVIEW", "PUBLISHED", "RETURNED", "WITHDRAWN":
|
||||
default:
|
||||
return nil, apperror.ErrInvalidInput
|
||||
}
|
||||
if !admin && status != "" {
|
||||
return nil, apperror.ErrInvalidInput
|
||||
}
|
||||
return s.repo.List(ctx, admin, status, limit)
|
||||
}
|
||||
func (s *CandidateService) Detail(ctx context.Context, actor model.User, id string, admin bool) (model.CandidateDetail, error) {
|
||||
if err := s.authorize(actor, admin); err != nil {
|
||||
return model.CandidateDetail{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
return model.CandidateDetail{}, apperror.ErrInvalidInput
|
||||
}
|
||||
return s.repo.Detail(ctx, id, admin)
|
||||
}
|
||||
func (s *CandidateService) Create(ctx context.Context, actor model.User, source, requestID string, input model.CandidateDraftInput, expected int64) (model.CandidateRevision, error) {
|
||||
if err := s.authorize(actor, true); err != nil {
|
||||
return model.CandidateRevision{}, err
|
||||
}
|
||||
if source != "" {
|
||||
if _, err := uuid.Parse(source); err != nil {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if expected < 1 {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
}
|
||||
if _, err := uuid.Parse(requestID); err != nil {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if err := model.ValidateDraft(input, time.Now().UTC()); err != nil {
|
||||
return model.CandidateRevision{}, err
|
||||
}
|
||||
return s.repo.Create(ctx, input, actor.ID, requestID, source, expected)
|
||||
}
|
||||
func (s *CandidateService) Transition(ctx context.Context, actor model.User, id, action, requestID string, input model.CandidateActionInput) (model.CandidateRevision, error) {
|
||||
if err := s.authorize(actor, true); err != nil {
|
||||
return model.CandidateRevision{}, err
|
||||
}
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if _, err := uuid.Parse(requestID); err != nil {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if input.ExpectedVersion < 1 {
|
||||
return model.CandidateRevision{}, apperror.ErrInvalidInput
|
||||
}
|
||||
if err := model.ValidateReason(input.Reason); err != nil {
|
||||
return model.CandidateRevision{}, err
|
||||
}
|
||||
return s.repo.Transition(ctx, id, action, input, actor.ID, requestID)
|
||||
}
|
||||
Reference in New Issue
Block a user