demo
This commit is contained in:
@@ -5,12 +5,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.starryskymeow.cn/B309/datamarket/internal/handler"
|
|
||||||
"gitea.starryskymeow.cn/B309/datamarket/internal/repository"
|
"gitea.starryskymeow.cn/B309/datamarket/internal/repository"
|
||||||
"github.com/go-chi/chi/v5"
|
"gitea.starryskymeow.cn/B309/datamarket/internal/router"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,25 +27,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queries := repository.New(pool)
|
queries := repository.New(pool)
|
||||||
|
appService := service.New(queries)
|
||||||
|
r := router.New(appService)
|
||||||
|
|
||||||
// route
|
addr := os.Getenv("SERVER_ADDR")
|
||||||
r := chi.NewRouter()
|
|
||||||
|
|
||||||
r.Use(middleware.RequestID)
|
if addr == "" {
|
||||||
r.Use(middleware.RealIP)
|
addr = ":8080"
|
||||||
r.Use(middleware.Logger)
|
}
|
||||||
r.Use(middleware.Recoverer)
|
|
||||||
r.Use(middleware.Timeout(60 * time.Second))
|
|
||||||
|
|
||||||
r.Route("/api", func(r chi.Router) {
|
err = http.ListenAndServe(addr, r)
|
||||||
r.Route("/assets", func(r chi.Router) {
|
|
||||||
r.Post("/", handler.CreateDataAsset(queries))
|
|
||||||
r.Get("/", handler.ListDataAssets(queries))
|
|
||||||
r.Get("/{id}", handler.GetDataAsset(queries))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
err = http.ListenAndServe(":8080", r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,64 @@
|
|||||||
-- name: CreateDataAsset :one
|
-- name: CreateDataAsset :one
|
||||||
INSERT INTO data_assets (asset_name, asset_type, domain, application_scene,
|
INSERT INTO data_assets (
|
||||||
data_description, data_scale, collection_method, labeling_status,
|
asset_name,
|
||||||
update_frequency, privacy_level, permission_mode, supports_validation,
|
asset_type,
|
||||||
seller_expected_price_min, seller_expected_price_max, asset_status)
|
domain,
|
||||||
VALUES ($1, $2, $3, $4,
|
application_scene,
|
||||||
$5, $6, $7, $8,
|
data_description,
|
||||||
$9, $10, $11, $12,
|
data_scale,
|
||||||
$13, $14, $15)
|
collection_method,
|
||||||
RETURNING *;
|
labeling_status,
|
||||||
|
update_frequency,
|
||||||
|
privacy_level,
|
||||||
|
permission_mode,
|
||||||
|
supports_validation,
|
||||||
|
seller_expected_price_min,
|
||||||
|
seller_expected_price_max,
|
||||||
|
quality_level,
|
||||||
|
scarcity_level,
|
||||||
|
base_value_score,
|
||||||
|
base_price_min,
|
||||||
|
base_price_max,
|
||||||
|
asset_status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
|
||||||
|
$11, $12, $13, $14, $15, $16, $17, $18, $19, $20
|
||||||
|
)
|
||||||
|
RETURNING id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at;
|
||||||
|
|
||||||
|
-- name: CountDataAssets :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM data_assets
|
||||||
|
WHERE (
|
||||||
|
NULLIF(sqlc.narg(keyword)::text, '') IS NULL
|
||||||
|
OR asset_name ILIKE '%' || sqlc.narg(keyword)::text || '%'
|
||||||
|
OR data_description ILIKE '%' || sqlc.narg(keyword)::text || '%'
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF(sqlc.narg(asset_type)::text, '') IS NULL
|
||||||
|
OR asset_type = sqlc.narg(asset_type)::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF(sqlc.narg(domain)::text, '') IS NULL
|
||||||
|
OR domain = sqlc.narg(domain)::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF(sqlc.narg(privacy_level)::text, '') IS NULL
|
||||||
|
OR privacy_level = sqlc.narg(privacy_level)::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
sqlc.narg(supports_validation)::boolean IS NULL
|
||||||
|
OR supports_validation = sqlc.narg(supports_validation)::boolean
|
||||||
|
);
|
||||||
|
|
||||||
-- name: GetDataAsset :one
|
-- name: GetDataAsset :one
|
||||||
SELECT *
|
SELECT id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at
|
||||||
FROM data_assets
|
FROM data_assets
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListDataAssets :many
|
-- name: ListDataAssets :many
|
||||||
SELECT *
|
SELECT id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at
|
||||||
FROM data_assets
|
FROM data_assets
|
||||||
WHERE (
|
WHERE (
|
||||||
NULLIF(sqlc.narg(keyword)::text, '') IS NULL
|
NULLIF(sqlc.narg(keyword)::text, '') IS NULL
|
||||||
@@ -42,46 +84,149 @@ WHERE (
|
|||||||
ORDER BY created_at DESC, id DESC
|
ORDER BY created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: UpdateDataAssetStatus :one
|
||||||
|
UPDATE data_assets
|
||||||
|
SET asset_status = $2,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at;
|
||||||
|
|
||||||
|
-- name: CreateBuyerRequest :one
|
||||||
|
INSERT INTO buyer_requests (
|
||||||
|
asset_id,
|
||||||
|
task_type,
|
||||||
|
model_type,
|
||||||
|
buyer_budget_min,
|
||||||
|
buyer_budget_max,
|
||||||
|
privacy_requirement,
|
||||||
|
usage_purpose,
|
||||||
|
request_note,
|
||||||
|
request_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
|
RETURNING id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at;
|
||||||
|
|
||||||
-- name: GetBuyerRequest :one
|
-- name: GetBuyerRequest :one
|
||||||
SELECT *
|
SELECT id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at
|
||||||
FROM buyer_requests
|
FROM buyer_requests
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListBuyerRequests :many
|
-- name: ListBuyerRequests :many
|
||||||
SELECT *
|
SELECT id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at
|
||||||
FROM buyer_requests
|
FROM buyer_requests
|
||||||
ORDER BY created_at DESC, id DESC
|
ORDER BY created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: CreatePricingResult :one
|
||||||
|
INSERT INTO pricing_results (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
scenario_value_score,
|
||||||
|
scenario_price_min,
|
||||||
|
scenario_price_max,
|
||||||
|
suggested_price,
|
||||||
|
success_probability,
|
||||||
|
pricing_reason_1,
|
||||||
|
pricing_reason_2,
|
||||||
|
pricing_reason_3,
|
||||||
|
verification_suggestion,
|
||||||
|
pricing_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
|
RETURNING id, asset_id, request_id, scenario_value_score, scenario_price_min, scenario_price_max, suggested_price, success_probability, pricing_reason_1, pricing_reason_2, pricing_reason_3, verification_suggestion, pricing_status, created_at, updated_at;
|
||||||
|
|
||||||
-- name: GetPricingResult :one
|
-- name: GetPricingResult :one
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, scenario_value_score, scenario_price_min, scenario_price_max, suggested_price, success_probability, pricing_reason_1, pricing_reason_2, pricing_reason_3, verification_suggestion, pricing_status, created_at, updated_at
|
||||||
FROM pricing_results
|
FROM pricing_results
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListPricingResults :many
|
-- name: ListPricingResults :many
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, scenario_value_score, scenario_price_min, scenario_price_max, suggested_price, success_probability, pricing_reason_1, pricing_reason_2, pricing_reason_3, verification_suggestion, pricing_status, created_at, updated_at
|
||||||
FROM pricing_results
|
FROM pricing_results
|
||||||
ORDER BY created_at DESC, id DESC
|
ORDER BY created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: CreateValidation :one
|
||||||
|
INSERT INTO validations (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
validation_type,
|
||||||
|
validation_requested,
|
||||||
|
validation_status,
|
||||||
|
validation_signal,
|
||||||
|
validation_score,
|
||||||
|
risk_warning,
|
||||||
|
continue_recommendation,
|
||||||
|
validation_finished_at
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
RETURNING id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at;
|
||||||
|
|
||||||
|
-- name: UpdateValidationResult :one
|
||||||
|
UPDATE validations
|
||||||
|
SET validation_status = $2,
|
||||||
|
validation_signal = $3,
|
||||||
|
validation_score = $4,
|
||||||
|
risk_warning = $5,
|
||||||
|
continue_recommendation = $6,
|
||||||
|
validation_finished_at = $7
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at;
|
||||||
|
|
||||||
-- name: GetValidation :one
|
-- name: GetValidation :one
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at
|
||||||
FROM validations
|
FROM validations
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListValidations :many
|
-- name: ListValidations :many
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at
|
||||||
FROM validations
|
FROM validations
|
||||||
ORDER BY validation_created_at DESC, id DESC
|
ORDER BY validation_created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: CountValidations :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM validations;
|
||||||
|
|
||||||
|
-- name: CreateOrder :one
|
||||||
|
INSERT INTO orders (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
pricing_id,
|
||||||
|
validation_id,
|
||||||
|
asset_name,
|
||||||
|
current_price,
|
||||||
|
negotiation_min,
|
||||||
|
negotiation_max,
|
||||||
|
validation_used,
|
||||||
|
delivery_mode,
|
||||||
|
order_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
RETURNING id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at;
|
||||||
|
|
||||||
-- name: GetOrder :one
|
-- name: GetOrder :one
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
||||||
FROM orders
|
FROM orders
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListOrders :many
|
-- name: ListOrders :many
|
||||||
SELECT *
|
SELECT id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
||||||
FROM orders
|
FROM orders
|
||||||
|
WHERE NULLIF(sqlc.narg(order_status)::text, '') IS NULL
|
||||||
|
OR order_status = sqlc.narg(order_status)::text
|
||||||
ORDER BY order_created_at DESC, id DESC
|
ORDER BY order_created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: CountOrders :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM orders
|
||||||
|
WHERE NULLIF(sqlc.narg(order_status)::text, '') IS NULL
|
||||||
|
OR order_status = sqlc.narg(order_status)::text;
|
||||||
|
|
||||||
|
-- name: UpdateOrderStatus :one
|
||||||
|
UPDATE orders
|
||||||
|
SET order_status = $2,
|
||||||
|
order_updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at;
|
||||||
|
|||||||
19
internal/handler/admin.go
Normal file
19
internal/handler/admin.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AdminListAssets(svc service.AssetService) http.HandlerFunc {
|
||||||
|
return ListAssets(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AdminListValidations(svc service.ValidationService) http.HandlerFunc {
|
||||||
|
return ListValidations(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AdminListOrders(svc service.OrderService) http.HandlerFunc {
|
||||||
|
return ListOrders(svc)
|
||||||
|
}
|
||||||
144
internal/handler/assets.go
Normal file
144
internal/handler/assets.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateAssetRequest struct {
|
||||||
|
AssetName string `json:"asset_name"`
|
||||||
|
AssetType string `json:"asset_type"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
ApplicationScene *string `json:"application_scene"`
|
||||||
|
DataDescription string `json:"data_description"`
|
||||||
|
DataScale string `json:"data_scale"`
|
||||||
|
CollectionMethod string `json:"collection_method"`
|
||||||
|
LabelingStatus *string `json:"labeling_status"`
|
||||||
|
UpdateFrequency *string `json:"update_frequency"`
|
||||||
|
PrivacyLevel string `json:"privacy_level"`
|
||||||
|
PermissionMode string `json:"permission_mode"`
|
||||||
|
SupportsValidation bool `json:"supports_validation"`
|
||||||
|
SellerExpectedPriceMin *float64 `json:"seller_expected_price_min"`
|
||||||
|
SellerExpectedPriceMax *float64 `json:"seller_expected_price_max"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CreateAssetRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListAssetsQuery struct {
|
||||||
|
Limit int32 `schema:"limit"`
|
||||||
|
Offset int32 `schema:"offset"`
|
||||||
|
Keyword *string `schema:"keyword"`
|
||||||
|
AssetType *string `schema:"asset_type"`
|
||||||
|
Domain *string `schema:"domain"`
|
||||||
|
PrivacyLevel *string `schema:"privacy_level"`
|
||||||
|
SupportsValidation *bool `schema:"supports_validation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateStatusRequest struct {
|
||||||
|
Status string `json:"asset_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *UpdateStatusRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAsset(svc service.AssetService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &CreateAssetRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.CreateAsset(r.Context(), service.AssetCreateInput{
|
||||||
|
AssetName: req.AssetName,
|
||||||
|
AssetType: req.AssetType,
|
||||||
|
Domain: req.Domain,
|
||||||
|
ApplicationScene: req.ApplicationScene,
|
||||||
|
DataDescription: req.DataDescription,
|
||||||
|
DataScale: req.DataScale,
|
||||||
|
CollectionMethod: req.CollectionMethod,
|
||||||
|
LabelingStatus: req.LabelingStatus,
|
||||||
|
UpdateFrequency: req.UpdateFrequency,
|
||||||
|
PrivacyLevel: req.PrivacyLevel,
|
||||||
|
PermissionMode: req.PermissionMode,
|
||||||
|
SupportsValidation: req.SupportsValidation,
|
||||||
|
SellerExpectedPriceMin: req.SellerExpectedPriceMin,
|
||||||
|
SellerExpectedPriceMax: req.SellerExpectedPriceMax,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusCreated, "资产创建成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListAssets(svc service.AssetService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var query ListAssetsQuery
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.ListAssets(r.Context(), service.AssetListInput{
|
||||||
|
Limit: query.Limit,
|
||||||
|
Offset: query.Offset,
|
||||||
|
Keyword: query.Keyword,
|
||||||
|
AssetType: query.AssetType,
|
||||||
|
Domain: query.Domain,
|
||||||
|
PrivacyLevel: query.PrivacyLevel,
|
||||||
|
SupportsValidation: query.SupportsValidation,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", ListResponse[service.Asset]{
|
||||||
|
List: result.List,
|
||||||
|
Total: result.Total,
|
||||||
|
Limit: result.Limit,
|
||||||
|
Offset: result.Offset,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAsset(svc service.AssetService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result, err := svc.GetAsset(r.Context(), chi.URLParam(r, "id"))
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateAssetStatus(svc service.AssetService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &UpdateStatusRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.UpdateAssetStatus(r.Context(), chi.URLParam(r, "id"), service.StatusUpdate{
|
||||||
|
Status: req.Status,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "状态更新成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
90
internal/handler/assets_test.go
Normal file
90
internal/handler/assets_test.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeAssetService struct {
|
||||||
|
listResult service.ListResult[service.Asset]
|
||||||
|
listErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeAssetService) CreateAsset(context.Context, service.AssetCreateInput) (service.AssetStatusResult, error) {
|
||||||
|
return service.AssetStatusResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeAssetService) GetAsset(context.Context, string) (service.Asset, error) {
|
||||||
|
return service.Asset{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeAssetService) ListAssets(context.Context, service.AssetListInput) (service.ListResult[service.Asset], error) {
|
||||||
|
return f.listResult, f.listErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeAssetService) UpdateAssetStatus(context.Context, string, service.StatusUpdate) (service.AssetStatusResult, error) {
|
||||||
|
return service.AssetStatusResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAssetHandlerRejectsInvalidJSON(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/assets", bytes.NewBufferString("{"))
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
CreateAsset(fakeAssetService{}).ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusBadRequest {
|
||||||
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListAssetsHandlerReturnsListPayload(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/api/assets?limit=5&offset=0", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler := ListAssets(fakeAssetService{
|
||||||
|
listResult: service.ListResult[service.Asset]{
|
||||||
|
List: []service.Asset{
|
||||||
|
{
|
||||||
|
ID: "asset-1",
|
||||||
|
AssetName: "机械臂抓取演示数据集A",
|
||||||
|
AssetType: "视频+轨迹",
|
||||||
|
Domain: "机器人",
|
||||||
|
DataDescription: "包含工业抓取场景的机械臂操作视频与轨迹",
|
||||||
|
DataScale: "1200条轨迹",
|
||||||
|
CollectionMethod: "真实采集",
|
||||||
|
PrivacyLevel: "中",
|
||||||
|
PermissionMode: "授权访问",
|
||||||
|
SupportsValidation: true,
|
||||||
|
AssetStatus: "已上架",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Total: 1,
|
||||||
|
Limit: 5,
|
||||||
|
Offset: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
handler.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body Response[ListResponse[service.Asset]]
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||||
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.Code != 0 {
|
||||||
|
t.Fatalf("code = %d, want 0", body.Code)
|
||||||
|
}
|
||||||
|
if body.Data.Total != 1 || len(body.Data.List) != 1 {
|
||||||
|
t.Fatalf("unexpected payload: %+v", body.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
internal/handler/common.go
Normal file
46
internal/handler/common.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"github.com/gorilla/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response[T any] struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data T `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResponse[T any] struct {
|
||||||
|
List []T `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoder = schema.NewDecoder()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
decoder.IgnoreUnknownKeys(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderSuccess[T any](w http.ResponseWriter, r *http.Request, status int, message string, data T) {
|
||||||
|
render.Status(r, status)
|
||||||
|
render.JSON(w, r, Response[T]{
|
||||||
|
Code: 0,
|
||||||
|
Message: message,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderServiceError(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
status := service.StatusCode(err)
|
||||||
|
render.Status(r, status)
|
||||||
|
render.JSON(w, r, Response[any]{
|
||||||
|
Code: status,
|
||||||
|
Message: service.Message(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"gitea.starryskymeow.cn/B309/datamarket/internal/repository"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
"github.com/go-chi/render"
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CreateDataAssetRequest struct {
|
|
||||||
AssetName string `json:"asset_name"`
|
|
||||||
AssetType string `json:"asset_type"`
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
ApplicationScene pgtype.Text `json:"application_scene"`
|
|
||||||
DataDescription string `json:"data_description"`
|
|
||||||
DataScale string `json:"data_scale"`
|
|
||||||
CollectionMethod string `json:"collection_method"`
|
|
||||||
LabelingStatus pgtype.Text `json:"labeling_status"`
|
|
||||||
UpdateFrequency pgtype.Text `json:"update_frequency"`
|
|
||||||
PrivacyLevel string `json:"privacy_level"`
|
|
||||||
PermissionMode string `json:"permission_mode"`
|
|
||||||
SupportsValidation bool `json:"supports_validation"`
|
|
||||||
SellerExpectedPriceMin pgtype.Numeric `json:"seller_expected_price_min"`
|
|
||||||
SellerExpectedPriceMax pgtype.Numeric `json:"seller_expected_price_max"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req *CreateDataAssetRequest) Bind(r *http.Request) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListDataAssetsParams struct {
|
|
||||||
Limit *int32 `json:"limit" schema:"limit"`
|
|
||||||
Offset *int32 `json:"offset" schema:"offset"`
|
|
||||||
Keyword *string `json:"keyword" schema:"keyword"`
|
|
||||||
AssetType *string `json:"asset_type" schema:"asset_type"`
|
|
||||||
Domain *string `json:"domain" schema:"domain"`
|
|
||||||
PrivacyLevel *string `json:"privacy_level" schema:"privacy_level"`
|
|
||||||
SupportsValidation *bool `json:"supports_validation" schema:"supports_validation"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDataAsset POST /api/assets
|
|
||||||
func CreateDataAsset(queries *repository.Queries) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
req := &CreateDataAssetRequest{}
|
|
||||||
err := render.Bind(r, req)
|
|
||||||
if err != nil {
|
|
||||||
render.Status(r, http.StatusBadRequest)
|
|
||||||
render.JSON(w, r, Response[any]{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
Message: "request data error: " + err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dataAsset, err := queries.CreateDataAsset(r.Context(), repository.CreateDataAssetParams{
|
|
||||||
AssetName: req.AssetName,
|
|
||||||
AssetType: req.AssetType,
|
|
||||||
Domain: req.Domain,
|
|
||||||
ApplicationScene: req.ApplicationScene,
|
|
||||||
DataDescription: req.DataDescription,
|
|
||||||
DataScale: req.DataScale,
|
|
||||||
CollectionMethod: req.CollectionMethod,
|
|
||||||
LabelingStatus: req.LabelingStatus,
|
|
||||||
UpdateFrequency: req.UpdateFrequency,
|
|
||||||
PrivacyLevel: req.PrivacyLevel,
|
|
||||||
PermissionMode: req.PermissionMode,
|
|
||||||
SupportsValidation: req.SupportsValidation,
|
|
||||||
SellerExpectedPriceMin: req.SellerExpectedPriceMin,
|
|
||||||
SellerExpectedPriceMax: req.SellerExpectedPriceMax,
|
|
||||||
AssetStatus: "已上架",
|
|
||||||
})
|
|
||||||
|
|
||||||
render.Status(r, http.StatusCreated)
|
|
||||||
render.JSON(w, r, Response[repository.DataAsset]{
|
|
||||||
Code: 0,
|
|
||||||
Message: "create assets successfully",
|
|
||||||
Data: dataAsset,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDataAsset GET /api/assets/{id}
|
|
||||||
func GetDataAsset(queries *repository.Queries) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var id pgtype.UUID
|
|
||||||
err := id.Scan(chi.URLParam(r, "id"))
|
|
||||||
if err != nil {
|
|
||||||
render.Status(r, http.StatusBadRequest)
|
|
||||||
render.JSON(w, r, Response[any]{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
Message: "request data error: " + err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dataAsset, err := queries.GetDataAsset(r.Context(), id)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "no rows in result set" {
|
|
||||||
render.Status(r, http.StatusNotFound)
|
|
||||||
render.JSON(w, r, Response[any]{
|
|
||||||
Code: http.StatusNotFound,
|
|
||||||
Message: "data_asset not found",
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
render.Status(r, http.StatusInternalServerError)
|
|
||||||
render.JSON(w, r, Response[any]{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
Message: "internal server error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
render.Status(r, http.StatusOK)
|
|
||||||
render.JSON(w, r, Response[repository.DataAsset]{
|
|
||||||
Code: 0,
|
|
||||||
Message: "get asset successfully",
|
|
||||||
Data: dataAsset,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListDataAssets GET /api/assets
|
|
||||||
func ListDataAssets(queries *repository.Queries) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
params := &ListDataAssetsParams{}
|
|
||||||
if err := decoder.Decode(params, r.URL.Query()); err != nil {
|
|
||||||
render.Status(r, http.StatusBadRequest)
|
|
||||||
render.JSON(w, r, Response[any]{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
Message: "request data error: " + err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println(params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import "github.com/gorilla/schema"
|
|
||||||
|
|
||||||
type Response[T any] struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data T `json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var decoder = schema.NewDecoder()
|
|
||||||
114
internal/handler/orders.go
Normal file
114
internal/handler/orders.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateOrderRequest struct {
|
||||||
|
AssetID string `json:"asset_id"`
|
||||||
|
RequestID string `json:"request_id"`
|
||||||
|
PricingID string `json:"pricing_id"`
|
||||||
|
ValidationID *string `json:"validation_id"`
|
||||||
|
CurrentPrice *float64 `json:"current_price"`
|
||||||
|
DeliveryMode string `json:"delivery_mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CreateOrderRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateOrderStatusRequest struct {
|
||||||
|
Status string `json:"order_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *UpdateOrderStatusRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateOrder(svc service.OrderService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &CreateOrderRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.CreateOrder(r.Context(), service.OrderCreateInput{
|
||||||
|
AssetID: req.AssetID,
|
||||||
|
RequestID: req.RequestID,
|
||||||
|
PricingID: req.PricingID,
|
||||||
|
ValidationID: req.ValidationID,
|
||||||
|
CurrentPrice: req.CurrentPrice,
|
||||||
|
DeliveryMode: req.DeliveryMode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusCreated, "订单创建成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrder(svc service.OrderService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result, err := svc.GetOrder(r.Context(), chi.URLParam(r, "id"))
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListOrders(svc service.OrderService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var query ListPageQuery
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.ListOrders(r.Context(), service.OrderListInput{
|
||||||
|
Limit: query.Limit,
|
||||||
|
Offset: query.Offset,
|
||||||
|
OrderStatus: query.OrderStatus,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", ListResponse[service.Order]{
|
||||||
|
List: result.List,
|
||||||
|
Total: result.Total,
|
||||||
|
Limit: result.Limit,
|
||||||
|
Offset: result.Offset,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateOrderStatus(svc service.OrderService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &UpdateOrderStatusRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.UpdateOrderStatus(r.Context(), chi.URLParam(r, "id"), service.StatusUpdate{
|
||||||
|
Status: req.Status,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "订单状态更新成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
internal/handler/pricing.go
Normal file
63
internal/handler/pricing.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreatePricingRequest struct {
|
||||||
|
AssetID string `json:"asset_id"`
|
||||||
|
TaskType string `json:"task_type"`
|
||||||
|
ModelType string `json:"model_type"`
|
||||||
|
BuyerBudgetMin *float64 `json:"buyer_budget_min"`
|
||||||
|
BuyerBudgetMax *float64 `json:"buyer_budget_max"`
|
||||||
|
PrivacyRequirement *string `json:"privacy_requirement"`
|
||||||
|
UsagePurpose *string `json:"usage_purpose"`
|
||||||
|
RequestNote *string `json:"request_note"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CreatePricingRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePricing(svc service.PricingService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &CreatePricingRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.CreatePricing(r.Context(), service.PricingCreateInput{
|
||||||
|
AssetID: req.AssetID,
|
||||||
|
TaskType: req.TaskType,
|
||||||
|
ModelType: req.ModelType,
|
||||||
|
BuyerBudgetMin: req.BuyerBudgetMin,
|
||||||
|
BuyerBudgetMax: req.BuyerBudgetMax,
|
||||||
|
PrivacyRequirement: req.PrivacyRequirement,
|
||||||
|
UsagePurpose: req.UsagePurpose,
|
||||||
|
RequestNote: req.RequestNote,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusCreated, "定价建议生成成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPricing(svc service.PricingService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result, err := svc.GetPricing(r.Context(), chi.URLParam(r, "id"))
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
85
internal/handler/validations.go
Normal file
85
internal/handler/validations.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateValidationRequest struct {
|
||||||
|
AssetID string `json:"asset_id"`
|
||||||
|
RequestID string `json:"request_id"`
|
||||||
|
ValidationType string `json:"validation_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CreateValidationRequest) Bind(_ *http.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListPageQuery struct {
|
||||||
|
Limit int32 `schema:"limit"`
|
||||||
|
Offset int32 `schema:"offset"`
|
||||||
|
OrderStatus string `schema:"order_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateValidation(svc service.ValidationService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &CreateValidationRequest{}
|
||||||
|
if err := render.Bind(r, req); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.CreateValidation(r.Context(), service.ValidationCreateInput{
|
||||||
|
AssetID: req.AssetID,
|
||||||
|
RequestID: req.RequestID,
|
||||||
|
ValidationType: req.ValidationType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusCreated, "验证申请提交成功", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetValidation(svc service.ValidationService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result, err := svc.GetValidation(r.Context(), chi.URLParam(r, "id"))
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListValidations(svc service.ValidationService) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var query ListPageQuery
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
renderServiceError(w, r, service.NewValidationError("request data error: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := svc.ListValidations(r.Context(), service.ValidationListInput{
|
||||||
|
Limit: query.Limit,
|
||||||
|
Offset: query.Offset,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
renderServiceError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuccess(w, r, http.StatusOK, "success", ListResponse[service.Validation]{
|
||||||
|
List: result.List,
|
||||||
|
Total: result.Total,
|
||||||
|
Limit: result.Limit,
|
||||||
|
Offset: result.Offset,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,15 +11,164 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const countDataAssets = `-- name: CountDataAssets :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM data_assets
|
||||||
|
WHERE (
|
||||||
|
NULLIF($1::text, '') IS NULL
|
||||||
|
OR asset_name ILIKE '%' || $1::text || '%'
|
||||||
|
OR data_description ILIKE '%' || $1::text || '%'
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF($2::text, '') IS NULL
|
||||||
|
OR asset_type = $2::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF($3::text, '') IS NULL
|
||||||
|
OR domain = $3::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF($4::text, '') IS NULL
|
||||||
|
OR privacy_level = $4::text
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
$5::boolean IS NULL
|
||||||
|
OR supports_validation = $5::boolean
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CountDataAssetsParams struct {
|
||||||
|
Keyword pgtype.Text `json:"keyword"`
|
||||||
|
AssetType pgtype.Text `json:"asset_type"`
|
||||||
|
Domain pgtype.Text `json:"domain"`
|
||||||
|
PrivacyLevel pgtype.Text `json:"privacy_level"`
|
||||||
|
SupportsValidation pgtype.Bool `json:"supports_validation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CountDataAssets(ctx context.Context, arg CountDataAssetsParams) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countDataAssets,
|
||||||
|
arg.Keyword,
|
||||||
|
arg.AssetType,
|
||||||
|
arg.Domain,
|
||||||
|
arg.PrivacyLevel,
|
||||||
|
arg.SupportsValidation,
|
||||||
|
)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const countOrders = `-- name: CountOrders :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM orders
|
||||||
|
WHERE NULLIF($1::text, '') IS NULL
|
||||||
|
OR order_status = $1::text
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountOrders(ctx context.Context, orderStatus pgtype.Text) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countOrders, orderStatus)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const countValidations = `-- name: CountValidations :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM validations
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountValidations(ctx context.Context) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countValidations)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createBuyerRequest = `-- name: CreateBuyerRequest :one
|
||||||
|
INSERT INTO buyer_requests (
|
||||||
|
asset_id,
|
||||||
|
task_type,
|
||||||
|
model_type,
|
||||||
|
buyer_budget_min,
|
||||||
|
buyer_budget_max,
|
||||||
|
privacy_requirement,
|
||||||
|
usage_purpose,
|
||||||
|
request_note,
|
||||||
|
request_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
|
RETURNING id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateBuyerRequestParams struct {
|
||||||
|
AssetID pgtype.UUID `json:"asset_id"`
|
||||||
|
TaskType string `json:"task_type"`
|
||||||
|
ModelType string `json:"model_type"`
|
||||||
|
BuyerBudgetMin pgtype.Numeric `json:"buyer_budget_min"`
|
||||||
|
BuyerBudgetMax pgtype.Numeric `json:"buyer_budget_max"`
|
||||||
|
PrivacyRequirement pgtype.Text `json:"privacy_requirement"`
|
||||||
|
UsagePurpose pgtype.Text `json:"usage_purpose"`
|
||||||
|
RequestNote pgtype.Text `json:"request_note"`
|
||||||
|
RequestStatus string `json:"request_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateBuyerRequest(ctx context.Context, arg CreateBuyerRequestParams) (BuyerRequest, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createBuyerRequest,
|
||||||
|
arg.AssetID,
|
||||||
|
arg.TaskType,
|
||||||
|
arg.ModelType,
|
||||||
|
arg.BuyerBudgetMin,
|
||||||
|
arg.BuyerBudgetMax,
|
||||||
|
arg.PrivacyRequirement,
|
||||||
|
arg.UsagePurpose,
|
||||||
|
arg.RequestNote,
|
||||||
|
arg.RequestStatus,
|
||||||
|
)
|
||||||
|
var i BuyerRequest
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.TaskType,
|
||||||
|
&i.ModelType,
|
||||||
|
&i.BuyerBudgetMin,
|
||||||
|
&i.BuyerBudgetMax,
|
||||||
|
&i.PrivacyRequirement,
|
||||||
|
&i.UsagePurpose,
|
||||||
|
&i.RequestNote,
|
||||||
|
&i.RequestStatus,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const createDataAsset = `-- name: CreateDataAsset :one
|
const createDataAsset = `-- name: CreateDataAsset :one
|
||||||
INSERT INTO data_assets (asset_name, asset_type, domain, application_scene,
|
INSERT INTO data_assets (
|
||||||
data_description, data_scale, collection_method, labeling_status,
|
asset_name,
|
||||||
update_frequency, privacy_level, permission_mode, supports_validation,
|
asset_type,
|
||||||
seller_expected_price_min, seller_expected_price_max, asset_status)
|
domain,
|
||||||
VALUES ($1, $2, $3, $4,
|
application_scene,
|
||||||
$5, $6, $7, $8,
|
data_description,
|
||||||
$9, $10, $11, $12,
|
data_scale,
|
||||||
$13, $14, $15)
|
collection_method,
|
||||||
|
labeling_status,
|
||||||
|
update_frequency,
|
||||||
|
privacy_level,
|
||||||
|
permission_mode,
|
||||||
|
supports_validation,
|
||||||
|
seller_expected_price_min,
|
||||||
|
seller_expected_price_max,
|
||||||
|
quality_level,
|
||||||
|
scarcity_level,
|
||||||
|
base_value_score,
|
||||||
|
base_price_min,
|
||||||
|
base_price_max,
|
||||||
|
asset_status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
|
||||||
|
$11, $12, $13, $14, $15, $16, $17, $18, $19, $20
|
||||||
|
)
|
||||||
RETURNING id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at
|
RETURNING id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -38,6 +187,11 @@ type CreateDataAssetParams struct {
|
|||||||
SupportsValidation bool `json:"supports_validation"`
|
SupportsValidation bool `json:"supports_validation"`
|
||||||
SellerExpectedPriceMin pgtype.Numeric `json:"seller_expected_price_min"`
|
SellerExpectedPriceMin pgtype.Numeric `json:"seller_expected_price_min"`
|
||||||
SellerExpectedPriceMax pgtype.Numeric `json:"seller_expected_price_max"`
|
SellerExpectedPriceMax pgtype.Numeric `json:"seller_expected_price_max"`
|
||||||
|
QualityLevel pgtype.Text `json:"quality_level"`
|
||||||
|
ScarcityLevel pgtype.Text `json:"scarcity_level"`
|
||||||
|
BaseValueScore pgtype.Numeric `json:"base_value_score"`
|
||||||
|
BasePriceMin pgtype.Numeric `json:"base_price_min"`
|
||||||
|
BasePriceMax pgtype.Numeric `json:"base_price_max"`
|
||||||
AssetStatus string `json:"asset_status"`
|
AssetStatus string `json:"asset_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +211,11 @@ func (q *Queries) CreateDataAsset(ctx context.Context, arg CreateDataAssetParams
|
|||||||
arg.SupportsValidation,
|
arg.SupportsValidation,
|
||||||
arg.SellerExpectedPriceMin,
|
arg.SellerExpectedPriceMin,
|
||||||
arg.SellerExpectedPriceMax,
|
arg.SellerExpectedPriceMax,
|
||||||
|
arg.QualityLevel,
|
||||||
|
arg.ScarcityLevel,
|
||||||
|
arg.BaseValueScore,
|
||||||
|
arg.BasePriceMin,
|
||||||
|
arg.BasePriceMax,
|
||||||
arg.AssetStatus,
|
arg.AssetStatus,
|
||||||
)
|
)
|
||||||
var i DataAsset
|
var i DataAsset
|
||||||
@@ -88,6 +247,203 @@ func (q *Queries) CreateDataAsset(ctx context.Context, arg CreateDataAssetParams
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createOrder = `-- name: CreateOrder :one
|
||||||
|
INSERT INTO orders (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
pricing_id,
|
||||||
|
validation_id,
|
||||||
|
asset_name,
|
||||||
|
current_price,
|
||||||
|
negotiation_min,
|
||||||
|
negotiation_max,
|
||||||
|
validation_used,
|
||||||
|
delivery_mode,
|
||||||
|
order_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
RETURNING id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateOrderParams struct {
|
||||||
|
AssetID pgtype.UUID `json:"asset_id"`
|
||||||
|
RequestID pgtype.UUID `json:"request_id"`
|
||||||
|
PricingID pgtype.UUID `json:"pricing_id"`
|
||||||
|
ValidationID pgtype.UUID `json:"validation_id"`
|
||||||
|
AssetName string `json:"asset_name"`
|
||||||
|
CurrentPrice pgtype.Numeric `json:"current_price"`
|
||||||
|
NegotiationMin pgtype.Numeric `json:"negotiation_min"`
|
||||||
|
NegotiationMax pgtype.Numeric `json:"negotiation_max"`
|
||||||
|
ValidationUsed bool `json:"validation_used"`
|
||||||
|
DeliveryMode string `json:"delivery_mode"`
|
||||||
|
OrderStatus string `json:"order_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateOrder(ctx context.Context, arg CreateOrderParams) (Order, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createOrder,
|
||||||
|
arg.AssetID,
|
||||||
|
arg.RequestID,
|
||||||
|
arg.PricingID,
|
||||||
|
arg.ValidationID,
|
||||||
|
arg.AssetName,
|
||||||
|
arg.CurrentPrice,
|
||||||
|
arg.NegotiationMin,
|
||||||
|
arg.NegotiationMax,
|
||||||
|
arg.ValidationUsed,
|
||||||
|
arg.DeliveryMode,
|
||||||
|
arg.OrderStatus,
|
||||||
|
)
|
||||||
|
var i Order
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.RequestID,
|
||||||
|
&i.PricingID,
|
||||||
|
&i.ValidationID,
|
||||||
|
&i.AssetName,
|
||||||
|
&i.CurrentPrice,
|
||||||
|
&i.NegotiationMin,
|
||||||
|
&i.NegotiationMax,
|
||||||
|
&i.ValidationUsed,
|
||||||
|
&i.DeliveryMode,
|
||||||
|
&i.OrderStatus,
|
||||||
|
&i.OrderCreatedAt,
|
||||||
|
&i.OrderUpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPricingResult = `-- name: CreatePricingResult :one
|
||||||
|
INSERT INTO pricing_results (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
scenario_value_score,
|
||||||
|
scenario_price_min,
|
||||||
|
scenario_price_max,
|
||||||
|
suggested_price,
|
||||||
|
success_probability,
|
||||||
|
pricing_reason_1,
|
||||||
|
pricing_reason_2,
|
||||||
|
pricing_reason_3,
|
||||||
|
verification_suggestion,
|
||||||
|
pricing_status
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
|
RETURNING id, asset_id, request_id, scenario_value_score, scenario_price_min, scenario_price_max, suggested_price, success_probability, pricing_reason_1, pricing_reason_2, pricing_reason_3, verification_suggestion, pricing_status, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreatePricingResultParams struct {
|
||||||
|
AssetID pgtype.UUID `json:"asset_id"`
|
||||||
|
RequestID pgtype.UUID `json:"request_id"`
|
||||||
|
ScenarioValueScore pgtype.Numeric `json:"scenario_value_score"`
|
||||||
|
ScenarioPriceMin pgtype.Numeric `json:"scenario_price_min"`
|
||||||
|
ScenarioPriceMax pgtype.Numeric `json:"scenario_price_max"`
|
||||||
|
SuggestedPrice pgtype.Numeric `json:"suggested_price"`
|
||||||
|
SuccessProbability pgtype.Numeric `json:"success_probability"`
|
||||||
|
PricingReason1 pgtype.Text `json:"pricing_reason_1"`
|
||||||
|
PricingReason2 pgtype.Text `json:"pricing_reason_2"`
|
||||||
|
PricingReason3 pgtype.Text `json:"pricing_reason_3"`
|
||||||
|
VerificationSuggestion pgtype.Text `json:"verification_suggestion"`
|
||||||
|
PricingStatus string `json:"pricing_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreatePricingResult(ctx context.Context, arg CreatePricingResultParams) (PricingResult, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createPricingResult,
|
||||||
|
arg.AssetID,
|
||||||
|
arg.RequestID,
|
||||||
|
arg.ScenarioValueScore,
|
||||||
|
arg.ScenarioPriceMin,
|
||||||
|
arg.ScenarioPriceMax,
|
||||||
|
arg.SuggestedPrice,
|
||||||
|
arg.SuccessProbability,
|
||||||
|
arg.PricingReason1,
|
||||||
|
arg.PricingReason2,
|
||||||
|
arg.PricingReason3,
|
||||||
|
arg.VerificationSuggestion,
|
||||||
|
arg.PricingStatus,
|
||||||
|
)
|
||||||
|
var i PricingResult
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.RequestID,
|
||||||
|
&i.ScenarioValueScore,
|
||||||
|
&i.ScenarioPriceMin,
|
||||||
|
&i.ScenarioPriceMax,
|
||||||
|
&i.SuggestedPrice,
|
||||||
|
&i.SuccessProbability,
|
||||||
|
&i.PricingReason1,
|
||||||
|
&i.PricingReason2,
|
||||||
|
&i.PricingReason3,
|
||||||
|
&i.VerificationSuggestion,
|
||||||
|
&i.PricingStatus,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createValidation = `-- name: CreateValidation :one
|
||||||
|
INSERT INTO validations (
|
||||||
|
asset_id,
|
||||||
|
request_id,
|
||||||
|
validation_type,
|
||||||
|
validation_requested,
|
||||||
|
validation_status,
|
||||||
|
validation_signal,
|
||||||
|
validation_score,
|
||||||
|
risk_warning,
|
||||||
|
continue_recommendation,
|
||||||
|
validation_finished_at
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
RETURNING id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateValidationParams struct {
|
||||||
|
AssetID pgtype.UUID `json:"asset_id"`
|
||||||
|
RequestID pgtype.UUID `json:"request_id"`
|
||||||
|
ValidationType pgtype.Text `json:"validation_type"`
|
||||||
|
ValidationRequested bool `json:"validation_requested"`
|
||||||
|
ValidationStatus string `json:"validation_status"`
|
||||||
|
ValidationSignal pgtype.Text `json:"validation_signal"`
|
||||||
|
ValidationScore pgtype.Numeric `json:"validation_score"`
|
||||||
|
RiskWarning pgtype.Text `json:"risk_warning"`
|
||||||
|
ContinueRecommendation pgtype.Text `json:"continue_recommendation"`
|
||||||
|
ValidationFinishedAt pgtype.Timestamptz `json:"validation_finished_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateValidation(ctx context.Context, arg CreateValidationParams) (Validation, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createValidation,
|
||||||
|
arg.AssetID,
|
||||||
|
arg.RequestID,
|
||||||
|
arg.ValidationType,
|
||||||
|
arg.ValidationRequested,
|
||||||
|
arg.ValidationStatus,
|
||||||
|
arg.ValidationSignal,
|
||||||
|
arg.ValidationScore,
|
||||||
|
arg.RiskWarning,
|
||||||
|
arg.ContinueRecommendation,
|
||||||
|
arg.ValidationFinishedAt,
|
||||||
|
)
|
||||||
|
var i Validation
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.RequestID,
|
||||||
|
&i.ValidationType,
|
||||||
|
&i.ValidationRequested,
|
||||||
|
&i.ValidationStatus,
|
||||||
|
&i.ValidationSignal,
|
||||||
|
&i.ValidationScore,
|
||||||
|
&i.RiskWarning,
|
||||||
|
&i.ContinueRecommendation,
|
||||||
|
&i.ValidationCreatedAt,
|
||||||
|
&i.ValidationFinishedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getBuyerRequest = `-- name: GetBuyerRequest :one
|
const getBuyerRequest = `-- name: GetBuyerRequest :one
|
||||||
SELECT id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at
|
SELECT id, asset_id, task_type, model_type, buyer_budget_min, buyer_budget_max, privacy_requirement, usage_purpose, request_note, request_status, created_at, updated_at
|
||||||
FROM buyer_requests
|
FROM buyer_requests
|
||||||
@@ -372,17 +728,20 @@ func (q *Queries) ListDataAssets(ctx context.Context, arg ListDataAssetsParams)
|
|||||||
const listOrders = `-- name: ListOrders :many
|
const listOrders = `-- name: ListOrders :many
|
||||||
SELECT id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
SELECT id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
||||||
FROM orders
|
FROM orders
|
||||||
|
WHERE NULLIF($3::text, '') IS NULL
|
||||||
|
OR order_status = $3::text
|
||||||
ORDER BY order_created_at DESC, id DESC
|
ORDER BY order_created_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListOrdersParams struct {
|
type ListOrdersParams struct {
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
Offset int32 `json:"offset"`
|
Offset int32 `json:"offset"`
|
||||||
|
OrderStatus pgtype.Text `json:"order_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListOrders(ctx context.Context, arg ListOrdersParams) ([]Order, error) {
|
func (q *Queries) ListOrders(ctx context.Context, arg ListOrdersParams) ([]Order, error) {
|
||||||
rows, err := q.db.Query(ctx, listOrders, arg.Limit, arg.Offset)
|
rows, err := q.db.Query(ctx, listOrders, arg.Limit, arg.Offset, arg.OrderStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -508,3 +867,132 @@ func (q *Queries) ListValidations(ctx context.Context, arg ListValidationsParams
|
|||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateDataAssetStatus = `-- name: UpdateDataAssetStatus :one
|
||||||
|
UPDATE data_assets
|
||||||
|
SET asset_status = $2,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_name, asset_type, domain, application_scene, data_description, data_scale, collection_method, labeling_status, update_frequency, privacy_level, permission_mode, supports_validation, seller_expected_price_min, seller_expected_price_max, quality_level, scarcity_level, base_value_score, base_price_min, base_price_max, asset_status, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateDataAssetStatusParams struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
AssetStatus string `json:"asset_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateDataAssetStatus(ctx context.Context, arg UpdateDataAssetStatusParams) (DataAsset, error) {
|
||||||
|
row := q.db.QueryRow(ctx, updateDataAssetStatus, arg.ID, arg.AssetStatus)
|
||||||
|
var i DataAsset
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetName,
|
||||||
|
&i.AssetType,
|
||||||
|
&i.Domain,
|
||||||
|
&i.ApplicationScene,
|
||||||
|
&i.DataDescription,
|
||||||
|
&i.DataScale,
|
||||||
|
&i.CollectionMethod,
|
||||||
|
&i.LabelingStatus,
|
||||||
|
&i.UpdateFrequency,
|
||||||
|
&i.PrivacyLevel,
|
||||||
|
&i.PermissionMode,
|
||||||
|
&i.SupportsValidation,
|
||||||
|
&i.SellerExpectedPriceMin,
|
||||||
|
&i.SellerExpectedPriceMax,
|
||||||
|
&i.QualityLevel,
|
||||||
|
&i.ScarcityLevel,
|
||||||
|
&i.BaseValueScore,
|
||||||
|
&i.BasePriceMin,
|
||||||
|
&i.BasePriceMax,
|
||||||
|
&i.AssetStatus,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOrderStatus = `-- name: UpdateOrderStatus :one
|
||||||
|
UPDATE orders
|
||||||
|
SET order_status = $2,
|
||||||
|
order_updated_at = now()
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_id, request_id, pricing_id, validation_id, asset_name, current_price, negotiation_min, negotiation_max, validation_used, delivery_mode, order_status, order_created_at, order_updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateOrderStatusParams struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
OrderStatus string `json:"order_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateOrderStatus(ctx context.Context, arg UpdateOrderStatusParams) (Order, error) {
|
||||||
|
row := q.db.QueryRow(ctx, updateOrderStatus, arg.ID, arg.OrderStatus)
|
||||||
|
var i Order
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.RequestID,
|
||||||
|
&i.PricingID,
|
||||||
|
&i.ValidationID,
|
||||||
|
&i.AssetName,
|
||||||
|
&i.CurrentPrice,
|
||||||
|
&i.NegotiationMin,
|
||||||
|
&i.NegotiationMax,
|
||||||
|
&i.ValidationUsed,
|
||||||
|
&i.DeliveryMode,
|
||||||
|
&i.OrderStatus,
|
||||||
|
&i.OrderCreatedAt,
|
||||||
|
&i.OrderUpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateValidationResult = `-- name: UpdateValidationResult :one
|
||||||
|
UPDATE validations
|
||||||
|
SET validation_status = $2,
|
||||||
|
validation_signal = $3,
|
||||||
|
validation_score = $4,
|
||||||
|
risk_warning = $5,
|
||||||
|
continue_recommendation = $6,
|
||||||
|
validation_finished_at = $7
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, asset_id, request_id, validation_type, validation_requested, validation_status, validation_signal, validation_score, risk_warning, continue_recommendation, validation_created_at, validation_finished_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateValidationResultParams struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
ValidationStatus string `json:"validation_status"`
|
||||||
|
ValidationSignal pgtype.Text `json:"validation_signal"`
|
||||||
|
ValidationScore pgtype.Numeric `json:"validation_score"`
|
||||||
|
RiskWarning pgtype.Text `json:"risk_warning"`
|
||||||
|
ContinueRecommendation pgtype.Text `json:"continue_recommendation"`
|
||||||
|
ValidationFinishedAt pgtype.Timestamptz `json:"validation_finished_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateValidationResult(ctx context.Context, arg UpdateValidationResultParams) (Validation, error) {
|
||||||
|
row := q.db.QueryRow(ctx, updateValidationResult,
|
||||||
|
arg.ID,
|
||||||
|
arg.ValidationStatus,
|
||||||
|
arg.ValidationSignal,
|
||||||
|
arg.ValidationScore,
|
||||||
|
arg.RiskWarning,
|
||||||
|
arg.ContinueRecommendation,
|
||||||
|
arg.ValidationFinishedAt,
|
||||||
|
)
|
||||||
|
var i Validation
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AssetID,
|
||||||
|
&i.RequestID,
|
||||||
|
&i.ValidationType,
|
||||||
|
&i.ValidationRequested,
|
||||||
|
&i.ValidationStatus,
|
||||||
|
&i.ValidationSignal,
|
||||||
|
&i.ValidationScore,
|
||||||
|
&i.RiskWarning,
|
||||||
|
&i.ContinueRecommendation,
|
||||||
|
&i.ValidationCreatedAt,
|
||||||
|
&i.ValidationFinishedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|||||||
55
internal/router/router.go
Normal file
55
internal/router/router.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/handler"
|
||||||
|
"gitea.starryskymeow.cn/B309/datamarket/internal/service"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(svc *service.Service) http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
r.Use(middleware.RequestID)
|
||||||
|
r.Use(middleware.RealIP)
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
r.Use(middleware.Recoverer)
|
||||||
|
r.Use(middleware.Timeout(60 * time.Second))
|
||||||
|
|
||||||
|
r.Route("/api", func(r chi.Router) {
|
||||||
|
r.Route("/assets", func(r chi.Router) {
|
||||||
|
r.Post("/", handler.CreateAsset(svc))
|
||||||
|
r.Get("/", handler.ListAssets(svc))
|
||||||
|
r.Get("/{id}", handler.GetAsset(svc))
|
||||||
|
r.Put("/{id}/status", handler.UpdateAssetStatus(svc))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/pricing", func(r chi.Router) {
|
||||||
|
r.Post("/", handler.CreatePricing(svc))
|
||||||
|
r.Get("/{id}", handler.GetPricing(svc))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/validations", func(r chi.Router) {
|
||||||
|
r.Post("/", handler.CreateValidation(svc))
|
||||||
|
r.Get("/{id}", handler.GetValidation(svc))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/orders", func(r chi.Router) {
|
||||||
|
r.Post("/", handler.CreateOrder(svc))
|
||||||
|
r.Get("/", handler.ListOrders(svc))
|
||||||
|
r.Get("/{id}", handler.GetOrder(svc))
|
||||||
|
r.Put("/{id}/status", handler.UpdateOrderStatus(svc))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/admin", func(r chi.Router) {
|
||||||
|
r.Get("/assets", handler.AdminListAssets(svc))
|
||||||
|
r.Get("/validations", handler.AdminListValidations(svc))
|
||||||
|
r.Get("/orders", handler.AdminListOrders(svc))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
65
internal/service/errors.go
Normal file
65
internal/service/errors.go
Normal 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
1020
internal/service/service.go
Normal file
File diff suppressed because it is too large
Load Diff
84
internal/service/service_test.go
Normal file
84
internal/service/service_test.go
Normal 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
197
internal/service/types.go
Normal 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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user