go: Bounded Parallelism Pattern

postgreSQL

-- 创建 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
);

  

项目结构:

image

 

/*
# 版权所有  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)),
	}
}
/*
# 版权所有  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
}

  

/*
# 版权所有  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
}

  

 

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
}

  

/*
# 版权所有  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
}

  

调用:

/*
# 版权所有  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("服务器已成功关闭")
}

  

输出:

image

 

 

 GitHub - lotusirous/go-concurrency-patterns: Concurrency patterns in Go · GitHub

  https://github.com/lotusirous/go-concurrency-patterns

posted @ 2026-05-30 00:16  ®Geovin Du Dream Park™  阅读(4)  评论(0)    收藏  举报