- examples/big-suite: E-Commerce 10 functions, 6 layers, real depends_on - provider: nodejs packages now wrapped in ESM zip (buildJSDeployZip) - provider: loadPackageLiteral reads raw file, nodejs zip via source_dir - all 10 functions verified working: python/node/ruby/go runtimes
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type OrderItem struct {
|
|
ProductID string `json:"product_id"`
|
|
Name string `json:"name"`
|
|
Price float64 `json:"price"`
|
|
Qty int `json:"qty"`
|
|
}
|
|
|
|
type OrderRequest struct {
|
|
UserID string `json:"user_id"`
|
|
Items []OrderItem `json:"items"`
|
|
PayToken string `json:"pay_token"`
|
|
Address string `json:"address"`
|
|
}
|
|
|
|
type OrderResponse struct {
|
|
Status string `json:"status"`
|
|
OrderID string `json:"order_id"`
|
|
UserID string `json:"user_id"`
|
|
Items []OrderItem `json:"items"`
|
|
Total float64 `json:"total"`
|
|
CreatedAt string `json:"created_at"`
|
|
EstDays int `json:"estimated_delivery_days"`
|
|
}
|
|
|
|
func generateOrderID() string {
|
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
b := make([]byte, 10)
|
|
for i := range b {
|
|
b[i] = chars[r.Intn(len(chars))]
|
|
}
|
|
return "ORD-" + string(b)
|
|
}
|
|
|
|
func calcTotal(items []OrderItem) float64 {
|
|
total := 0.0
|
|
for _, item := range items {
|
|
total += item.Price * float64(item.Qty)
|
|
}
|
|
// Округление до 2 знаков
|
|
return float64(int(total*100)) / 100
|
|
}
|
|
|
|
func Handler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
if r.Method != http.MethodPost {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
fmt.Fprintln(w, `{"error":"method not allowed"}`)
|
|
return
|
|
}
|
|
|
|
var req OrderRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
fmt.Fprintf(w, `{"error":"invalid JSON: %s"}`, err.Error())
|
|
return
|
|
}
|
|
|
|
if req.UserID == "" {
|
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
fmt.Fprintln(w, `{"error":"user_id required"}`)
|
|
return
|
|
}
|
|
if len(req.Items) == 0 {
|
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
fmt.Fprintln(w, `{"error":"items cannot be empty"}`)
|
|
return
|
|
}
|
|
if req.PayToken == "" {
|
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
fmt.Fprintln(w, `{"error":"pay_token required"}`)
|
|
return
|
|
}
|
|
|
|
resp := OrderResponse{
|
|
Status: "created",
|
|
OrderID: generateOrderID(),
|
|
UserID: req.UserID,
|
|
Items: req.Items,
|
|
Total: calcTotal(req.Items),
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
|
EstDays: 3 + rand.Intn(5),
|
|
}
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(resp)
|
|
}
|