refactor(*): 平台架构 - 拆分后端领域模块并同步插件协议与模型能力

This commit is contained in:
ddCat
2026-09-11 14:02:49 +08:00
parent 916043fb5a
commit 6e5cbc8b05
644 changed files with 62731 additions and 2320 deletions

View File

@@ -0,0 +1,68 @@
package kernel
// 业务信封 code:成功为 0;失败默认等于 HTTP 状态。需要区分同一状态的不同原因时,用 status*100+n。
// 不使用与 HTTP 无关的 1001/2001 号段,避免和现有 42901 以及前端按 status/code 判断重试打架。
const (
CodeOK = 0
CodeInvalidArgument = 400
CodeUnauthorized = 401
CodeForbidden = 403
CodeNotFound = 404
CodeConflict = 409
CodeTooManyRequests = 429
CodeInternal = 500
CodeBadGateway = 502
CodeUnavailable = 503
CodeTimeout = 504
CodeQuotaExceeded = 40301
CodeIdempotencyConflict = 40901
CodeRateLimited = 42901
)
// ErrorReason 是稳定机器可读原因,前端应判断 reason 而不是解析 msg。
type ErrorReason string
const (
ReasonInvalidArgument ErrorReason = "invalid_argument"
ReasonUnauthorized ErrorReason = "unauthorized"
ReasonForbidden ErrorReason = "forbidden"
ReasonNotFound ErrorReason = "not_found"
ReasonConflict ErrorReason = "conflict"
ReasonFailedPrecondition ErrorReason = "failed_precondition"
ReasonQuotaExceeded ErrorReason = "quota_exceeded"
ReasonRateLimited ErrorReason = "rate_limited"
ReasonUnavailable ErrorReason = "unavailable"
ReasonTimeout ErrorReason = "timeout"
ReasonInternal ErrorReason = "internal"
ReasonBadGateway ErrorReason = "bad_gateway"
)
func ReasonForStatus(status int) ErrorReason {
switch status {
case CodeInvalidArgument:
return ReasonInvalidArgument
case CodeUnauthorized:
return ReasonUnauthorized
case CodeForbidden:
return ReasonForbidden
case CodeNotFound:
return ReasonNotFound
case CodeConflict:
return ReasonConflict
case CodeTooManyRequests:
return ReasonRateLimited
case CodeBadGateway:
return ReasonBadGateway
case CodeUnavailable:
return ReasonUnavailable
case CodeTimeout:
return ReasonTimeout
default:
if status >= 500 {
return ReasonInternal
}
return ReasonInvalidArgument
}
}

View File

@@ -0,0 +1,60 @@
package kernel
// AppError 是业务层对外公开的结构化错误。
// Message 必须可安全展示给用户,Cause 仅用于保留内部诊断链路,不得直接写入 HTTP 响应。
type AppError struct {
Status int
Code int
Reason ErrorReason
Message string
Retryable bool
Cause error
}
func (e *AppError) Error() string {
if e == nil {
return ""
}
return e.Message
}
func (e *AppError) Unwrap() error {
if e == nil {
return nil
}
return e.Cause
}
func NewAppError(status int, message string) *AppError {
return &AppError{Status: status, Code: status, Reason: ReasonForStatus(status), Message: message}
}
func WrapAppError(status int, message string, cause error) *AppError {
err := NewAppError(status, message)
err.Cause = cause
return err
}
func RateLimited(message string) *AppError {
return &AppError{Status: CodeTooManyRequests, Code: CodeRateLimited, Reason: ReasonRateLimited, Message: message, Retryable: true}
}
func QuotaExceeded(message string) *AppError {
return &AppError{Status: CodeForbidden, Code: CodeQuotaExceeded, Reason: ReasonQuotaExceeded, Message: message}
}
func BadAuthRequest(message string) *AppError {
return NewAppError(400, message)
}
func NotFound(message string) *AppError {
return NewAppError(404, message)
}
func Unauthorized(message string) *AppError {
return NewAppError(401, message)
}
func Forbidden(message string) *AppError {
return NewAppError(403, message)
}

View File

@@ -0,0 +1,82 @@
package kernel
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"time"
)
// NewID 生成唯一标识符。
func NewID() string {
var b [16]byte
if _, err := rand.Read(b[:]); err != nil {
return fmt.Sprintf("%d", time.Now().UnixNano())
}
return hex.EncodeToString(b[:])
}
// FirstNonEmpty 返回第一个非空字符串。
func FirstNonEmpty(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
return strings.TrimSpace(value)
}
}
return ""
}
// UniqueNonEmpty 去重并过滤空字符串。
func UniqueNonEmpty(values []string) []string {
seen := map[string]bool{}
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" || seen[value] {
continue
}
seen[value] = true
result = append(result, value)
}
return result
}
// DefaultString 返回非空值或回退值。
func DefaultString(value string, fallback string) string {
if strings.TrimSpace(value) == "" {
return fallback
}
return value
}
// Ptr 返回值的指针。
func Ptr[T any](value T) *T {
return &value
}
// StringValue 从任意值提取字符串。
func StringValue(value any) string {
if value == nil {
return ""
}
if text, ok := value.(string); ok {
return text
}
return fmt.Sprintf("%v", value)
}
// TruncateRunes 截断字符串到指定 rune 数。
func TruncateRunes(value string, limit int) string {
runes := []rune(value)
if len(runes) <= limit {
return value
}
return string(runes[:limit]) + "..."
}
// Megabytes 转换 MB 到字节。
func Megabytes(value int64) int64 { return value << 20 }
// Gigabytes 转换 GB 到字节。
func Gigabytes(value int64) int64 { return value << 30 }