feat(t215): add admin order authorization

This commit is contained in:
QiuSW
2026-07-28 12:50:42 +08:00
parent 16c2ea410a
commit 827afc7257
24 changed files with 2688 additions and 113 deletions
@@ -14,20 +14,23 @@ import (
const localAdminSubject = "local-admin"
type UsecaseAdapter struct {
tasks *usecase.TaskService
assets *usecase.AssetService
tasks *usecase.TaskService
assets *usecase.AssetService
authorizations *usecase.OrderAuthorizationService
}
func NewUsecaseAdapter(
tasks *usecase.TaskService,
assets *usecase.AssetService,
authorizations *usecase.OrderAuthorizationService,
) (*UsecaseAdapter, error) {
if tasks == nil || assets == nil {
if tasks == nil || assets == nil || authorizations == nil {
return nil, errors.New("admin web use cases are required")
}
return &UsecaseAdapter{
tasks: tasks,
assets: assets,
tasks: tasks,
assets: assets,
authorizations: authorizations,
}, nil
}
@@ -146,6 +149,70 @@ func (adapter *UsecaseAdapter) CancelTask(
return taskFromPurchase(task), nil
}
func (adapter *UsecaseAdapter) AuthorizeOrder(
ctx context.Context,
input AuthorizeOrderInput,
) (OrderAuthorization, error) {
detail, err := adapter.tasks.Get(ctx, localAdminSubject, input.TaskID)
if err != nil {
return OrderAuthorization{}, mapUsecaseError(err)
}
if detail.Task.Version != input.ExpectedTaskVersion ||
detail.Execution == nil ||
detail.Report == nil ||
detail.Report.DecisionDataset == nil {
return OrderAuthorization{}, ErrConflict
}
var supersedes *string
if input.SupersedesAuthorizationID != "" {
value := input.SupersedesAuthorizationID
supersedes = &value
}
items := make(
[]usecase.OrderAuthorizationItemInput,
0,
len(detail.Report.DecisionDataset.Observations),
)
for _, observation := range detail.Report.DecisionDataset.Observations {
if observation.Identity == nil {
return OrderAuthorization{}, ErrConflict
}
label := "REJECT"
reason := input.RejectedReasonCode
if observation.Identity.CandidateKey == input.CandidateKey {
label = "ACCEPT"
reason = input.SelectedReasonCode
}
items = append(items, usecase.OrderAuthorizationItemInput{
CandidateKey: observation.Identity.CandidateKey,
Label: label,
PrimaryReasonCode: reason,
ReasonCodes: []string{reason},
})
}
result, err := adapter.authorizations.Create(
ctx,
usecase.CreateOrderAuthorizationCommand{
ActorUserID: actorUserID(ctx),
TaskID: input.TaskID,
IdempotencyKey: input.IdempotencyKey,
ExecutionID: detail.Execution.ID,
TaskContentSHA256: usecase.TaskContentSHA256(detail.Task),
ExpectedTaskVersion: input.ExpectedTaskVersion,
CandidateKey: input.CandidateKey,
ReasonSchemaVersion: 1,
PrimaryReasonCode: "SELECTED_BEST_MATCH",
Note: input.Note,
SupersedesAuthorizationID: supersedes,
Items: items,
},
)
if err != nil {
return OrderAuthorization{}, mapUsecaseError(err)
}
return orderAuthorizationFrom(result.Authorization), nil
}
func taskSummaryFrom(task domain.PurchaseTask) TaskSummary {
return TaskSummary{
ID: task.ID,
@@ -177,11 +244,64 @@ func taskFromPurchase(task domain.PurchaseTask) Task {
func taskFromDetail(detail domain.TaskDetail) Task {
task := taskFromPurchase(detail.Task)
task.Version = detail.Task.Version
task.ReferenceAssetID = detail.Asset.ID
task.ExecutionReport = executionReportFrom(detail.Report)
task.TaskContentSHA256 = usecase.TaskContentSHA256(detail.Task)
if detail.Execution != nil {
task.ExecutionID = detail.Execution.ID
}
if detail.Report != nil && detail.Report.DecisionDataset != nil {
for _, observation := range detail.Report.DecisionDataset.Observations {
if observation.Identity == nil {
continue
}
candidate := AuthorizationCandidate{
CandidateKey: observation.Identity.CandidateKey,
Ordinal: observation.Ordinal,
Title: observation.Title,
SKUText: observation.SKUText,
PriceText: observation.PriceText,
EvidenceURLs: make([]string, 0, len(observation.EvidenceAssetIDs)),
}
for _, evidenceID := range observation.EvidenceAssetIDs {
candidate.EvidenceURLs = append(
candidate.EvidenceURLs,
"/api/v1/tasks/"+detail.Task.ID+
"/evidence/"+evidenceID+"/content",
)
}
task.Candidates = append(task.Candidates, candidate)
}
}
for _, authorization := range detail.OrderAuthorizations {
task.OrderAuthorizations = append(
task.OrderAuthorizations,
orderAuthorizationFrom(authorization),
)
}
return task
}
func orderAuthorizationFrom(
authorization domain.OrderAuthorization,
) OrderAuthorization {
result := OrderAuthorization{
ID: authorization.ID,
Version: authorization.AuthorizationVersion,
CandidateKey: authorization.CandidateKey,
CandidateSKUText: authorization.CandidateSKUText,
CandidatePriceText: authorization.CandidatePriceText,
Quantity: authorization.Quantity,
Status: string(authorization.Status),
CreatedAt: authorization.CreatedAt,
}
if authorization.SupersedesAuthorizationID != nil {
result.SupersedesID = *authorization.SupersedesAuthorizationID
}
return result
}
func executionReportFrom(report *domain.ExecutionReport) *ExecutionReport {
if report == nil {
return nil