feat(t223): generate tasks from freight items

This commit is contained in:
QiuSW
2026-07-28 23:51:59 +08:00
parent 6d052b0462
commit 433db45100
25 changed files with 2697 additions and 48 deletions
@@ -18,6 +18,13 @@ type UsecaseAdapter struct {
assets *usecase.AssetService
authorizations *usecase.OrderAuthorizationService
freight *usecase.FreightService
procurement *usecase.ProcurementService
}
func (adapter *UsecaseAdapter) SetProcurement(
procurement *usecase.ProcurementService,
) {
adapter.procurement = procurement
}
func NewUsecaseAdapter(
@@ -73,7 +80,23 @@ func (adapter *UsecaseAdapter) GetFreightOrder(
if err != nil {
return FreightOrderDetail{}, mapUsecaseError(err)
}
items := make([]FreightOrderItem, 0, len(detail.Items))
requestByItem := map[string]domain.ProcurementRequest{}
if adapter.procurement != nil {
requests, requestErr := adapter.procurement.ListForOrder(
ctx,
localAdminSubject,
detail.Order.ID,
)
if requestErr != nil {
return FreightOrderDetail{}, mapUsecaseError(requestErr)
}
for _, request := range requests {
if _, exists := requestByItem[request.FreightOrderItemID]; !exists {
requestByItem[request.FreightOrderItemID] = request
}
}
}
items := make([]FreightItemReview, 0, len(detail.Items))
for _, item := range detail.Items {
view := FreightOrderItem{
ID: item.ID,
@@ -88,7 +111,12 @@ func (adapter *UsecaseAdapter) GetFreightOrder(
if item.Quantity != nil {
view.Quantity = *item.Quantity
}
items = append(items, view)
review := FreightItemReview{Item: view}
if request, exists := requestByItem[item.ID]; exists {
requestView := procurementRequestFrom(request)
review.Request = &requestView
}
items = append(items, review)
}
return FreightOrderDetail{
Order: freightOrderFrom(detail.Order),
@@ -96,6 +124,131 @@ func (adapter *UsecaseAdapter) GetFreightOrder(
}, nil
}
func (adapter *UsecaseAdapter) CreateProcurementRequest(
ctx context.Context,
input CreateProcurementRequestInput,
) (ProcurementRequest, error) {
if adapter.procurement == nil {
return ProcurementRequest{}, ErrUnavailable
}
result, err := adapter.procurement.CreateRequest(
ctx,
usecase.CreateProcurementRequestCommand{
CreatorSubject: localAdminSubject,
ActorUserID: input.ActorUserID,
FreightOrderItemID: input.FreightOrderItemID,
ConfirmProcurementNeeded: input.ConfirmProcurementNeeded,
},
)
if err != nil {
return ProcurementRequest{}, mapUsecaseError(err)
}
return procurementRequestFrom(result.Request), nil
}
func (adapter *UsecaseAdapter) BindProcurementReference(
ctx context.Context,
input BindProcurementReferenceInput,
) (ProcurementRequest, error) {
if adapter.procurement == nil {
return ProcurementRequest{}, ErrUnavailable
}
request, err := adapter.procurement.BindReference(
ctx,
usecase.BindProcurementReferenceCommand{
CreatorSubject: localAdminSubject,
ActorUserID: input.ActorUserID,
RequestID: input.RequestID,
ImageAssetID: input.ImageAssetID,
},
)
if err != nil {
return ProcurementRequest{}, mapUsecaseError(err)
}
return procurementRequestFrom(request), nil
}
func (adapter *UsecaseAdapter) CreateProcurementTask(
ctx context.Context,
input CreateProcurementTaskInput,
) (Task, error) {
if adapter.procurement == nil {
return Task{}, ErrUnavailable
}
result, err := adapter.procurement.CreateTask(
ctx,
usecase.CreateProcurementTaskCommand{
CreatorSubject: localAdminSubject,
ActorUserID: input.ActorUserID,
RequestID: input.RequestID,
IdempotencyKey: input.IdempotencyKey,
},
)
if err != nil {
return Task{}, mapUsecaseError(err)
}
return taskFromPurchase(result.Task), nil
}
func procurementRequestFrom(
request domain.ProcurementRequest,
) ProcurementRequest {
result := ProcurementRequest{
ID: request.ID,
FreightOrderItemID: request.FreightOrderItemID,
SourceRevision: request.SourceRevision,
Status: string(request.Status),
StatusLabel: procurementStatusLabel(request.Status),
BlockingCode: stringValue(request.BlockingCode),
BlockingLabel: procurementBlockingLabel(
stringValue(request.BlockingCode),
),
ReferenceAssetID: stringValue(request.ReferenceAssetID),
PurchaseTaskID: stringValue(request.PurchaseTaskID),
SourceChanged: request.SourceChanged,
}
if request.SourceChanged {
result.StatusLabel = "来源已变化"
}
return result
}
func procurementStatusLabel(
status domain.ProcurementRequestStatus,
) string {
switch status {
case domain.ProcurementBlocked:
return "资料阻塞"
case domain.ProcurementNeedsImage:
return "需要参考图"
case domain.ProcurementReady:
return "可以生成任务"
case domain.ProcurementTaskCreated:
return "任务已生成"
case domain.ProcurementSourceChanged:
return "来源已变化"
default:
return "未知状态"
}
}
func procurementBlockingLabel(code string) string {
switch code {
case domain.ProcurementBlockSourceCanceled:
return "货运单已取消"
case domain.ProcurementBlockTitleRequired:
return "商品标题缺失或超限"
case domain.ProcurementBlockSKURequired:
return "SKU 缺失或超限"
case domain.ProcurementBlockQuantityRequired:
return "数量缺失或无效"
case domain.ProcurementBlockSourceChanged:
return "ERP 来源已更新"
default:
return ""
}
}
func (adapter *UsecaseAdapter) GetFreightSync(
ctx context.Context,
syncID string,
@@ -660,3 +813,4 @@ func (err *adapterError) Unwrap() []error {
var _ Service = (*UsecaseAdapter)(nil)
var _ FreightService = (*UsecaseAdapter)(nil)
var _ ProcurementService = (*UsecaseAdapter)(nil)