Files
cmroubao/backend-api/internal/usecase/errors.go
T

135 lines
3.0 KiB
Go
Raw Normal View History

package usecase
import (
"errors"
"fmt"
)
type ErrorKind string
const (
ErrorKindInvalid ErrorKind = "INVALID"
ErrorKindNotFound ErrorKind = "NOT_FOUND"
ErrorKindConflict ErrorKind = "CONFLICT"
ErrorKindUnavailable ErrorKind = "UNAVAILABLE"
ErrorKindInternal ErrorKind = "INTERNAL"
)
type Error struct {
Kind ErrorKind
Code string
Message string
Retryable bool
Fields map[string]string
Cause error
}
func (e *Error) Error() string {
if e.Message != "" {
return e.Message
}
return "use case failed"
}
func (e *Error) Unwrap() error {
return e.Cause
}
func newError(
kind ErrorKind,
code string,
message string,
cause error,
) *Error {
return &Error{
Kind: kind,
Code: code,
Message: message,
Cause: cause,
Fields: map[string]string{},
}
}
func invalidError(code, message string, fields map[string]string) *Error {
return &Error{
Kind: ErrorKindInvalid,
Code: code,
Message: message,
Fields: fields,
}
}
var (
ErrRepositoryNotFound = errors.New("repository resource not found")
ErrIdempotencyConflict = errors.New("idempotency key payload conflict")
ErrSourceReferenceConflict = errors.New("source reference conflict")
ErrAssetUnavailable = errors.New("asset is unavailable")
ErrTaskStateConflict = errors.New("task state conflict")
2026-07-29 16:10:45 +08:00
ErrFreightOrderInUse = errors.New("freight order is in use")
ErrRepositoryUnavailable = errors.New("repository unavailable")
ErrRepositoryInvariant = errors.New("repository invariant failed")
)
func wrapRepositoryError(err error) error {
switch {
case errors.Is(err, ErrRepositoryNotFound):
return newError(
ErrorKindNotFound,
"RESOURCE_NOT_FOUND",
"resource not found",
err,
)
case errors.Is(err, ErrIdempotencyConflict):
return newError(
ErrorKindConflict,
"IDEMPOTENCY_KEY_CONFLICT",
"idempotency key was already used for a different request",
err,
)
case errors.Is(err, ErrSourceReferenceConflict):
return newError(
ErrorKindConflict,
"TASK_SOURCE_REF_CONFLICT",
"source reference already exists",
err,
)
case errors.Is(err, ErrAssetUnavailable):
return newError(
ErrorKindConflict,
"TASK_ASSET_UNAVAILABLE",
"reference image is not available for this task",
err,
)
case errors.Is(err, ErrTaskStateConflict):
return newError(
ErrorKindConflict,
"TASK_STATE_CONFLICT",
"task state does not allow this operation",
err,
)
2026-07-29 16:10:45 +08:00
case errors.Is(err, ErrFreightOrderInUse):
return newError(
ErrorKindConflict,
"FREIGHT_ORDER_IN_USE",
"freight order is already used by procurement",
err,
)
case errors.Is(err, ErrRepositoryUnavailable):
result := newError(
ErrorKindUnavailable,
"STORAGE_UNAVAILABLE",
"storage is temporarily unavailable",
err,
)
result.Retryable = true
return result
default:
return newError(
ErrorKindInternal,
"INTERNAL_ERROR",
"internal server error",
fmt.Errorf("%w: %v", ErrRepositoryInvariant, err),
)
}
}