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
@@ -24,13 +24,15 @@ const (
)
type AdminServices struct {
Assets *usecase.AssetService
Tasks *usecase.TaskService
Results *usecase.ExecutionResultService
Assets *usecase.AssetService
Tasks *usecase.TaskService
Results *usecase.ExecutionResultService
Authorizations *usecase.OrderAuthorizationService
}
func (s AdminServices) validate() error {
if s.Assets == nil || s.Tasks == nil || s.Results == nil {
if s.Assets == nil || s.Tasks == nil || s.Results == nil ||
s.Authorizations == nil {
return errors.New("admin services are required")
}
return nil
@@ -55,6 +57,10 @@ func registerAdminAPI(routes gin.IRoutes, services AdminServices) error {
handler.evidenceContent,
)
routes.POST("/api/v1/tasks/:id/cancel", handler.cancelTask)
routes.POST(
"/api/v1/tasks/:id/order-authorizations",
handler.createOrderAuthorization,
)
return nil
}
@@ -371,13 +377,116 @@ func (h *adminHandlers) taskDetail(ctx *gin.Context) {
"claim": claim,
"execution": execution,
"execution_report": executionReport,
"events": events,
"order_authorizations": orderAuthorizationResponses(
detail.OrderAuthorizations,
),
"events": events,
"assets": []gin.H{
assetResponse(detail.Asset),
},
})
}
func (h *adminHandlers) createOrderAuthorization(ctx *gin.Context) {
var request struct {
ExecutionID string `json:"execution_id"`
TaskContentSHA256 string `json:"task_content_sha256"`
ExpectedTaskVersion int64 `json:"expected_task_version"`
CandidateKey string `json:"candidate_key"`
ReasonSchemaVersion int `json:"reason_schema_version"`
PrimaryReasonCode string `json:"primary_reason_code"`
Note string `json:"note"`
SupersedesAuthorizationID *string `json:"supersedes_authorization_id"`
Items []usecase.OrderAuthorizationItemInput `json:"items"`
}
if err := decodeJSON(ctx, &request); err != nil {
writePublicError(
ctx,
http.StatusBadRequest,
"INVALID_JSON",
"request body must be valid JSON",
false,
gin.H{},
)
return
}
result, err := h.services.Authorizations.Create(
ctx.Request.Context(),
usecase.CreateOrderAuthorizationCommand{
ActorUserID: adminActorUserID(ctx),
TaskID: ctx.Param("id"),
IdempotencyKey: ctx.GetHeader("Idempotency-Key"),
ExecutionID: request.ExecutionID,
TaskContentSHA256: request.TaskContentSHA256,
ExpectedTaskVersion: request.ExpectedTaskVersion,
CandidateKey: request.CandidateKey,
ReasonSchemaVersion: request.ReasonSchemaVersion,
PrimaryReasonCode: request.PrimaryReasonCode,
Note: request.Note,
SupersedesAuthorizationID: request.SupersedesAuthorizationID,
Items: request.Items,
},
)
if err != nil {
writeUsecaseError(ctx, err)
return
}
ctx.Header("Cache-Control", "no-store")
ctx.JSON(http.StatusCreated, gin.H{
"authorization": orderAuthorizationResponse(result.Authorization),
"replayed": result.Replayed,
})
}
func orderAuthorizationResponses(
authorizations []domain.OrderAuthorization,
) []gin.H {
result := make([]gin.H, 0, len(authorizations))
for _, authorization := range authorizations {
result = append(result, orderAuthorizationResponse(authorization))
}
return result
}
func orderAuthorizationResponse(
authorization domain.OrderAuthorization,
) gin.H {
return gin.H{
"id": authorization.ID,
"task_id": authorization.TaskID,
"execution_id": authorization.ExecutionID,
"authorization_version": authorization.AuthorizationVersion,
"candidate_key": authorization.CandidateKey,
"task_content_sha256": authorization.TaskContentSHA256,
"task_version": authorization.TaskVersion,
"review_id": authorization.ReviewID,
"review_version": authorization.ReviewVersion,
"user_id": authorization.UserID,
"device_id": authorization.DeviceID,
"claim_generation": authorization.ClaimGeneration,
"original_sku": authorization.OriginalSKU,
"quantity": authorization.Quantity,
"candidate_sku_text": authorization.CandidateSKUText,
"candidate_price_text": authorization.CandidatePriceText,
"card_signature": authorization.CardSignature,
"detail_signature": authorization.DetailSignature,
"detail_evidence_sha256": authorization.DetailEvidenceSHA256,
"specification_evidence_sha256": authorization.SpecificationEvidenceSHA256,
"status": authorization.Status,
"supersedes_authorization_id": authorization.SupersedesAuthorizationID,
"created_by_user_id": authorization.CreatedByUserID,
"created_at": formatTime(authorization.CreatedAt),
"delivered_at": formatOptionalTime(authorization.DeliveredAt),
"acknowledged_at": formatOptionalTime(authorization.AcknowledgedAt),
"execution_started_at": formatOptionalTime(authorization.ExecutionStartedAt),
"consumed_at": formatOptionalTime(authorization.ConsumedAt),
"failed_at": formatOptionalTime(authorization.FailedAt),
"revoked_at": formatOptionalTime(authorization.RevokedAt),
"failure_code": authorization.FailureCode,
"failure_message": authorization.FailureMessage,
}
}
func executionReportResponse(report *domain.ExecutionReport) gin.H {
events := make([]gin.H, 0, len(report.Events))
for _, event := range report.Events {