feat: integrate OA activation workflows and business migration

This commit is contained in:
Yvan
2026-11-07 00:09:19 +08:00
parent 64aca495f2
commit 9caf5b1abc
47 changed files with 1831 additions and 152 deletions
+13 -5
View File
@@ -12,10 +12,13 @@ type ActivationUsecase interface {
List(context.Context, business.ActivationFilter, ...business.ListOption) (*business.ActivationPage, error)
SpendDetail(context.Context, uint64) (*business.ActivationSpendDetail, error)
}
type ActivationService struct{ uc ActivationUsecase }
type ActivationService struct {
uc ActivationUsecase
pool *business.ActivationPoolUsecase
}
func NewActivationService(uc *business.ActivationUsecase) *ActivationService {
return &ActivationService{uc: uc}
func NewActivationService(uc *business.ActivationUsecase, pool *business.ActivationPoolUsecase) *ActivationService {
return &ActivationService{uc: uc, pool: pool}
}
func splitQueryValues(values []string) []string {
@@ -57,7 +60,7 @@ func (s *ActivationService) List(ctx context.Context, req *dto.ActivationQuery)
}
out := &dto.ActivationPage{Rows: []*dto.ActivationResponse{}, Total: result.Total, Available: result.Available}
for _, r := range result.Rows {
out.Rows = append(out.Rows, &dto.ActivationResponse{ID: r.ID, PromoteAccount: r.PromoteAccount, PlatformName: r.PlatformName, PlatformTitle: business.PlatformName(r.PlatformName), BusierAccount: r.BusierAccount, ApplicantOA: r.ApplicantOA, BindAt: localTime(r.BindAt), UnbindAt: localTime(r.UnbindAt), ReachAt: localTime(r.ReachAt), EndAt: localTime(r.EndAt()), AccumAmount: r.AccumAmount, Status: r.Status, StatusText: business.ActivationStatusLabel(r.Status)})
out.Rows = append(out.Rows, &dto.ActivationResponse{CreatedAt: localTime(r.CreatedAt), RegisteredPlayers: r.RegisteredPlayers, MetricsAvailable: r.MetricsAvailable, ID: r.ID, PromoteAccount: r.PromoteAccount, PlatformName: r.PlatformName, PlatformTitle: business.PlatformName(r.PlatformName), BusierAccount: r.BusierAccount, ApplicantOA: r.ApplicantOA, BindAt: localTime(r.BindAt), UnbindAt: localTime(r.UnbindAt), ReachAt: localTime(r.ReachAt), EndAt: localTime(r.EndAt()), AccumAmount: r.AccumAmount, Status: r.Status, StatusText: business.ActivationStatusLabel(r.Status)})
}
return out, nil
}
@@ -72,7 +75,12 @@ func (s *ActivationService) SpendDetail(ctx context.Context, id uint64) (*dto.Ac
a := result.Activation
out := &dto.ActivationSpendDetail{PromoteAccount: a.PromoteAccount, BindAt: localTime(a.BindAt), EndAt: localTime(a.EndAt()), List: []*dto.ActivationSpendItem{}, TotalAmount: result.TotalAmount}
for _, item := range result.Items {
out.List = append(out.List, &dto.ActivationSpendItem{PlatformTitle: business.PlatformName(a.PlatformName), GameName: item.GameName, TotalAmount: item.Amount})
out.List = append(out.List, &dto.ActivationSpendItem{PlatformTitle: business.PlatformName(func() string {
if item.PlatformName != "" {
return item.PlatformName
}
return a.PlatformName
}()), GameName: item.GameName, TotalAmount: item.Amount})
}
return out, nil
}
@@ -0,0 +1,112 @@
package business
import (
"context"
"kra/internal/biz/business"
"kra/internal/service/dto"
"strings"
"time"
)
func activationPoolFilter(req *dto.ActivationPoolQuery) (business.ActivationPoolFilter, []business.ListOption, error) {
f := business.ActivationPoolFilter{}
if req == nil {
return f, nil, business.ErrActivationInput
}
for _, source := range splitQueryValues(req.Sources) {
value, err := business.NormalizePlatform(source)
if err != nil {
return f, nil, err
}
f.Sources = append(f.Sources, value)
}
f.Name = strings.TrimSpace(req.Name)
f.Target = strings.TrimSpace(req.Target)
f.Status = req.Status
f.ChannelType = req.ChannelType
if f.ChannelType != "" && f.ChannelType != "公会" && f.ChannelType != "个人" {
return f, nil, business.ErrActivationInput
}
if f.Status != "" && f.Status != "正常" && f.Status != "锁定" && f.Status != "解除合作" {
return f, nil, business.ErrActivationInput
}
loc := time.FixedZone("Asia/Shanghai", 8*3600)
if req.Begin != "" {
v, err := time.ParseInLocation("2006-01-02", req.Begin, loc)
if err != nil {
return f, nil, business.ErrActivationInput
}
f.Begin = &v
}
if req.End != "" {
v, err := time.ParseInLocation("2006-01-02", req.End, loc)
if err != nil {
return f, nil, business.ErrActivationInput
}
f.End = &v
}
if f.Begin != nil && f.End != nil && f.Begin.After(*f.End) {
return f, nil, business.ErrActivationInput
}
page, size := req.Page, req.PageSize
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
if size > 100 {
size = 100
}
if page > int(^uint(0)>>1)/size {
return f, nil, business.ErrActivationInput
}
return f, []business.ListOption{business.ListOffset((page - 1) * size), business.ListLimit(size)}, nil
}
func (s *ActivationService) Pool(ctx context.Context, req *dto.ActivationPoolQuery) (*dto.ActivationPoolPage, error) {
f, opts, err := activationPoolFilter(req)
if err != nil {
return nil, err
}
page, err := s.pool.Pool(ctx, f, opts...)
if err != nil {
return nil, err
}
out := &dto.ActivationPoolPage{Rows: []*dto.ActivationPoolRow{}, Total: page.Total, Available: page.Available}
for _, r := range page.Rows {
out.Rows = append(out.Rows, &dto.ActivationPoolRow{ID: r.ID, SourceDB: r.SourceDB, PlatformTitle: business.PlatformName(r.SourceDB), Name: r.Name, ChannelType: r.ChannelType, Status: r.Status, NoLoginDays: r.NoLoginDays, Rejected: r.Rejected, RejectReason: r.RejectReason, RejectedAt: localTime(r.RejectedAt)})
}
return out, nil
}
func (s *ActivationService) Options(ctx context.Context) ([]*dto.ActivationBindingOption, error) {
rows, err := s.pool.Options(ctx)
if err != nil {
return nil, err
}
out := []*dto.ActivationBindingOption{}
for _, r := range rows {
out = append(out, &dto.ActivationBindingOption{ID: r.ID, SourceDB: r.SourceDB, Account: r.Account, Level: r.Level})
}
return out, nil
}
func (s *ActivationService) Apply(ctx context.Context, req *dto.ActivationApplyRequest) error {
if req == nil || req.PoolID == 0 || req.BusierID == 0 {
return business.ErrActivationInput
}
return s.pool.Apply(ctx, req.PoolID, req.BusierID, req.SourceDB)
}
func (s *ActivationService) Churn(ctx context.Context, req *dto.ActivationPoolQuery) (*dto.ActivationChurnPage, error) {
f, opts, err := activationPoolFilter(req)
if err != nil {
return nil, err
}
page, err := s.pool.Churn(ctx, f, opts...)
if err != nil {
return nil, err
}
out := &dto.ActivationChurnPage{Rows: []*dto.ActivationChurnRow{}, Total: page.Total, Available: page.Available}
for _, r := range page.Rows {
out.Rows = append(out.Rows, &dto.ActivationChurnRow{ID: r.ID, SourceDB: r.SourceDB, PlatformTitle: business.PlatformName(r.SourceDB), Name: r.Name, From: r.From, To: r.To, Reason: r.Reason, Operator: r.Operator, At: *localTime(&r.At), ToPool: r.ToPool})
}
return out, nil
}
+16 -13
View File
@@ -9,19 +9,22 @@ type ActivationQuery struct {
BusierAccount string `form:"busierAccount"`
}
type ActivationResponse struct {
ID uint64 `json:"id,string"`
PromoteAccount string `json:"promoteAccount"`
PlatformName string `json:"platformName"`
PlatformTitle string `json:"platformTitle"`
BusierAccount string `json:"busierAccount"`
ApplicantOA string `json:"applicantOa"`
BindAt *string `json:"bindAt"`
UnbindAt *string `json:"unbindAt"`
ReachAt *string `json:"reachAt"`
EndAt *string `json:"endAt"`
AccumAmount string `json:"accumAmount"`
Status string `json:"status"`
StatusText string `json:"statusText"`
CreatedAt *string `json:"createdAt"`
RegisteredPlayers *int64 `json:"registeredPlayers"`
MetricsAvailable bool `json:"metricsAvailable"`
ID uint64 `json:"id,string"`
PromoteAccount string `json:"promoteAccount"`
PlatformName string `json:"platformName"`
PlatformTitle string `json:"platformTitle"`
BusierAccount string `json:"busierAccount"`
ApplicantOA string `json:"applicantOa"`
BindAt *string `json:"bindAt"`
UnbindAt *string `json:"unbindAt"`
ReachAt *string `json:"reachAt"`
EndAt *string `json:"endAt"`
AccumAmount string `json:"accumAmount"`
Status string `json:"status"`
StatusText string `json:"statusText"`
}
type ActivationPage struct {
Rows []*ActivationResponse `json:"rows"`
+58
View File
@@ -0,0 +1,58 @@
package dto
type ActivationPoolQuery struct {
Page int `form:"pageNum"`
PageSize int `form:"pageSize"`
Sources []string `form:"platformNames"`
Name string `form:"promoteAccount"`
ChannelType string `form:"channelType"`
Status string `form:"status"`
Target string `form:"target"`
Begin string `form:"begin"`
End string `form:"end"`
}
type ActivationPoolRow struct {
ID uint64 `json:"id,string"`
SourceDB string `json:"sourceDb"`
PlatformTitle string `json:"platformTitle"`
Name string `json:"name"`
ChannelType string `json:"channelType"`
Status string `json:"status"`
NoLoginDays *int64 `json:"noLoginDays"`
Rejected bool `json:"rejected"`
RejectReason string `json:"rejectReason"`
RejectedAt *string `json:"rejectedAt"`
}
type ActivationPoolPage struct {
Rows []*ActivationPoolRow `json:"rows"`
Total int64 `json:"total"`
Available bool `json:"available"`
}
type ActivationBindingOption struct {
ID uint64 `json:"id,string"`
SourceDB string `json:"sourceDb"`
Account string `json:"account"`
Level int `json:"level"`
}
type ActivationApplyRequest struct {
PoolID uint64 `json:"poolId,string"`
BusierID uint64 `json:"busierId,string"`
SourceDB string `json:"sourceDb"`
}
type ActivationChurnRow struct {
ID uint64 `json:"id,string"`
SourceDB string `json:"sourceDb"`
PlatformTitle string `json:"platformTitle"`
Name string `json:"name"`
From string `json:"from"`
To string `json:"to"`
Reason string `json:"reason"`
Operator string `json:"operator"`
At string `json:"at"`
ToPool bool `json:"toPool"`
}
type ActivationChurnPage struct {
Rows []*ActivationChurnRow `json:"rows"`
Total int64 `json:"total"`
Available bool `json:"available"`
}