Checks / verify (push) Failing after 9m52s
统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
103 lines
3.7 KiB
Go
103 lines
3.7 KiB
Go
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)
|
|
}
|