go: Bounded Parallelism Pattern

postgreSQL

sql 复制代码
-- 创建 orders 表
CREATE TABLE orders (
    id VARCHAR(36) PRIMARY KEY,
    customer_id VARCHAR(36) NOT NULL,
    status VARCHAR(20) NOT NULL,
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL,
    gold_price DECIMAL(10, 2) NOT NULL,
    currency VARCHAR(3) NOT NULL
);
 
-- 创建 order_items 表
CREATE TABLE order_items (
    id SERIAL PRIMARY KEY,
    order_id VARCHAR(36) REFERENCES orders(id),
    product_id VARCHAR(50) NOT NULL,
    weight_grams DECIMAL(10, 3) NOT NULL,
    purity DECIMAL(4, 3) NOT NULL,
    subtotal DECIMAL(12, 2) NOT NULL
);

项目结构:

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:13
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : bounded_executor.go
*/
package concurrency
 
import (
    "context"
    "sync"
)
 
type BoundedExecutor struct {
    workerCount int
    sem         chan struct{}
    wg          sync.WaitGroup
}
 
func NewBoundedExecutor(maxWorkers int) *BoundedExecutor {
    return &BoundedExecutor{
        workerCount: maxWorkers,
        sem:         make(chan struct{}, maxWorkers),
    }
}
 
func (e *BoundedExecutor) Submit(task func()) {
    e.wg.Add(1)
 
    go func() {
        defer e.wg.Done()
 
        // 等待获取信号量
        select {
        case e.sem <- struct{}{}:
            defer func() { <-e.sem }()
            task()
        case <-context.Background().Done():
            return
        }
    }()
}
 
func (e *BoundedExecutor) Shutdown(ctx context.Context) error {
    done := make(chan struct{})
 
    go func() {
        e.wg.Wait()
        close(done)
    }()
 
    select {
    case <-done:
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# github.com/prometheus/client_golang/prometheus
# go get github.com/prometheus/client_golang/prometheus
# go get github.com/prometheus/client_golang/prometheus/promauto
# go get github.com/prometheus/client_golang/prometheus/promhttp
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:14
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : prometheus.go
*/
package metrics
 
import (
    "github.com/prometheus/client_golang/prometheus"
    "sync/atomic"
    "time"
)
 
var (
    workerPoolGauge = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "worker_pool_active_workers",
            Help: "当前活跃的工作线程数",
        },
        []string{"pool"},
    )
 
    queueLengthGauge = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "worker_pool_queue_length",
            Help: "当前任务队列长度",
        },
        []string{"pool"},
    )
 
    tasksCounter = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "worker_pool_tasks_total",
            Help: "处理的任务总数",
        },
        []string{"pool", "result"}, // result: success, failure
    )
 
    taskDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "worker_pool_task_duration_seconds",
            Help:    "任务处理耗时分布",
            Buckets: []float64{0.001, 0.01, 0.1, 0.5, 1.0, 2.5, 5.0, 10.0},
        },
        []string{"pool"},
    )
)
 
func init() {
    prometheus.MustRegister(workerPoolGauge)
    prometheus.MustRegister(queueLengthGauge)
    prometheus.MustRegister(tasksCounter)
    prometheus.MustRegister(taskDuration)
}
 
type PoolMetrics struct {
    Name           string
    poolName       string
    activeWorkers  int32
    completedTasks int32
    queuedTasks    int32
    QueueLength    int
    queueLength    int32
    rejectedTasks  int32
    ActiveWorkers  int
    QueueCapacity  int
    CompletedTasks int
    RejectedTasks  int
}
 
func NewPoolMetrics(poolName string) *PoolMetrics {
    return &PoolMetrics{poolName: poolName}
}
 
func (m *PoolMetrics) UpdateActiveWorkers(count int) {
    workerPoolGauge.WithLabelValues(m.poolName).Set(float64(count))
}
 
func (m *PoolMetrics) UpdateQueueLength(length, capacity int) {
    queueLengthGauge.WithLabelValues(m.poolName).Set(float64(length))
}
 
func (m *PoolMetrics) RecordTaskCompletion(success bool, duration time.Duration) {
    result := "success"
    if !success {
        result = "failure"
    }
 
    tasksCounter.WithLabelValues(m.poolName, result).Add(1)
    taskDuration.WithLabelValues(m.poolName).Observe(duration.Seconds())
}
 
func (m *PoolMetrics) RecordTaskSubmission() {
    // 可以添加任务提交的指标
}
func (m *PoolMetrics) RecordTaskStarted() {
    atomic.AddInt32(&m.activeWorkers, 1)
}
 
func (m *PoolMetrics) RecordTaskCompleted() {
    atomic.AddInt32(&m.activeWorkers, -1)
    atomic.AddInt32(&m.completedTasks, 1)
}
 
func (m *PoolMetrics) RecordTaskQueued() {
    atomic.AddInt32(&m.queuedTasks, 1)
}
 
func (m *PoolMetrics) RecordTaskRejected() {
    atomic.AddInt32(&m.rejectedTasks, 1)
}
 
/*
    func (m *PoolMetrics) Snapshot() PoolMetrics {
        return PoolMetrics{
            Name:           m.Name,
            ActiveWorkers:  int(atomic.LoadInt32(&m.activeWorkers)),
            QueueLength:    int(atomic.LoadInt32(&m.queuedTasks)),
            QueueCapacity:  m.QueueCapacity,
            CompletedTasks: int(atomic.LoadInt32(&m.completedTasks)),
            RejectedTasks:  int(atomic.LoadInt32(&m.rejectedTasks)),
        }
    }
*/
func (m *PoolMetrics) Snapshot() PoolMetrics {
    return PoolMetrics{
        Name:           m.Name,
        ActiveWorkers:  int(atomic.LoadInt32(&m.activeWorkers)),
        QueueLength:    int(atomic.LoadInt32(&m.queueLength)),
        QueueCapacity:  m.QueueCapacity,
        CompletedTasks: int(atomic.LoadInt32(&m.completedTasks)),
        RejectedTasks:  int(atomic.LoadInt32(&m.rejectedTasks)),
    }
}
Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 21:56
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : gold_price.go
*/
package gold
 
import (
    "context"
    "time"
)
 
type GoldPrice struct {
    Amount    float64   // 每克价格
    Currency  string    // 货币代码
    Timestamp time.Time // 价格时间戳
    Source    string    // 数据来源
}
 
type PriceClient interface {
    FetchCurrentPrice(ctx context.Context) (*GoldPrice, error)
    FetchHistoricalPrices(ctx context.Context, startDate, endDate time.Time) ([]*GoldPrice, error)
}
 
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 21:57
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : base_calculator.go
*/
package calculator
 
import (
    "godesginpattern/boundedparallelism/domain/order"
)
 
type BaseCalculator struct{}
 
func (c *BaseCalculator) CalculateTotalWeight(items []order.OrderItem) float64 {
    var total float64
    for _, item := range items {
        total += item.WeightGrams
    }
    return total
}
 
func (c *BaseCalculator) CalculatePurityAdjustedWeight(items []order.OrderItem) float64 {
    var total float64
    for _, item := range items {
        total += item.WeightGrams * item.Purity
    }
    return total
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 21:59
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : vip_calculator.go
*/
package calculator
 
import "godesginpattern/boundedparallelism/domain/order"
 
type VIPCalculator struct {
    BaseCalculator
}
 
func NewVIPCalculator() *VIPCalculator {
    return &VIPCalculator{}
}
 
func (c *VIPCalculator) CalculateTotalWeight(items []order.OrderItem) float64 {
    // VIP客户享受1%的重量折扣
    baseWeight := c.BaseCalculator.CalculateTotalWeight(items)
    return baseWeight * 0.99
}
 
func (c *VIPCalculator) CalculatePurityAdjustedWeight(items []order.OrderItem) float64 {
    // VIP客户享受1.5%的纯度调整折扣
    baseWeight := c.BaseCalculator.CalculatePurityAdjustedWeight(items)
    return baseWeight * 0.985
}
 
func (c *VIPCalculator) ApplyVIPDiscount(total float64) float64 {
    return total * 0.95 // 5%折扣
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:01
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order.go
*/
package order
 
import (
    "errors"
    "fmt"
    "godesginpattern/boundedparallelism/domain/gold"
    "math/rand"
    "time"
)
 
var (
    ErrInvalidWeight     = errors.New("无效的黄金重量")
    ErrInvalidPurity     = errors.New("无效的黄金纯度")
    ErrEmptyItems        = errors.New("订单必须包含至少一个商品")
    ErrUnsupportedPurity = errors.New("不支持的黄金纯度")
)
 
type Order struct {
    ID         string
    CustomerID string
    Items      []OrderItem
    IsVIP      bool
    Status     string
    CreatedAt  time.Time
    UpdatedAt  time.Time
    GoldPrice  *gold.GoldPrice
}
 
type OrderItem struct {
    ProductID   string
    WeightGrams float64
    Purity      float64
    Subtotal    float64
}
 
func NewOrder(
    customerID string,
    items []OrderItem,
    isVIP bool,
    goldPrice *gold.GoldPrice,
) (*Order, error) {
    if customerID == "" {
        return nil, errors.New("客户ID不能为空")
    }
 
    if len(items) == 0 {
        return nil, ErrEmptyItems
    }
 
    // 验证订单项
    for _, item := range items {
        if item.WeightGrams <= 0 {
            return nil, ErrInvalidWeight
        }
        if item.Purity < 0.5 || item.Purity > 1.0 {
            return nil, ErrInvalidPurity
        }
    }
 
    return &Order{
        ID:         generateOrderID(),
        CustomerID: customerID,
        Items:      items,
        IsVIP:      isVIP,
        Status:     "PENDING",
        CreatedAt:  time.Now(),
        UpdatedAt:  time.Now(),
        GoldPrice:  goldPrice,
    }, nil
}
 
func (o *Order) CalculateTotal() (float64, error) {
    var total float64
 
    if o.GoldPrice == nil || o.GoldPrice.Amount <= 0 {
        return 0, errors.New("无效的金价数据")
    }
 
    for i, item := range o.Items {
        // 计算每件商品价格:重量(克) * 纯度 * 金价(每克)
        subtotal := item.WeightGrams * item.Purity * o.GoldPrice.Amount
        o.Items[i].Subtotal = subtotal
        total += subtotal
    }
 
    // VIP客户享受95折
    if o.IsVIP {
        total = total * 0.95
    }
 
    // 添加10%增值税
    total = total * 1.10
 
    return total, nil
}
 
func (o *Order) UpdateStatus(newStatus string) error {
    validStatuses := map[string]bool{
        "PENDING":    true,
        "PROCESSING": true,
        "COMPLETED":  true,
        "CANCELLED":  true,
    }
 
    if !validStatuses[newStatus] {
        return errors.New("无效的订单状态")
    }
 
    o.Status = newStatus
    o.UpdatedAt = time.Now()
    return nil
}
 
func generateOrderID() string {
    // 实际生产中应使用更可靠的ID生成器
    return "ORD-" + time.Now().Format("20060102") + "-" +
        fmt.Sprintf("%06d", rand.Intn(999999))
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:00
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order_repository.go
*/
package order
 
import "context"
 
type Repository interface {
    Save(ctx context.Context, order *Order) error
    FindByID(ctx context.Context, id string) (*Order, error)
    FindByCustomer(ctx context.Context, customerID string, limit, offset int) ([]*Order, error)
    UpdateStatus(ctx context.Context, id, status string) error
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 21:55
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order_validator.go
*/
package order
 
import (
    "errors"
    //"godesginpattern/boundedparallelism/domain/order"
    "regexp"
    "time"
)
 
var (
    ErrInvalidCustomerID = errors.New("无效的客户ID格式")
    ErrInvalidProductID  = errors.New("无效的产品ID格式")
)
 
type OrderValidator struct {
    customerIDRegex *regexp.Regexp
    productIDRegex  *regexp.Regexp
}
 
type OrderRequest struct {
    // 字段定义
    ID         string
    CustomerID string
    Items      []OrderItem
    Timestamp  time.Time
    UserID     string
    IsVIP      bool // 添加缺失的字段
    // 其他必要字段
}
 
func NewOrderValidator() *OrderValidator {
    return &OrderValidator{
        customerIDRegex: regexp.MustCompile(`^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$`),
        productIDRegex:  regexp.MustCompile(`^PROD-[A-Z0-9]{6}$`),
    }
}
 
func (v *OrderValidator) Validate(req *OrderRequest) error {
    // 验证客户ID格式
    if !v.customerIDRegex.MatchString(req.CustomerID) {
        return ErrInvalidCustomerID
    }
 
    // 验证订单项
    if len(req.Items) == 0 {
        return errors.New("订单必须包含至少一个商品")
    }
 
    for _, item := range req.Items {
        if !v.productIDRegex.MatchString(item.ProductID) {
            return ErrInvalidProductID
        }
 
        if item.WeightGrams <= 0 {
            return errors.New("商品重量必须大于0")
        }
 
        if item.Purity < 0.5 || item.Purity > 1.0 {
            return errors.New("黄金纯度必须在0.5到1.0之间")
        }
    }
 
    return nil
}
Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
#  github.com/go-playground/validator/v10
#  go get github.com/go-playground/validator/v10
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:12
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order_handler.go
*/
package http
 
import (
    "context"
    "encoding/json"
    "github.com/go-playground/validator/v10"
    "godesginpattern/boundedparallelism/application/order"
    "godesginpattern/boundedparallelism/application/order/dto"
    "log"
    "net/http"
    "time"
)
 
type OrderHandler struct {
    processor *order.OrderProcessor
    validator *validator.Validate
}
 
func NewOrderHandler(processor *order.OrderProcessor) *OrderHandler {
    return &OrderHandler{
        processor: processor,
        validator: validator.New(),
    }
}
 
func (h *OrderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodPost:
        if r.URL.Path == "/orders" {
            h.createOrder(w, r)
            return
        }
    }
 
    http.NotFound(w, r)
}
 
func (h *OrderHandler) createOrder(w http.ResponseWriter, r *http.Request) {
    var req dto.CreateOrderRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        h.handleError(w, http.StatusBadRequest, "无效的请求体")
        return
    }
 
    // 验证请求数据
    if err := h.validator.Struct(req); err != nil {
        h.handleError(w, http.StatusBadRequest, "请求数据验证失败: "+err.Error())
        return
    }
 
    // 处理订单
    ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
    defer cancel()
 
    response, err := h.processor.ProcessOrder(ctx, &order.OrderRequest{
        CustomerID: req.CustomerID,
        Items:      convertToDomainItems(req.Items),
        IsVIP:      req.IsVIP,
    })
 
    if err != nil {
        h.handleError(w, http.StatusInternalServerError, "订单处理失败: "+err.Error())
        return
    }
 
    // 转换为DTO响应
    dtoResponse := dto.OrderResponseDTO{
        OrderID:   response.OrderID,
        Total:     response.Total,
        Status:    response.Status,
        CreatedAt: response.Created,
    }
 
    // 填充订单项
    for _, item := range req.Items {
        dtoResponse.Items = append(dtoResponse.Items, struct {
            ProductID   string  `json:"product_id"`
            WeightGrams float64 `json:"weight_grams"`
            Purity      float64 `json:"purity"`
            Subtotal    float64 `json:"subtotal"`
        }{
            ProductID:   item.ProductID,
            WeightGrams: item.WeightGrams,
            Purity:      item.Purity,
            Subtotal:    calculateSubtotal(item, response.Total),
        })
    }
 
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    if err := json.NewEncoder(w).Encode(dtoResponse); err != nil {
        log.Printf("响应编码失败: %v", err)
    }
}
 
func (h *OrderHandler) handleError(w http.ResponseWriter, status int, message string) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(map[string]string{
        "error": message,
    })
}
 
func convertToDomainItems(items []dto.OrderItemDTO) []order.OrderItem {
    domainItems := make([]order.OrderItem, len(items))
    for i, item := range items {
        domainItems[i] = order.OrderItem{
            ProductID:   item.ProductID,
            WeightGrams: item.WeightGrams,
            Purity:      item.Purity,
        }
    }
    return domainItems
}
 
func calculateSubtotal(item dto.OrderItemDTO, total float64) float64 {
    // 简化实现,实际应基于金价计算
    return item.WeightGrams * item.Purity * 60.0
}
  
Go 复制代码
package concurrency
 
import (
    "context"
    "errors"
    "godesginpattern/boundedparallelism/common/concurrency"
    "godesginpattern/boundedparallelism/common/metrics"
    "sync"
    "time"
)
 
var (
    ErrQueueFull = errors.New("任务队列已满")
)
 
type BoundedWorkerPool struct {
    queue        chan func()
    workerPool   *concurrency.BoundedExecutor
    metrics      *metrics.PoolMetrics
    shutdownOnce sync.Once
}
 
func NewBoundedWorkerPool(workerCount, queueCapacity int) *BoundedWorkerPool {
    pool := &BoundedWorkerPool{
        queue:      make(chan func(), queueCapacity),
        metrics:    metrics.NewPoolMetrics("bounded_worker_pool"),
        workerPool: concurrency.NewBoundedExecutor(workerCount),
    }
 
    for i := 0; i < workerCount; i++ {
        pool.workerPool.Submit(pool.worker)
    }
 
    return pool
}
 
func (p *BoundedWorkerPool) worker() {
    for task := range p.queue {
        p.metrics.RecordTaskStarted()
        task()
        p.metrics.RecordTaskCompleted()
    }
}
 
func (p *BoundedWorkerPool) Submit(task func()) error {
    select {
    case p.queue <- task:
        p.metrics.RecordTaskQueued()
        return nil
    default:
        p.metrics.RecordTaskRejected()
        return ErrQueueFull
    }
}
 
func (p *BoundedWorkerPool) GetMetrics() metrics.PoolMetrics {
    return p.metrics.Snapshot()
}
 
func (p *BoundedWorkerPool) Shutdown(ctx context.Context) error {
    var err error
    p.shutdownOnce.Do(func() {
        close(p.queue)
        shutdownCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
        defer cancel()
        err = p.workerPool.Shutdown(shutdownCtx)
    })
    return err
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:09
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : metrics_collector.go
*/
package concurrency
 
import (
    "sync/atomic"
    "time"
)
 
/**/
type PoolMetrics struct {
    activeWorkers int32
    queueLength   int32
 
    queueCapacity    int
    QueueLength      int
    completedTasks   uint64
    failedTasks      uint64
    totalWaitTime    time.Duration
    totalProcessTime time.Duration
}
 
func NewPoolMetrics() *PoolMetrics {
    return &PoolMetrics{
        queueCapacity: 0,
    }
}
 
func (m *PoolMetrics) RecordTaskQueued() {
    atomic.AddInt32(&m.queueLength, 1)
}
 
func (m *PoolMetrics) RecordTaskDequeued() {
    atomic.AddInt32(&m.queueLength, -1)
}
 
func (m *PoolMetrics) UpdateQueueLength(length int) {
    m.QueueLength = length // 使用 QueueLength 而非 queueLength
}
 
func (m *PoolMetrics) RecordTaskStarted() {
    atomic.AddInt32(&m.activeWorkers, 1)
}
 
func (m *PoolMetrics) RecordTaskCompleted(processTime time.Duration) {
    atomic.AddInt32(&m.activeWorkers, -1)
    atomic.AddUint64(&m.completedTasks, 1)
    atomic.AddInt64((*int64)(&m.totalProcessTime), int64(processTime))
}
 
func (m *PoolMetrics) RecordTaskRejected() {
    atomic.AddUint64(&m.failedTasks, 1)
}
 
func (m *PoolMetrics) RecordWaitTime(waitTime time.Duration) {
    atomic.AddInt64((*int64)(&m.totalWaitTime), int64(waitTime))
}
 
func (m *PoolMetrics) Snapshot() PoolMetricsSnapshot {
    return PoolMetricsSnapshot{
        ActiveWorkers:    int(atomic.LoadInt32(&m.activeWorkers)),
        QueueLength:      int(atomic.LoadInt32(&m.queueLength)),
        QueueCapacity:    m.queueCapacity,
        CompletedTasks:   atomic.LoadUint64(&m.completedTasks),
        FailedTasks:      atomic.LoadUint64(&m.failedTasks),
        TotalWaitTime:    m.totalWaitTime,
        TotalProcessTime: m.totalProcessTime,
    }
}
 
type PoolMetricsSnapshot struct {
    ActiveWorkers    int
    QueueLength      int
    QueueCapacity    int
    CompletedTasks   uint64
    FailedTasks      uint64
    TotalWaitTime    time.Duration
    TotalProcessTime time.Duration
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:09
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : priority_queue.go
*/
package concurrency
 
import (
    "container/heap"
    "sync"
    "time"
)
 
type Priority int
 
const (
    LowPriority Priority = iota
    MediumPriority
    HighPriority
)
 
type Task struct {
    Priority    Priority
    SubmitTime  int64
    ExecuteFunc func()
}
 
type PriorityQueue struct {
    items []*Task
    mu    sync.Mutex
}
 
func (pq *PriorityQueue) Len() int { return len(pq.items) }
 
func (pq *PriorityQueue) Less(i, j int) bool {
    // 高优先级先执行,同优先级按提交时间排序
    if pq.items[i].Priority == pq.items[j].Priority {
        return pq.items[i].SubmitTime < pq.items[j].SubmitTime
    }
    return pq.items[i].Priority > pq.items[j].Priority
}
 
func (pq *PriorityQueue) Swap(i, j int) {
    pq.items[i], pq.items[j] = pq.items[j], pq.items[i]
}
 
func (pq *PriorityQueue) Push(x interface{}) {
    item := x.(*Task)
    pq.items = append(pq.items, item)
}
 
func (pq *PriorityQueue) Pop() interface{} {
    old := pq.items
    n := len(old)
    item := old[n-1]
    old[n-1] = nil
    pq.items = old[0 : n-1]
    return item
}
 
func (pq *PriorityQueue) Enqueue(task *Task) {
    pq.mu.Lock()
    defer pq.mu.Unlock()
 
    task.SubmitTime = time.Now().UnixNano()
    heap.Push(pq, task)
}
 
func (pq *PriorityQueue) Dequeue() *Task {
    pq.mu.Lock()
    defer pq.mu.Unlock()
 
    if pq.Len() == 0 {
        return nil
    }
 
    return heap.Pop(pq).(*Task)
}
 
func (pq *PriorityQueue) Peek() *Task {
    pq.mu.Lock()
    defer pq.mu.Unlock()
 
    if pq.Len() == 0 {
        return nil
    }
 
    return pq.items[0]
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# github.com/joho/godotenv
# go get github.com/joho/godotenv
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:10
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : config_loader.go
*/
package config
 
import (
    "github.com/joho/godotenv"
    "os"
    "strconv"
)
 
type Config struct {
    ServerAddress   string
    DatabaseURL     string
    GoldAPIKey      string
    GoldAPIEndpoint string
    WorkerPoolSize  int
    QueueCapacity   int
}
 
func LoadConfig() (*Config, error) {
    // 尝试加载 .env 文件(仅在开发环境)
    _ = godotenv.Load()
 
    workerPoolSize, _ := strconv.Atoi(getEnv("WORKER_POOL_SIZE", "10"))
    queueCapacity, _ := strconv.Atoi(getEnv("QUEUE_CAPACITY", "100"))
 
    return &Config{
        ServerAddress:   getEnv("SERVER_ADDRESS", ":8082"),
        DatabaseURL:     getEnv("DATABASE_URL", "postgres://postgres:geovindu@localhost:5432/jewelry?sslmode=disable"),
        GoldAPIKey:      getEnv("GOLD_API_KEY", ""),
        GoldAPIEndpoint: getEnv("GOLD_API_ENDPOINT", ""),
        WorkerPoolSize:  workerPoolSize,
        QueueCapacity:   queueCapacity,
    }, nil
}
 
func getEnv(key, defaultValue string) string {
    if value, exists := os.LookupEnv(key); exists {
        return value
    }
    return defaultValue
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:09
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : lbma_client.go
*/
package goldprice
 
import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "godesginpattern/boundedparallelism/domain/gold"
    "net/http"
    "time"
)
 
const (
    lbmaAPIEndpoint = "https://www.lbma.org.uk/json-prices/current"
    timeout         = 10 * time.Second
)
 
type LBMAClient struct {
    apiKey     string
    endpoint   string
    httpClient *http.Client
}
 
func NewLBMAClient(apiKey, endpoint string) *LBMAClient {
    if endpoint == "" {
        endpoint = lbmaAPIEndpoint
    }
 
    return &LBMAClient{
        apiKey:   apiKey,
        endpoint: endpoint,
        httpClient: &http.Client{
            Timeout: timeout,
        },
    }
}
 
type lbmaResponse struct {
    USD []struct {
        Currency string  `json:"currency"`
        Amount   float64 `json:"price"`
        Unit     string  `json:"unitcode"`
        DateTime string  `json:"datetime"`
    } `json:"usd"`
}
 
func (c *LBMAClient) FetchCurrentPrice(ctx context.Context) (*gold.GoldPrice, error) {
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.endpoint, nil)
    if err != nil {
        return nil, err
    }
 
    req.Header.Set("Authorization", "Bearer "+c.apiKey)
    req.Header.Set("Accept", "application/json")
 
    resp, err := c.httpClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
 
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("LBMA API返回错误状态码: %d", resp.StatusCode)
    }
 
    var data lbmaResponse
    if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
        return nil, err
    }
 
    if len(data.USD) == 0 {
        return nil, errors.New("LBMA API返回空数据")
    }
 
    // LBMA通常返回每盎司价格,需要转换为每克价格
    // 1盎司 = 31.1035克
    pricePerGram := data.USD[0].Amount / 31.1035
 
    timestamp, err := time.Parse("2006-01-02T15:04:05", data.USD[0].DateTime)
    if err != nil {
        timestamp = time.Now()
    }
 
    return &gold.GoldPrice{
        Amount:    pricePerGram,
        Currency:  "USD",
        Timestamp: timestamp,
        Source:    "LBMA",
    }, nil
}
 
func (c *LBMAClient) FetchHistoricalPrices(ctx context.Context, startDate, endDate time.Time) ([]*gold.GoldPrice, error) {
    // 实际实现应调用LBMA的历史数据API
    // 这里简化实现只返回错误
    return nil, errors.New("历史数据查询功能尚未实现")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:10
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : mock_client.go
*/
package goldprice
 
import (
    "context"
    "godesginpattern/boundedparallelism/domain/gold"
    "time"
)
 
type MockClient struct {
    currentPrice *gold.GoldPrice
    history      []*gold.GoldPrice
}
 
func NewMockClient(currentPrice float64) *MockClient {
    now := time.Now()
    return &MockClient{
        currentPrice: &gold.GoldPrice{
            Amount:    currentPrice,
            Currency:  "USD",
            Timestamp: now,
            Source:    "MOCK",
        },
        history: []*gold.GoldPrice{
            {
                Amount:    currentPrice - 5.0,
                Currency:  "USD",
                Timestamp: now.Add(-24 * time.Hour),
                Source:    "MOCK",
            },
            {
                Amount:    currentPrice - 2.5,
                Currency:  "USD",
                Timestamp: now.Add(-12 * time.Hour),
                Source:    "MOCK",
            },
            {
                Amount:    currentPrice,
                Currency:  "USD",
                Timestamp: now,
                Source:    "MOCK",
            },
        },
    }
}
 
func (m *MockClient) FetchCurrentPrice(ctx context.Context) (*gold.GoldPrice, error) {
    return m.currentPrice, nil
}
 
func (m *MockClient) FetchHistoricalPrices(ctx context.Context, startDate, endDate time.Time) ([]*gold.GoldPrice, error) {
    var result []*gold.GoldPrice
    for _, price := range m.history {
        if !price.Timestamp.Before(startDate) && !price.Timestamp.After(endDate) {
            result = append(result, price)
        }
    }
    return result, nil
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:09
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : db.go
*/
 
package persistence
 
import (
    "database/sql"
    _ "github.com/lib/pq"
)
 
func NewDBConnection(databaseURL string) (*sql.DB, error) {
    db, err := sql.Open("postgres", databaseURL)
    if err != nil {
        return nil, err
    }
 
    if err := db.Ping(); err != nil {
        return nil, err
    }
 
    return db, nil
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:10
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order_repository.go
*/
package persistence
 
import (
    "context"
    "database/sql"
    "errors"
    "fmt"
    "godesginpattern/boundedparallelism/domain/gold"
    "godesginpattern/boundedparallelism/domain/order"
    "time"
)
 
var (
    ErrOrderNotFound = errors.New("订单未找到")
)
 
type OrderRepository struct {
    db *sql.DB
}
 
func NewOrderRepository(db *sql.DB) *OrderRepository {
    return &OrderRepository{db: db}
}
 
func (r *OrderRepository) Save(ctx context.Context, o *order.Order) error {
    tx, err := r.db.BeginTx(ctx, nil)
    if err != nil {
        return err
    }
    defer tx.Rollback()
 
    // 保存主订单
    _, err = tx.ExecContext(ctx,
        `INSERT INTO orders (id, customer_id, status, created_at, updated_at, gold_price, currency)
         VALUES ($1, $2, $3, $4, $5, $6, $7)
         ON CONFLICT (id) DO UPDATE
         SET status = $3, updated_at = $5, gold_price = $6, currency = $7`,
        o.ID,
        o.CustomerID,
        o.Status,
        o.CreatedAt,
        o.UpdatedAt,
        o.GoldPrice.Amount,
        o.GoldPrice.Currency,
    )
    if err != nil {
        return fmt.Errorf("保存订单失败: %w", err)
    }
 
    // 删除旧的订单项
    _, err = tx.ExecContext(ctx,
        `DELETE FROM order_items WHERE order_id = $1`,
        o.ID,
    )
    if err != nil {
        return fmt.Errorf("清理订单项失败: %w", err)
    }
 
    // 保存新的订单项
    for _, item := range o.Items {
        _, err = tx.ExecContext(ctx,
            `INSERT INTO order_items (order_id, product_id, weight_grams, purity, subtotal)
             VALUES ($1, $2, $3, $4, $5)`,
            o.ID,
            item.ProductID,
            item.WeightGrams,
            item.Purity,
            item.Subtotal,
        )
        if err != nil {
            return fmt.Errorf("保存订单项失败: %w", err)
        }
    }
 
    return tx.Commit()
}
 
func (r *OrderRepository) FindByID(ctx context.Context, id string) (*order.Order, error) {
    var o order.Order
    var goldPrice float64
    var currency string
    var createdAt, updatedAt time.Time
 
    err := r.db.QueryRowContext(ctx,
        `SELECT id, customer_id, status, created_at, updated_at, gold_price, currency
         FROM orders WHERE id = $1`,
        id,
    ).Scan(
        &o.ID,
        &o.CustomerID,
        &o.Status,
        &createdAt,
        &updatedAt,
        &goldPrice,
        &currency,
    )
 
    if err == sql.ErrNoRows {
        return nil, ErrOrderNotFound
    }
    if err != nil {
        return nil, fmt.Errorf("查询订单失败: %w", err)
    }
 
    o.CreatedAt = createdAt
    o.UpdatedAt = updatedAt
    o.GoldPrice = &gold.GoldPrice{
        Amount:   goldPrice,
        Currency: currency,
    }
 
    // 查询订单项
    rows, err := r.db.QueryContext(ctx,
        `SELECT product_id, weight_grams, purity, subtotal
         FROM order_items WHERE order_id = $1`,
        id,
    )
    if err != nil {
        return nil, fmt.Errorf("查询订单项失败: %w", err)
    }
    defer rows.Close()
 
    for rows.Next() {
        var item order.OrderItem
        if err := rows.Scan(
            &item.ProductID,
            &item.WeightGrams,
            &item.Purity,
            &item.Subtotal,
        ); err != nil {
            return nil, fmt.Errorf("解析订单项失败: %w", err)
        }
        o.Items = append(o.Items, item)
    }
 
    if err := rows.Err(); err != nil {
        return nil, fmt.Errorf("遍历订单项失败: %w", err)
    }
 
    return &o, nil
}
 
func (r *OrderRepository) FindByCustomer(ctx context.Context, customerID string, limit, offset int) ([]*order.Order, error) {
    rows, err := r.db.QueryContext(ctx,
        `SELECT id, customer_id, status, created_at, updated_at, gold_price, currency
         FROM orders WHERE customer_id = $1
         ORDER BY created_at DESC
         LIMIT $2 OFFSET $3`,
        customerID, limit, offset,
    )
    if err != nil {
        return nil, fmt.Errorf("查询客户订单失败: %w", err)
    }
    defer rows.Close()
 
    var orders []*order.Order
    for rows.Next() {
        var o order.Order
        var goldPrice float64
        var currency string
        var createdAt, updatedAt time.Time
 
        if err := rows.Scan(
            &o.ID,
            &o.CustomerID,
            &o.Status,
            &createdAt,
            &updatedAt,
            &goldPrice,
            &currency,
        ); err != nil {
            return nil, fmt.Errorf("解析订单失败: %w", err)
        }
 
        o.CreatedAt = createdAt
        o.UpdatedAt = updatedAt
        o.GoldPrice = &gold.GoldPrice{
            Amount:   goldPrice,
            Currency: currency,
        }
 
        // 查询订单项
        items, err := r.loadOrderItems(ctx, o.ID)
        if err != nil {
            return nil, err
        }
        o.Items = items
 
        orders = append(orders, &o)
    }
 
    if err := rows.Err(); err != nil {
        return nil, fmt.Errorf("遍历订单结果失败: %w", err)
    }
 
    return orders, nil
}
 
func (r *OrderRepository) loadOrderItems(ctx context.Context, orderID string) ([]order.OrderItem, error) {
    rows, err := r.db.QueryContext(ctx,
        `SELECT product_id, weight_grams, purity, subtotal
         FROM order_items WHERE order_id = $1`,
        orderID,
    )
    if err != nil {
        return nil, fmt.Errorf("查询订单项失败: %w", err)
    }
    defer rows.Close()
 
    var items []order.OrderItem
    for rows.Next() {
        var item order.OrderItem
        if err := rows.Scan(
            &item.ProductID,
            &item.WeightGrams,
            &item.Purity,
            &item.Subtotal,
        ); err != nil {
            return nil, fmt.Errorf("解析订单项失败: %w", err)
        }
        items = append(items, item)
    }
 
    return items, rows.Err()
}
 
func (r *OrderRepository) UpdateStatus(ctx context.Context, id, status string) error {
    result, err := r.db.ExecContext(ctx,
        `UPDATE orders SET status = $1, updated_at = $2 WHERE id = $3`,
        status,
        time.Now(),
        id,
    )
    if err != nil {
        return fmt.Errorf("更新订单状态失败: %w", err)
    }
 
    rowsAffected, err := result.RowsAffected()
    if err != nil {
        return fmt.Errorf("获取受影响行数失败: %w", err)
    }
 
    if rowsAffected == 0 {
        return ErrOrderNotFound
    }
 
    return nil
}
  
Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:19
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : price_service.go
*/
package gold
 
import (
    "context"
    "errors"
    "godesginpattern/boundedparallelism/domain/gold"
    "time"
)
 
var (
    ErrPriceServiceUnavailable = "金价服务暂时不可用"
    ErrInvalidPriceData        = errors.New("无效的金价数据")
)
 
type PriceService interface {
    GetCurrentPrice(ctx context.Context) (*gold.GoldPrice, error)
    GetHistoricalPrices(ctx context.Context, startDate, endDate time.Time) ([]*gold.GoldPrice, error)
}
 
type GoldPriceService struct {
    client gold.PriceClient
}
 
func NewGoldPriceService(client gold.PriceClient) *GoldPriceService {
    return &GoldPriceService{
        client: client,
    }
}
 
func (s *GoldPriceService) GetCurrentPrice(ctx context.Context) (*gold.GoldPrice, error) {
    price, err := s.client.FetchCurrentPrice(ctx)
    if err != nil {
        return nil, err
    }
 
    // 验证价格数据有效性
    if price.Amount <= 0 || price.Currency != "USD" || price.Timestamp.IsZero() {
        return nil, ErrInvalidPriceData
    }
 
    return price, nil
}
 
func (s *GoldPriceService) GetHistoricalPrices(ctx context.Context, startDate, endDate time.Time) ([]*gold.GoldPrice, error) {
    if endDate.Before(startDate) {
        return nil, errors.New("结束日期不能早于开始日期")
    }
 
    // 限制查询范围不超过30天
    if endDate.Sub(startDate) > 30*24*time.Hour {
        return nil, errors.New("历史价格查询范围不能超过30天")
    }
 
    return s.client.FetchHistoricalPrices(ctx, startDate, endDate)
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:18
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : order_request.go
*/
package dto
 
import "time"
 
type CreateOrderRequest struct {
    CustomerID string         `json:"customer_id" validate:"required,uuid4"`
    Items      []OrderItemDTO `json:"items" validate:"required,min=1,dive"`
    IsVIP      bool           `json:"is_vip"`
}
 
type OrderItemDTO struct {
    ProductID   string  `json:"product_id" validate:"required"`
    WeightGrams float64 `json:"weight_grams" validate:"gt=0"`
    Purity      float64 `json:"purity" validate:"min=0.5,max=1.0"`
}
 
type OrderResponseDTO struct {
    OrderID   string    `json:"order_id"`
    Total     float64   `json:"total"`
    Status    string    `json:"status"`
    CreatedAt time.Time `json:"created_at"`
    Items     []struct {
        ProductID   string  `json:"product_id"`
        WeightGrams float64 `json:"weight_grams"`
        Purity      float64 `json:"purity"`
        Subtotal    float64 `json:"subtotal"`
    } `json:"items"`
}
type OrderRequest struct {
    UserID    string
    Items     []OrderItem
    Timestamp time.Time
    IsVIP     bool // 确保包含此字段
}
 
type OrderItem struct {
    ProductID string
    Quantity  int
    Price     float64
}
 
 
package order
 
import (
    "context"
    "errors"
    "godesginpattern/boundedparallelism/application/gold"
    domainorder "godesginpattern/boundedparallelism/domain/order"
    "godesginpattern/boundedparallelism/infrastructure/concurrency"
    "time"
)
 
var (
    ErrInvalidOrder         = errors.New("无效的订单数据")
    ErrProcessingFailed     = errors.New("订单处理失败")
    ErrConcurrencyLimit     = errors.New("并发处理已达上限")
    ErrGoldPriceUnavailable = errors.New("金价数据不可用")
)
 
type OrderProcessor struct {
    repo         domainorder.Repository
    priceService gold.PriceService
    validator    *domainorder.OrderValidator
    workerPool   *concurrency.BoundedWorkerPool
}
 
func NewOrderProcessor(
    repo domainorder.Repository,
    priceService gold.PriceService,
    validator *domainorder.OrderValidator,
    workerPool *concurrency.BoundedWorkerPool,
) *OrderProcessor {
    return &OrderProcessor{
        repo:         repo,
        priceService: priceService,
        validator:    validator,
        workerPool:   workerPool,
    }
}
 
func (p *OrderProcessor) ProcessOrder(ctx context.Context, req *OrderRequest) (*OrderResponse, error) {
    if err := p.validator.Validate(&domainorder.OrderRequest{
        CustomerID: req.CustomerID,
        Items:      convertToDomainItems(req.Items),
        IsVIP:      req.IsVIP,
    }); err != nil {
        return nil, ErrInvalidOrder
    }
 
    goldPrice, err := p.priceService.GetCurrentPrice(ctx)
    if err != nil {
        return nil, ErrGoldPriceUnavailable
    }
 
    domainItems := convertToDomainItems(req.Items)
    domainOrder, err := domainorder.NewOrder(
        req.CustomerID,
        domainItems,
        req.IsVIP,
        goldPrice,
    )
    if err != nil {
        return nil, ErrInvalidOrder
    }
 
    resultChan := make(chan *OrderProcessingResult, 1)
    err = p.workerPool.Submit(func() {
        if err := p.repo.Save(ctx, domainOrder); err != nil {
            resultChan <- &OrderProcessingResult{Err: err}
            return
        }
 
        total, err := domainOrder.CalculateTotal()
        if err != nil {
            resultChan <- &OrderProcessingResult{Err: err}
            return
        }
 
        resultChan <- &OrderProcessingResult{
            OrderID: domainOrder.ID,
            Total:   total,
            Status:  domainOrder.Status,
        }
    })
 
    if err != nil {
        return nil, ErrConcurrencyLimit
    }
 
    select {
    case result := <-resultChan:
        if result.Err != nil {
            return nil, ErrProcessingFailed
        }
        return &OrderResponse{
            OrderID: result.OrderID,
            Total:   result.Total,
            Status:  result.Status,
            Created: time.Now(),
        }, nil
    case <-ctx.Done():
        return nil, ctx.Err()
    }
}
 
func convertToDomainItems(items []OrderItem) []domainorder.OrderItem {
    domainItems := make([]domainorder.OrderItem, len(items))
    for i, item := range items {
        domainItems[i] = domainorder.OrderItem{
            ProductID:   item.ProductID,
            WeightGrams: item.WeightGrams,
            Purity:      item.Purity,
        }
    }
    return domainItems
}
 
type OrderRequest struct {
    CustomerID string
    Items      []OrderItem
    IsVIP      bool
}
 
type OrderItem struct {
    ProductID   string
    WeightGrams float64
    Purity      float64
}
 
type OrderResponse struct {
    OrderID string
    Total   float64
    Status  string
    Created time.Time
}
 
type OrderProcessingResult struct {
    OrderID string
    Total   float64
    Status  string
    Err     error
}

调用:

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Bounded Parallelism  Pattern 有界并行模式
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : goLang 2024.3.6 go 26.2
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/5/29 22:13
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : boundedparallelismbll.go
*/
 
package bll
 
import (
    "context"
    "godesginpattern/boundedparallelism/application/gold"
    "godesginpattern/boundedparallelism/application/order"
    domainorder "godesginpattern/boundedparallelism/domain/order"
    "godesginpattern/boundedparallelism/infrastructure/concurrency"
    "godesginpattern/boundedparallelism/infrastructure/config"
    "godesginpattern/boundedparallelism/infrastructure/goldprice"
    "godesginpattern/boundedparallelism/infrastructure/persistence"
    httpapi "godesginpattern/boundedparallelism/interfaces/http"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)
 
func BoundedparallelismMain() {
    log.Println("步骤1: 加载配置")
    cfg, err := config.LoadConfig()
    if err != nil {
        log.Fatalf("无法加载配置: %v", err)
    }
    log.Printf("配置加载成功: ServerAddress=%s", cfg.ServerAddress)
 
    log.Println("步骤2: 连接数据库")
    db, err := persistence.NewDBConnection(cfg.DatabaseURL)
    if err != nil {
        log.Fatalf("无法连接数据库: %v", err)
    }
    defer db.Close()
    log.Println("数据库连接成功")
 
    log.Println("步骤3: 创建仓库和客户端")
    orderRepo := persistence.NewOrderRepository(db)
 
    // 使用Mock客户端(用于测试)
    log.Println("使用Mock金价客户端进行测试")
    goldClient := goldprice.NewMockClient(60.0) // 60美元/克
 
    log.Println("仓库和客户端创建成功")
 
    log.Println("步骤4: 创建服务")
    priceService := gold.NewGoldPriceService(goldClient)
    orderValidator := domainorder.NewOrderValidator()
    log.Println("服务创建成功")
 
    log.Println("步骤5: 创建订单处理器")
    orderProcessor := order.NewOrderProcessor(
        orderRepo,
        priceService,
        orderValidator,
        concurrency.NewBoundedWorkerPool(cfg.WorkerPoolSize, cfg.QueueCapacity),
    )
    log.Println("订单处理器创建成功")
 
    log.Println("步骤6: 创建HTTP处理器和服务器")
    handler := httpapi.NewOrderHandler(orderProcessor)
    server := &http.Server{
        Addr:         cfg.ServerAddress,
        Handler:      handler,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
    }
    log.Println("HTTP服务器创建成功")
 
    log.Println("步骤7: 启动HTTP服务器...")
    go func() {
        log.Printf("订单处理器服务已启动,监听地址: %s", cfg.ServerAddress)
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("服务器启动失败: %v", err)
        }
    }()
 
    log.Println("步骤8: 等待关闭信号")
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("关闭服务器...")
 
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("服务器强制关闭: %v", err)
    }
 
    log.Println("服务器已成功关闭")
}

输出:

相关推荐
YikNjy1 小时前
break和continue
java·开发语言·算法
日月云棠1 小时前
10 Integer —— 最常用的整数包装类深度解析
java·后端
大鸡腿同学2 小时前
大模型为何总 “胡说八道”?做完 RAG 知识库,我看懂了它的底层逻辑
后端
秋92 小时前
java项目中cpu飙升排查及解决方法
java·开发语言
野生技术架构师2 小时前
牛客网2026最新大厂Java高频面试题精选(附标准答案)
java·开发语言
PH = 72 小时前
JAVA的SPI机制
java·开发语言
一 乐2 小时前
高校实习信息发布网站|基于Spring Boot的高校实习信息发布网站的设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·高校实习信息发布网站
安久12 小时前
springboot图片上传至服务器本地保存
后端
IT猿手2 小时前
多目标优化算法:多目标蛇优化算法(Multiple Objective Snake Optimizer,MOSO)(提供MATLAB代码)
开发语言·算法·matlab·动态路径规划·光伏模型参数估计
朔北之忘 Clancy2 小时前
2026 年 3 月青少年软编等考 C/C++ 一级真题解析
c语言·开发语言·c++·青少年编程·题解·考级