go: Publish/Subscribe Pattern

先安装包:

bash 复制代码
go install github.com/peterbourgon/diskv/v3 
go install github.com/rabbitmq/amqp091-go 
go install github.com/rs/zerolog 
go install github.com/joho/godotenv 
go get github.com/mattn/go-sqlite3
go get github.com/google/uuid
go get github.com/rabbitmq/amqp091-go

//go get github.com/glebarez/sqlite

如果遇到不是WIN32程序时,终端命令行执行:
$env:CGO_ENABLED=0; go build -o bin/godesginpattern.exe .
go run main.go

项目结构:

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:20
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : settings.go
*/
package config
 
import (
    "github.com/joho/godotenv"
    "os"
    "strconv"
)
 
var (
    LogLevel      = "INFO"
    LogDir        = "./logs"
    LogRotateDays = 7
 
    DBPath = "./jewelry_msg.db"
 
    AsyncWorkerNum = 5
    UseRabbitMQ    = false
 
    RabbitHost  string
    RabbitPort  int
    RabbitUser  string
    RabbitPwd   string
    RabbitVhost = "/"
)
 
func InitConfig() {
    _ = godotenv.Load()
    RabbitHost = getEnv("RABBIT_HOST", "127.0.0.1")
    portStr := getEnv("RABBIT_PORT", "5672")
    RabbitPort, _ = strconv.Atoi(portStr)
    RabbitUser = getEnv("RABBIT_USER", "guest")
    RabbitPwd = getEnv("RABBIT_PWD", "guest")
}
 
func getEnv(key, def string) string {
    val := os.Getenv(key)
    if val == "" {
        return def
    }
    return val
}
 
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:28
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : topics.go
*/
package config
 
type Topic struct {
    RawMaterialPurchase string
    JewelryProduction   string
    QualityInspection   string
    StoreDistribution   string
    JewelrySale         string
    AfterSalesCare      string
}
 
var TopicConst = Topic{
    RawMaterialPurchase: "RawMaterialPurchase",
    JewelryProduction:   "JewelryProduction",
    QualityInspection:   "QualityInspection",
    StoreDistribution:   "StoreDistribution",
    JewelrySale:         "JewelrySale",
    AfterSalesCare:      "AfterSalesCare",
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:29
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : model.go
*/
package domain
 
import "encoding/json"
 
type JewelryMessage struct {
    Title      string `json:"title"`
    Content    string `json:"content"`
    Department string `json:"department"`
}
 
type RawMaterialPurchaseMsg struct {
    JewelryMessage
    Material string `json:"material"`
    Quantity string `json:"quantity"`
}
 
type JewelryProductionMsg struct {
    JewelryMessage
    ProductCount int    `json:"product_count"`
    ProductType  string `json:"product_type"`
}
 
type QualityInspectionMsg struct {
    JewelryMessage
    QualifiedCount int  `json:"qualified_count"`
    HasCertificate bool `json:"has_certificate"`
}
 
type StoreDistributionMsg struct {
    JewelryMessage
    StoreName       string `json:"store_name"`
    DistributeCount int    `json:"distribute_count"`
}
 
type JewelrySaleMsg struct {
    JewelryMessage
    ProductName string  `json:"product_name"`
    Amount      float64 `json:"amount"`
    Store       string  `json:"store"`
}
 
type AfterSalesCareMsg struct {
    JewelryMessage
    CustomerName string `json:"customer_name"`
    ServiceType  string `json:"service_type"`
}
 
func ToJson(v any) string {
    b, _ := json.MarshalIndent(v, "", "  ")
    return string(b)
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:30
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : log.go
*/
package logger
 
import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
    "godesginpattern/publishsubscribe/config"
    "os"
    "path/filepath"
)
 
var Logger zerolog.Logger
 
func InitLogger() {
    _ = os.MkdirAll(config.LogDir, 0755)
    logPath := filepath.Join(config.LogDir, "jewelry_pubsub.log")
    f, _ := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    multi := zerolog.MultiLevelWriter(os.Stdout, f)
    zerolog.TimeFieldFormat = "2006-01-02 15:04:05"
    Logger = zerolog.New(multi).With().Timestamp().Caller().Logger()
    log.Logger = Logger
}
  
Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:38
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : async_publisher.go
*/
package messaging
 
import (
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/logger"
    "sync"
)
 
// 统一两种发布方法的兼容包装:统一成无返回签名
type PublishFunc func(topic string, msg any)
 
type AsyncPublisher struct {
    workerNum int
    taskChan  chan func()
    wg        sync.WaitGroup
}
 
var AsyncPub = NewAsyncPublisher(config.AsyncWorkerNum)
 
func NewAsyncPublisher(worker int) *AsyncPublisher {
    ap := &AsyncPublisher{
        workerNum: worker,
        taskChan:  make(chan func(), 100),
    }
    for i := 0; i < worker; i++ {
        go ap.worker()
    }
    return ap
}
 
func (ap *AsyncPublisher) worker() {
    for task := range ap.taskChan {
        task()
        ap.wg.Done()
    }
}
 
func (ap *AsyncPublisher) PublishAsync(pub PublishFunc, topic string, msg any) {
    ap.wg.Add(1)
    ap.taskChan <- func() {
        defer func() {
            if r := recover(); r != nil {
                logger.Logger.Error().Any("panic", r).Str("topic", topic).Msg("async publish panic")
            }
        }()
        pub(topic, msg)
    }
}
 
func (ap *AsyncPublisher) WaitAndClose() {
    ap.wg.Wait()
    close(ap.taskChan)
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:39
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : broker.go
*/
package messaging
 
import (
    "godesginpattern/publishsubscribe/logger"
    "sync"
)
 
type SubCallback func(msg any)
type PubSubBroker struct {
    mu          sync.RWMutex
    subscribers map[string][]SubCallback
}
 
var LocalBroker = &PubSubBroker{
    subscribers: make(map[string][]SubCallback),
}
 
func (b *PubSubBroker) Subscribe(topic string, cb SubCallback) {
    b.mu.Lock()
    defer b.mu.Unlock()
    b.subscribers[topic] = append(b.subscribers[topic], cb)
    logger.Logger.Info().Str("topic", topic).Str("handler", getFuncName(cb)).Msg("subscribe register success")
}
 
func (b *PubSubBroker) UnSubscribe(topic string, cb SubCallback) {
    b.mu.Lock()
    defer b.mu.Unlock()
    list, ok := b.subscribers[topic]
    if !ok {
        return
    }
    newList := make([]SubCallback, 0)
    for _, item := range list {
        if &item != &cb {
            newList = append(newList, item)
        }
    }
    b.subscribers[topic] = newList
    logger.Logger.Info().Str("topic", topic).Str("handler", getFuncName(cb)).Msg("unsubscribe success")
}
 
func (b *PubSubBroker) Publish(topic string, msg any) {
    b.mu.RLock()
    list, ok := b.subscribers[topic]
    b.mu.RUnlock()
    msgID := MsgStore.SavePublish(topic, msg)
    logger.Logger.Info().Str("topic", topic).Str("msgID", msgID).Int("subCount", len(list)).Msg("publish message")
    if !ok || len(list) == 0 {
        logger.Logger.Warn().Str("topic", topic).Msg("no subscriber, only persist")
        return
    }
    allSuccess := true
    for _, cb := range list {
        func() {
            defer func() {
                if r := recover(); r != nil {
                    allSuccess = false
                    logger.Logger.Error().Any("panic", r).Str("handler", getFuncName(cb)).Str("topic", topic).Msg("sub callback panic")
                }
            }()
            cb(msg)
        }()
    }
    if allSuccess {
        MsgStore.MarkConsumed(msgID)
    }
}
 
func getFuncName(f SubCallback) string {
    return "callback"
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:33
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : exception.go
*/
package messaging
 
type PubSubException struct {
    Msg string
}
 
func (e PubSubException) Error() string {
    return e.Msg
}
 
type SubscriberInvalidError struct {
    PubSubException
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:34
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : message_store.go
*/
package messaging
 
import (
    "database/sql"
    "github.com/google/uuid"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
    _ "modernc.org/sqlite"
    "time"
)
 
type MessageStore struct {
    dbPath string
    db     *sql.DB
}
 
var MsgStore = &MessageStore{dbPath: config.DBPath}
 
func (m *MessageStore) initDB() error {
    if m.db != nil {
        return nil
    }
    db, err := sql.Open("sqlite", m.dbPath)
    if err != nil {
        return err
    }
    db.SetMaxOpenConns(1)
    db.SetMaxIdleConns(1)
    m.db = db
    return nil
}
 
func (m *MessageStore) getConn() (*sql.DB, error) {
    if err := m.initDB(); err != nil {
        return nil, err
    }
    return m.db, nil
}
 
func (m *MessageStore) CreateTable() {
    db, err := m.getConn()
    if err != nil {
        logger.Logger.Error().Err(err).Msg("open db failed")
        return
    }
    sqlCreate := `CREATE TABLE IF NOT EXISTS message_record (
        msg_id TEXT PRIMARY KEY,
        topic TEXT NOT NULL,
        msg_data TEXT NOT NULL,
        publish_time TEXT NOT NULL,
        consume_status INTEGER DEFAULT 0
    )`
    _, err = db.Exec(sqlCreate)
    if err != nil {
        logger.Logger.Error().Err(err).Msg("create table failed")
    }
}
 
func (m *MessageStore) SavePublish(topic string, msg any) string {
    msgID := uuid.NewString()
    jsonStr := domain.ToJson(msg)
    now := time.Now().Format("2006-01-02 15:04:05")
    db, err := m.getConn()
    if err != nil {
        logger.Logger.Error().Err(err).Msg("save publish get conn err")
        return msgID
    }
    _, err = db.Exec(`INSERT INTO message_record(msg_id,topic,msg_data,publish_time) VALUES (?,?,?,?)`,
        msgID, topic, jsonStr, now)
    if err != nil {
        logger.Logger.Error().Err(err).Str("msgID", msgID).Msg("insert msg failed")
    } else {
        logger.Logger.Debug().Str("msgID", msgID).Str("topic", topic).Msg("message saved")
    }
    return msgID
}
 
func (m *MessageStore) MarkConsumed(msgID string) {
    db, err := m.getConn()
    if err != nil {
        logger.Logger.Error().Err(err).Msg("mark consumed get conn err")
        return
    }
    res, err := db.Exec(`UPDATE message_record SET consume_status=1 WHERE msg_id=?`, msgID)
    if err != nil {
        logger.Logger.Error().Err(err).Str("msgID", msgID).Msg("update consume status failed")
        return
    }
    rowCnt, _ := res.RowsAffected()
    if rowCnt > 0 {
        logger.Logger.Debug().Str("msgID", msgID).Msg("marked consumed")
    } else {
        logger.Logger.Warn().Str("msgID", msgID).Msg("no record to mark consumed")
    }
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:40
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : rabbit_broker.go
*/
package messaging
 
import (
    "encoding/json"
    "fmt"
    "github.com/rabbitmq/amqp091-go"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/logger"
    "sync"
)
 
type RabbitBroker struct {
    mu      sync.Mutex
    conn    *amqp091.Connection
    channel *amqp091.Channel
    subMap  map[string]bool
}
 
var RabbitBrokerIns = &RabbitBroker{
    subMap: make(map[string]bool),
}
 
func (r *RabbitBroker) connect() error {
    r.mu.Lock()
    defer r.mu.Unlock()
    if r.conn != nil && !r.conn.IsClosed() {
        return nil
    }
    url := fmt.Sprintf("amqp://%s:%s@%s:%d/%s",
        config.RabbitUser, config.RabbitPwd, config.RabbitHost, config.RabbitPort, config.RabbitVhost)
    conn, err := amqp091.Dial(url)
    if err != nil {
        return err
    }
    ch, err := conn.Channel()
    if err != nil {
        return err
    }
    r.conn = conn
    r.channel = ch
    logger.Logger.Info().Msg("rabbitmq connect success")
    return nil
}
 
func (r *RabbitBroker) Subscribe(topic string, cb SubCallback) error {
    if err := r.connect(); err != nil {
        return err
    }
    exName := topic
    _ = r.channel.ExchangeDeclare(exName, "fanout", true, false, false, false, nil)
    q, err := r.channel.QueueDeclare("", true, true, true, false, nil)
    if err != nil {
        return err
    }
    _ = r.channel.QueueBind(q.Name, "", exName, false, nil)
    msgs, err := r.channel.Consume(q.Name, "", false, false, false, false, nil)
    if err != nil {
        return err
    }
    go func() {
        for d := range msgs {
            var data map[string]any
            _ = json.Unmarshal(d.Body, &data)
            cb(data["data"])
            _ = d.Ack(false)
        }
    }()
    r.subMap[topic] = true
    logger.Logger.Info().Str("topic", topic).Str("queue", q.Name).Msg("rabbit subscribe ok")
    return nil
}
 
func (r *RabbitBroker) Publish(topic string, msg any) error {
    if err := r.connect(); err != nil {
        return err
    }
    exName := topic
    _ = r.channel.ExchangeDeclare(exName, "fanout", true, false, false, false, nil)
    msgID := MsgStore.SavePublish(topic, msg)
    payload := map[string]any{
        "msg_id": msgID,
        "data":   msg,
    }
    b, _ := json.Marshal(payload)
    err := r.channel.Publish(exName, "", false, false, amqp091.Publishing{
        Body: b,
    })
    if err != nil {
        logger.Logger.Error().Err(err).Str("topic", topic).Msg("rabbit publish failed")
        return err
    }
    logger.Logger.Info().Str("topic", topic).Str("msgID", msgID).Msg("rabbit publish ok")
    return nil
}
 
func (r *RabbitBroker) WrapPublish(topic string, msg any) {
    _ = r.Publish(topic, msg)
}

订阅者服务

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:46
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : customer.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func CustomerHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("customer raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "👥客户部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:44
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : finance.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func FinanceHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("finance raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "💰财务部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:45
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : quality.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func QualityHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("quality raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "🔍质检部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:45
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : sales.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func SalesHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("sales raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "🛍️销售部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:46
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : store.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func StoreHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("store raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "🏪门店").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:47
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : tech.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func TechHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("tech raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "🔧技术部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:43
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : warehouse.go
*/
package subscribers
 
import (
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/logger"
)
 
func WarehouseHandle(msg any) {
    m, ok := msg.(domain.JewelryMessage)
    if !ok {
        logger.Logger.Info().Any("warehouse raw msg", msg)
        return
    }
    logger.Logger.Info().Str("dept", "🏬仓库部").Str("title", m.Title).Str("content", m.Content).Msg("receive msg")
}

发布者服务

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:50
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : after_sales.go
*/
package publishers
 
import (
    "fmt"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func AfterSalesPublish(customerName, serviceType string) {
    msg := domain.AfterSalesCareMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "客户售后保养申请",
            Content:    fmt.Sprintf("客户%s申请%s服务,已受理", customerName, serviceType),
            Department: "客服部",
        },
        CustomerName: customerName,
        ServiceType:  serviceType,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.AfterSalesCare, msg)
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:49
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : distribution.go
*/
package publishers
 
import (
    "fmt"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func DistributionPublish(storeName string, distributeCount int) {
    msg := domain.StoreDistributionMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "门店铺货完成",
            Content:    fmt.Sprintf("向%s铺货%d件珠宝,已出库发货", storeName, distributeCount),
            Department: "仓库部",
        },
        StoreName:       storeName,
        DistributeCount: distributeCount,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.StoreDistribution, msg)
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:49
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : inspection.go
*/
package publishers
 
import (
    "fmt"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func InspectionPublish(qualifiedCount int, hasCertificate bool) {
    certStr := "未出具证书"
    if hasCertificate {
        certStr = "已出具证书"
    }
    msg := domain.QualityInspectionMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "珠宝质量检测完成",
            Content:    fmt.Sprintf("总计%d件珠宝,全部合格,%s", qualifiedCount, certStr),
            Department: "质检部",
        },
        QualifiedCount: qualifiedCount,
        HasCertificate: hasCertificate,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.QualityInspection, msg)
}
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:48
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : production.go
*/
package publishers
 
import (
    "fmt"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func ProductionPublish(productType string, count int) {
    msg := domain.JewelryProductionMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "珠宝生产完成",
            Content:    productType + fmt.Sprintf("%d件,待质检", count),
            Department: "生产部",
        },
        ProductCount: count,
        ProductType:  productType,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.JewelryProduction, msg)
}
 
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:48
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : purchase.go
*/
package publishers
 
import (
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func getBroker() messaging.PublishFunc {
    if config.UseRabbitMQ {
        // 使用包装后的无返回方法
        return messaging.RabbitBrokerIns.WrapPublish
    }
    // 本地Broker原生无返回,直接返回
    return messaging.LocalBroker.Publish
}
 
func PurchasePublish(material, quantity string) {
    msg := domain.RawMaterialPurchaseMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "原材料采购完成",
            Content:    material + " " + quantity + ",待入库",
            Department: "采购部",
        },
        Material: material,
        Quantity: quantity,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.RawMaterialPurchase, msg)
}
 
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:50
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : sale.go
*/
package publishers
 
import (
    "fmt"
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/domain"
    "godesginpattern/publishsubscribe/messaging"
)
 
func SalePublish(productName string, amount float64, store string) {
    msg := domain.JewelrySaleMsg{
        JewelryMessage: domain.JewelryMessage{
            Title:      "珠宝销售成功",
            Content:    fmt.Sprintf("%s售出%s,收款%.2f元,库存已扣减", store, productName, amount),
            Department: "门店",
        },
        ProductName: productName,
        Amount:      amount,
        Store:       store,
    }
    pubFunc := getBroker()
    messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.JewelrySale, msg)
}

调用:

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:51
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : publishsubscribebll.go
*/
package bll
 
import (
    "godesginpattern/publishsubscribe/config"
    "godesginpattern/publishsubscribe/logger"
    "godesginpattern/publishsubscribe/messaging"
    "godesginpattern/publishsubscribe/service/publishers"
    "godesginpattern/publishsubscribe/service/subscribers"
)
 
func registerSubscribers() {
    t := config.TopicConst
    b := messaging.LocalBroker
    if config.UseRabbitMQ {
        panic("本地测试请将UseRabbitMQ置false")
    }
    b.Subscribe(t.RawMaterialPurchase, subscribers.WarehouseHandle)
    b.Subscribe(t.RawMaterialPurchase, subscribers.FinanceHandle)
 
    b.Subscribe(t.JewelryProduction, subscribers.QualityHandle)
    b.Subscribe(t.JewelryProduction, subscribers.WarehouseHandle)
 
    b.Subscribe(t.QualityInspection, subscribers.SalesHandle)
    b.Subscribe(t.QualityInspection, subscribers.WarehouseHandle)
    b.Subscribe(t.QualityInspection, subscribers.FinanceHandle)
 
    b.Subscribe(t.StoreDistribution, subscribers.StoreHandle)
    b.Subscribe(t.StoreDistribution, subscribers.SalesHandle)
 
    b.Subscribe(t.JewelrySale, subscribers.FinanceHandle)
    b.Subscribe(t.JewelrySale, subscribers.WarehouseHandle)
 
    b.Subscribe(t.AfterSalesCare, subscribers.CustomerHandle)
    b.Subscribe(t.AfterSalesCare, subscribers.TechHandle)
}
 
func PublishSubscribeMain() {
    // 初始化配置、日志、数据库表
    config.InitConfig()
    logger.InitLogger()
    messaging.MsgStore.CreateTable()
    logger.Logger.Info().Msg("===== Go珠宝企业PubSub系统启动 =====")
 
    registerSubscribers()
 
    // 完整业务流程
    publishers.PurchasePublish("18K金+天然钻石", "500克+100颗")
    publishers.ProductionPublish("钻石戒指、黄金项链", 80)
    publishers.InspectionPublish(80, true)
    publishers.DistributionPublish("北京朝阳店、上海南京路店", 80)
    publishers.SalePublish("经典六爪钻戒", 12800.0, "上海南京路店")
    publishers.AfterSalesPublish("张先生", "钻石清洗、抛光保养")
 
    // 取消订阅测试
    logger.Logger.Info().Msg("执行取消仓库【珠宝销售】主题订阅")
    messaging.LocalBroker.UnSubscribe(config.TopicConst.JewelrySale, subscribers.WarehouseHandle)
    publishers.SalePublish("足金素圈项链", 3800.0, "北京朝阳店")
 
    // 等待异步任务执行完成
    messaging.AsyncPub.WaitAndClose()
    logger.Logger.Info().Msg("===== Go珠宝企业PubSub系统运行结束 =====")
}

输出:

另一种输出:

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:20
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : settings.go
*/
package config

import (
	"github.com/joho/godotenv"
	"os"
	"strconv"
)

var (
	LogLevel      = "INFO"
	LogDir        = "./logs"
	LogRotateDays = 7

	DBPath = "./jewelry_msg.db"

	AsyncWorkerNum = 5
	UseRabbitMQ    = false

	RabbitHost  string
	RabbitPort  int
	RabbitUser  string
	RabbitPwd   string
	RabbitVhost = "/"
)

func InitConfig() {
	_ = godotenv.Load()
	RabbitHost = getEnv("RABBIT_HOST", "127.0.0.1")
	portStr := getEnv("RABBIT_PORT", "5672")
	RabbitPort, _ = strconv.Atoi(portStr)
	RabbitUser = getEnv("RABBIT_USER", "guest")
	RabbitPwd = getEnv("RABBIT_PWD", "guest")
}

func getEnv(key, def string) string {
	val := os.Getenv(key)
	if val == "" {
		return def
	}
	return val
}



/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:28
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : topics.go
*/
package config

type Topic struct {
	RawMaterialPurchase string
	JewelryProduction   string
	QualityInspection   string
	StoreDistribution   string
	JewelrySale         string
	AfterSalesCare      string
}

var TopicConst = Topic{
	RawMaterialPurchase: "RawMaterialPurchase",
	JewelryProduction:   "JewelryProduction",
	QualityInspection:   "QualityInspection",
	StoreDistribution:   "StoreDistribution",
	JewelrySale:         "JewelrySale",
	AfterSalesCare:      "AfterSalesCare",
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:29
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : model.go
*/
package domain

import "encoding/json"

type JewelryMessage struct {
	Title      string `json:"title"`
	Content    string `json:"content"`
	Department string `json:"department"`
}

type RawMaterialPurchaseMsg struct {
	JewelryMessage
	Material string `json:"material"`
	Quantity string `json:"quantity"`
}

type JewelryProductionMsg struct {
	JewelryMessage
	ProductCount int    `json:"product_count"`
	ProductType  string `json:"product_type"`
}

type QualityInspectionMsg struct {
	JewelryMessage
	QualifiedCount int  `json:"qualified_count"`
	HasCertificate bool `json:"has_certificate"`
}

type StoreDistributionMsg struct {
	JewelryMessage
	StoreName       string `json:"store_name"`
	DistributeCount int    `json:"distribute_count"`
}

type JewelrySaleMsg struct {
	JewelryMessage
	ProductName string  `json:"product_name"`
	Amount      float64 `json:"amount"`
	Store       string  `json:"store"`
}

type AfterSalesCareMsg struct {
	JewelryMessage
	CustomerName string `json:"customer_name"`
	ServiceType  string `json:"service_type"`
}

func ToJson(v any) string {
	b, _ := json.MarshalIndent(v, "", "  ")
	return string(b)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:30
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : log.go
*/
package logger

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"godesginpattern/publishsubscribe/config"
	"os"
	"path/filepath"
)

var Logger zerolog.Logger

func InitLogger() {
	_ = os.MkdirAll(config.LogDir, 0755)
	logPath := filepath.Join(config.LogDir, "jewelry_pubsub.log")
	f, _ := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	
	consoleWriter := &zerolog.ConsoleWriter{
		Out:        os.Stdout,
		TimeFormat: "2006-01-02 15:04:05",
		NoColor:    true,
	}
	
	multi := zerolog.MultiLevelWriter(consoleWriter, f)
	zerolog.TimeFieldFormat = "2006-01-02 15:04:05"
	zerolog.SetGlobalLevel(zerolog.InfoLevel)
	Logger = zerolog.New(multi).With().Timestamp().Logger()
	log.Logger = Logger
}


//message 中的同上用,不用再改

订阅者服务

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:46
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : customer.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func CustomerHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("customer raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("👥 客户服务部 | 接收: %s | %s", m.Title, m.Content)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:44
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : finance.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func FinanceHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("finance raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("💰 财务部 | 接收: %s | %s", m.Title, m.Content)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:45
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : quality.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func QualityHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("quality raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("🔍 质检部 | 接收: %s | %s", m.Title, m.Content)
}



/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:45
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : sales.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func SalesHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("sales raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("🛍️ 营销部 | 接收: %s | %s", m.Title, m.Content)
}



/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:46
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : store.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func StoreHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("store raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("🏪 品牌门店 | 接收: %s | %s", m.Title, m.Content)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:47
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : tech.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func TechHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("tech raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("🔧 技术部 | 接收: %s | %s", m.Title, m.Content)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:43
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : warehouse.go
*/
package subscribers

import (
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/logger"
)

func WarehouseHandle(msg any) {
	var m domain.JewelryMessage
	switch v := msg.(type) {
	case domain.RawMaterialPurchaseMsg:
		m = v.JewelryMessage
	case domain.JewelryProductionMsg:
		m = v.JewelryMessage
	case domain.QualityInspectionMsg:
		m = v.JewelryMessage
	case domain.StoreDistributionMsg:
		m = v.JewelryMessage
	case domain.JewelrySaleMsg:
		m = v.JewelryMessage
	case domain.AfterSalesCareMsg:
		m = v.JewelryMessage
	default:
		logger.Logger.Info().Any("warehouse raw msg", msg)
		return
	}
	logger.Logger.Info().Msgf("🏬 仓库部 | 接收消息: %s | %s", m.Title, m.Content)
}

发布者服务

Go 复制代码
/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:50
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : after_sales.go
*/
package publishers

import (
	"fmt"
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func AfterSalesPublish(customerName, serviceType string) {
	msg := domain.AfterSalesCareMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "客户售后保养申请",
			Content:    fmt.Sprintf("客户%s申请%s服务,已受理", customerName, serviceType),
			Department: "客服部",
		},
		CustomerName: customerName,
		ServiceType:  serviceType,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.AfterSalesCare, msg)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:49
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : distribution.go
*/
package publishers

import (
	"fmt"
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func DistributionPublish(storeName string, distributeCount int) {
	msg := domain.StoreDistributionMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "门店铺货完成",
			Content:    fmt.Sprintf("向%s铺货%d件珠宝,已出库发货", storeName, distributeCount),
			Department: "仓库部",
		},
		StoreName:       storeName,
		DistributeCount: distributeCount,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.StoreDistribution, msg)
}



/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:49
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : inspection.go
*/
package publishers

import (
	"fmt"
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func InspectionPublish(qualifiedCount int, hasCertificate bool) {
	certStr := "未出具证书"
	if hasCertificate {
		certStr = "已出具证书"
	}
	msg := domain.QualityInspectionMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "珠宝质量检测完成",
			Content:    fmt.Sprintf("总计%d件珠宝,全部合格,%s", qualifiedCount, certStr),
			Department: "质检部",
		},
		QualifiedCount: qualifiedCount,
		HasCertificate: hasCertificate,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.QualityInspection, msg)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:48
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : production.go
*/
package publishers

import (
	"fmt"
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func ProductionPublish(productType string, count int) {
	msg := domain.JewelryProductionMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "珠宝生产完成",
			Content:    productType + fmt.Sprintf("%d件,待质检", count),
			Department: "生产部",
		},
		ProductCount: count,
		ProductType:  productType,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.JewelryProduction, msg)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:48
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : purchase.go
*/
package publishers

import (
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func getBroker() messaging.PublishFunc {
	if config.UseRabbitMQ {
		// 使用包装后的无返回方法
		return messaging.RabbitBrokerIns.WrapPublish
	}
	// 本地Broker原生无返回,直接返回
	return messaging.LocalBroker.Publish
}

func PurchasePublish(material, quantity string) {
	msg := domain.RawMaterialPurchaseMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "原材料采购完成",
			Content:    material + " " + quantity + ",待入库",
			Department: "采购部",
		},
		Material: material,
		Quantity: quantity,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.RawMaterialPurchase, msg)
}


/*
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Publish/Subscribe 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/6/24 20:50
# User      :  geovindu
# Product   : GoLand
# Project   : godesginpattern
# File      : sale.go
*/
package publishers

import (
	"fmt"
	"godesginpattern/publishsubscribe/config"
	"godesginpattern/publishsubscribe/domain"
	"godesginpattern/publishsubscribe/messaging"
)

func SalePublish(productName string, amount float64, store string) {
	msg := domain.JewelrySaleMsg{
		JewelryMessage: domain.JewelryMessage{
			Title:      "珠宝销售成功",
			Content:    fmt.Sprintf("%s售出%s,收款%.2f元,库存已扣减", store, productName, amount),
			Department: "门店",
		},
		ProductName: productName,
		Amount:      amount,
		Store:       store,
	}
	pubFunc := getBroker()
	messaging.AsyncPub.PublishAsync(pubFunc, config.TopicConst.JewelrySale, msg)
}

//调用,和上面一样

输出:

复制代码

SQLite: