This commit is contained in:
xkm
2026-04-01 17:05:08 +08:00
parent a9a1a1ec24
commit 31bf6330af
17 changed files with 2654 additions and 201 deletions

View File

@@ -0,0 +1,65 @@
package service
import "errors"
type Error struct {
Status int
Message string
Err error
}
func (e *Error) Error() string {
if e == nil {
return ""
}
return e.Message
}
func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.Err
}
func newError(status int, message string, err error) error {
return &Error{
Status: status,
Message: message,
Err: err,
}
}
func badRequest(message string) error {
return newError(400, message, nil)
}
func NewValidationError(message string) error {
return badRequest(message)
}
func notFound(message string) error {
return newError(404, message, nil)
}
func internalError(message string, err error) error {
return newError(500, message, err)
}
func StatusCode(err error) int {
if serviceErr, ok := errors.AsType[*Error](err); ok {
return serviceErr.Status
}
return 500
}
func Message(err error) string {
if serviceErr, ok := errors.AsType[*Error](err); ok {
return serviceErr.Message
}
return "internal server error"
}

1020
internal/service/service.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
package service
import (
"testing"
"gitea.starryskymeow.cn/B309/datamarket/internal/repository"
)
func TestNormalizePage(t *testing.T) {
tests := []struct {
name string
limit int32
offset int32
wantLimit int32
wantOffset int32
}{
{name: "default values", limit: 0, offset: -1, wantLimit: defaultLimit, wantOffset: 0},
{name: "cap max limit", limit: 999, offset: 3, wantLimit: maxLimit, wantOffset: 3},
{name: "keep valid page", limit: 15, offset: 8, wantLimit: 15, wantOffset: 8},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := normalizePage(tt.limit, tt.offset)
if got.Limit != tt.wantLimit || got.Offset != tt.wantOffset {
t.Fatalf("normalizePage() = %+v, want limit=%d offset=%d", got, tt.wantLimit, tt.wantOffset)
}
})
}
}
func TestBuildAssetDerivedFields(t *testing.T) {
input := AssetCreateInput{
AssetName: "机械臂抓取演示数据集A",
AssetType: "视频+轨迹",
Domain: "机器人",
DataDescription: "demo",
DataScale: "1200条轨迹",
CollectionMethod: "真实采集",
LabelingStatus: new("已完整标注"),
PrivacyLevel: "中",
PermissionMode: "授权访问",
SupportsValidation: true,
SellerExpectedPriceMin: new(float64(20000)),
SellerExpectedPriceMax: new(float64(50000)),
}
derived, err := buildAssetDerivedFields(input)
if err != nil {
t.Fatalf("buildAssetDerivedFields() error = %v", err)
}
if derived.QualityLevel != "高" {
t.Fatalf("unexpected quality level: %s", derived.QualityLevel)
}
if derived.ScarcityLevel != "高" {
t.Fatalf("unexpected scarcity level: %s", derived.ScarcityLevel)
}
if derived.BaseValueScore <= 0 {
t.Fatalf("unexpected base value score: %v", derived.BaseValueScore)
}
if derived.BasePriceMin >= derived.BasePriceMax {
t.Fatalf("unexpected base price range: %v - %v", derived.BasePriceMin, derived.BasePriceMax)
}
}
func TestBuildNegotiationRange(t *testing.T) {
pricing := repository.PricingResult{
ScenarioPriceMin: numericValue(30000),
ScenarioPriceMax: numericValue(42000),
}
min, max, err := buildNegotiationRange(pricing, 38000)
if err != nil {
t.Fatalf("buildNegotiationRange() error = %v", err)
}
if min != 34960 {
t.Fatalf("unexpected negotiation min: %v", min)
}
if max != 41040 {
t.Fatalf("unexpected negotiation max: %v", max)
}
}

197
internal/service/types.go Normal file
View File

@@ -0,0 +1,197 @@
package service
import (
"context"
"time"
)
type ListResult[T any] struct {
List []T `json:"list"`
Total int64 `json:"total"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
type Asset struct {
ID string `json:"id"`
AssetName string `json:"asset_name"`
AssetType string `json:"asset_type"`
Domain string `json:"domain"`
ApplicationScene *string `json:"application_scene,omitempty"`
DataDescription string `json:"data_description"`
DataScale string `json:"data_scale"`
CollectionMethod string `json:"collection_method"`
LabelingStatus *string `json:"labeling_status,omitempty"`
UpdateFrequency *string `json:"update_frequency,omitempty"`
PrivacyLevel string `json:"privacy_level"`
PermissionMode string `json:"permission_mode"`
SupportsValidation bool `json:"supports_validation"`
SellerExpectedPriceMin *float64 `json:"seller_expected_price_min,omitempty"`
SellerExpectedPriceMax *float64 `json:"seller_expected_price_max,omitempty"`
QualityLevel *string `json:"quality_level,omitempty"`
ScarcityLevel *string `json:"scarcity_level,omitempty"`
BaseValueScore *float64 `json:"base_value_score,omitempty"`
BasePriceMin *float64 `json:"base_price_min,omitempty"`
BasePriceMax *float64 `json:"base_price_max,omitempty"`
AssetStatus string `json:"asset_status"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type AssetCreateInput struct {
AssetName string
AssetType string
Domain string
ApplicationScene *string
DataDescription string
DataScale string
CollectionMethod string
LabelingStatus *string
UpdateFrequency *string
PrivacyLevel string
PermissionMode string
SupportsValidation bool
SellerExpectedPriceMin *float64
SellerExpectedPriceMax *float64
}
type AssetListInput struct {
Limit int32
Offset int32
Keyword *string
AssetType *string
Domain *string
PrivacyLevel *string
SupportsValidation *bool
}
type StatusUpdate struct {
Status string
}
type AssetStatusResult struct {
AssetID string `json:"asset_id"`
AssetStatus string `json:"asset_status"`
}
type Pricing struct {
ID string `json:"pricing_id"`
AssetID string `json:"asset_id"`
RequestID string `json:"request_id"`
ScenarioValueScore *float64 `json:"scenario_value_score,omitempty"`
ScenarioPriceMin *float64 `json:"scenario_price_min,omitempty"`
ScenarioPriceMax *float64 `json:"scenario_price_max,omitempty"`
SuggestedPrice *float64 `json:"suggested_price,omitempty"`
SuccessProbability *float64 `json:"success_probability,omitempty"`
PricingReason1 *string `json:"pricing_reason_1,omitempty"`
PricingReason2 *string `json:"pricing_reason_2,omitempty"`
PricingReason3 *string `json:"pricing_reason_3,omitempty"`
VerificationSuggestion *string `json:"verification_suggestion,omitempty"`
PricingStatus string `json:"pricing_status"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type PricingCreateInput struct {
AssetID string
TaskType string
ModelType string
BuyerBudgetMin *float64
BuyerBudgetMax *float64
PrivacyRequirement *string
UsagePurpose *string
RequestNote *string
}
type Validation struct {
ID string `json:"validation_id"`
AssetID string `json:"asset_id"`
RequestID string `json:"request_id"`
ValidationType *string `json:"validation_type,omitempty"`
ValidationRequested bool `json:"validation_requested"`
ValidationStatus string `json:"validation_status"`
ValidationSignal *string `json:"validation_signal,omitempty"`
ValidationScore *float64 `json:"validation_score,omitempty"`
RiskWarning *string `json:"risk_warning,omitempty"`
ContinueRecommendation *string `json:"continue_recommendation,omitempty"`
ValidationCreatedAt *time.Time `json:"validation_created_at,omitempty"`
ValidationFinishedAt *time.Time `json:"validation_finished_at,omitempty"`
}
type ValidationCreateInput struct {
AssetID string
RequestID string
ValidationType string
}
type ValidationCreateResult struct {
ValidationID string `json:"validation_id"`
ValidationStatus string `json:"validation_status"`
}
type Order struct {
ID string `json:"order_id"`
AssetID string `json:"asset_id"`
RequestID string `json:"request_id"`
PricingID string `json:"pricing_id"`
ValidationID string `json:"validation_id,omitempty"`
AssetName string `json:"asset_name"`
CurrentPrice *float64 `json:"current_price,omitempty"`
NegotiationMin *float64 `json:"negotiation_min,omitempty"`
NegotiationMax *float64 `json:"negotiation_max,omitempty"`
ValidationUsed bool `json:"validation_used"`
DeliveryMode string `json:"delivery_mode"`
OrderStatus string `json:"order_status"`
OrderCreatedAt *time.Time `json:"order_created_at,omitempty"`
OrderUpdatedAt *time.Time `json:"order_updated_at,omitempty"`
}
type OrderCreateInput struct {
AssetID string
RequestID string
PricingID string
ValidationID *string
CurrentPrice *float64
DeliveryMode string
}
type OrderCreateResult struct {
OrderID string `json:"order_id"`
OrderStatus string `json:"order_status"`
}
type OrderListInput struct {
Limit int32
Offset int32
OrderStatus string
}
type ValidationListInput struct {
Limit int32
Offset int32
}
type AssetService interface {
CreateAsset(context.Context, AssetCreateInput) (AssetStatusResult, error)
GetAsset(context.Context, string) (Asset, error)
ListAssets(context.Context, AssetListInput) (ListResult[Asset], error)
UpdateAssetStatus(context.Context, string, StatusUpdate) (AssetStatusResult, error)
}
type PricingService interface {
CreatePricing(context.Context, PricingCreateInput) (Pricing, error)
GetPricing(context.Context, string) (Pricing, error)
}
type ValidationService interface {
CreateValidation(context.Context, ValidationCreateInput) (ValidationCreateResult, error)
GetValidation(context.Context, string) (Validation, error)
ListValidations(context.Context, ValidationListInput) (ListResult[Validation], error)
}
type OrderService interface {
CreateOrder(context.Context, OrderCreateInput) (OrderCreateResult, error)
GetOrder(context.Context, string) (Order, error)
ListOrders(context.Context, OrderListInput) (ListResult[Order], error)
UpdateOrderStatus(context.Context, string, StatusUpdate) (OrderCreateResult, error)
}