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 );
项目结构:

/*
# 版权所有 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 concurrencyimport ("context""sync"
)type BoundedExecutor struct {workerCount intsem 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 nilcase <-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 metricsimport ("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 stringpoolName stringactiveWorkers int32completedTasks int32queuedTasks int32QueueLength intqueueLength int32rejectedTasks int32ActiveWorkers intQueueCapacity intCompletedTasks intRejectedTasks 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 goldimport ("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 calculatorimport ("godesginpattern/boundedparallelism/domain/order"
)type BaseCalculator struct{}func (c *BaseCalculator) CalculateTotalWeight(items []order.OrderItem) float64 {var total float64for _, item := range items {total += item.WeightGrams}return total
}func (c *BaseCalculator) CalculatePurityAdjustedWeight(items []order.OrderItem) float64 {var total float64for _, 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 calculatorimport "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 orderimport ("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 stringCustomerID stringItems []OrderItemIsVIP boolStatus stringCreatedAt time.TimeUpdatedAt time.TimeGoldPrice *gold.GoldPrice
}type OrderItem struct {ProductID stringWeightGrams float64Purity float64Subtotal 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 float64if o.GoldPrice == nil || o.GoldPrice.Amount <= 0 {return 0, errors.New("无效的金价数据")}for i, item := range o.Items {// 计算每件商品价格:重量(克) * 纯度 * 金价(每克)subtotal := item.WeightGrams * item.Purity * o.GoldPrice.Amounto.Items[i].Subtotal = subtotaltotal += subtotal}// VIP客户享受95折if o.IsVIP {total = total * 0.95}// 添加10%增值税total = total * 1.10return 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 = newStatuso.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 orderimport "context"type Repository interface {Save(ctx context.Context, order *Order) errorFindByID(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 orderimport ("errors"//"godesginpattern/boundedparallelism/domain/order""regexp""time"
)var (ErrInvalidCustomerID = errors.New("无效的客户ID格式")ErrInvalidProductID = errors.New("无效的产品ID格式")
)type OrderValidator struct {customerIDRegex *regexp.RegexpproductIDRegex *regexp.Regexp
}type OrderRequest struct {// 字段定义ID stringCustomerID stringItems []OrderItemTimestamp time.TimeUserID stringIsVIP 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 httpimport ("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.OrderProcessorvalidator *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.CreateOrderRequestif 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 concurrencyimport ("context""errors""godesginpattern/boundedparallelism/common/concurrency""godesginpattern/boundedparallelism/common/metrics""sync""time"
)var (ErrQueueFull = errors.New("任务队列已满")
)type BoundedWorkerPool struct {queue chan func()workerPool *concurrency.BoundedExecutormetrics *metrics.PoolMetricsshutdownOnce 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 nildefault: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 errorp.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 concurrencyimport ("sync/atomic""time"
)/**/
type PoolMetrics struct {activeWorkers int32queueLength int32queueCapacity intQueueLength intcompletedTasks uint64failedTasks uint64totalWaitTime time.DurationtotalProcessTime 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 intQueueLength intQueueCapacity intCompletedTasks uint64FailedTasks uint64TotalWaitTime time.DurationTotalProcessTime 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 concurrencyimport ("container/heap""sync""time"
)type Priority intconst (LowPriority Priority = iotaMediumPriorityHighPriority
)type Task struct {Priority PrioritySubmitTime int64ExecuteFunc func()
}type PriorityQueue struct {items []*Taskmu 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.itemsn := len(old)item := old[n-1]old[n-1] = nilpq.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 configimport ("github.com/joho/godotenv""os""strconv"
)type Config struct {ServerAddress stringDatabaseURL stringGoldAPIKey stringGoldAPIEndpoint stringWorkerPoolSize intQueueCapacity 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 goldpriceimport ("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 stringendpoint stringhttpClient *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 lbmaResponseif 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.1035timestamp, 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 goldpriceimport ("context""godesginpattern/boundedparallelism/domain/gold""time"
)type MockClient struct {currentPrice *gold.GoldPricehistory []*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.GoldPricefor _, 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 persistenceimport ("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 persistenceimport ("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 UPDATESET 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.Ordervar goldPrice float64var currency stringvar createdAt, updatedAt time.Timeerr := r.db.QueryRowContext(ctx,`SELECT id, customer_id, status, created_at, updated_at, gold_price, currencyFROM orders WHERE id = $1`,id,).Scan(&o.ID,&o.CustomerID,&o.Status,&createdAt,&updatedAt,&goldPrice,¤cy,)if err == sql.ErrNoRows {return nil, ErrOrderNotFound}if err != nil {return nil, fmt.Errorf("查询订单失败: %w", err)}o.CreatedAt = createdAto.UpdatedAt = updatedAto.GoldPrice = &gold.GoldPrice{Amount: goldPrice,Currency: currency,}// 查询订单项rows, err := r.db.QueryContext(ctx,`SELECT product_id, weight_grams, purity, subtotalFROM 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.OrderItemif 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, currencyFROM orders WHERE customer_id = $1ORDER BY created_at DESCLIMIT $2 OFFSET $3`,customerID, limit, offset,)if err != nil {return nil, fmt.Errorf("查询客户订单失败: %w", err)}defer rows.Close()var orders []*order.Orderfor rows.Next() {var o order.Ordervar goldPrice float64var currency stringvar createdAt, updatedAt time.Timeif err := rows.Scan(&o.ID,&o.CustomerID,&o.Status,&createdAt,&updatedAt,&goldPrice,¤cy,); err != nil {return nil, fmt.Errorf("解析订单失败: %w", err)}o.CreatedAt = createdAto.UpdatedAt = updatedAto.GoldPrice = &gold.GoldPrice{Amount: goldPrice,Currency: currency,}// 查询订单项items, err := r.loadOrderItems(ctx, o.ID)if err != nil {return nil, err}o.Items = itemsorders = 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, subtotalFROM order_items WHERE order_id = $1`,orderID,)if err != nil {return nil, fmt.Errorf("查询订单项失败: %w", err)}defer rows.Close()var items []order.OrderItemfor rows.Next() {var item order.OrderItemif 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 goldimport ("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 dtoimport "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 stringItems []OrderItemTimestamp time.TimeIsVIP bool // 确保包含此字段
}type OrderItem struct {ProductID stringQuantity intPrice float64
}package orderimport ("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.RepositorypriceService gold.PriceServicevalidator *domainorder.OrderValidatorworkerPool *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(),}, nilcase <-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 stringItems []OrderItemIsVIP bool
}type OrderItem struct {ProductID stringWeightGrams float64Purity float64
}type OrderResponse struct {OrderID stringTotal float64Status stringCreated time.Time
}type OrderProcessingResult struct {OrderID stringTotal float64Status stringErr 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 bllimport ("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)<-quitlog.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("服务器已成功关闭")
}
输出:

