惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Webroot Blog
Webroot Blog
腾讯CDC
The Last Watchdog
The Last Watchdog
博客园 - 司徒正美
H
Hacker News: Front Page
I
InfoQ
A
Arctic Wolf
H
Hackread – Cybersecurity News, Data Breaches, AI and More
H
Heimdal Security Blog
L
LINUX DO - 最新话题
T
Threat Research - Cisco Blogs
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
Security Latest
Security Latest
D
DataBreaches.Net
C
Check Point Blog
J
Java Code Geeks
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
Lohrmann on Cybersecurity
雷峰网
雷峰网
Vercel News
Vercel News
WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Spread Privacy
Spread Privacy
Forbes - Security
Forbes - Security
阮一峰的网络日志
阮一峰的网络日志
Hacker News: Ask HN
Hacker News: Ask HN
大猫的无限游戏
大猫的无限游戏
I
Intezer
N
News and Events Feed by Topic
小众软件
小众软件
B
Blog RSS Feed
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 三生石上(FineUI控件)
S
Security @ Cisco Blogs
美团技术团队
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main

博客园 - ®Geovin Du Dream Park™

go: Functional Options Pattern go:Timing Functions Pattern python: Timing Functions Pattern go: Steady-State Pattern python: Steady-State Pattern go: Handshaking Pattern python: Handshaking Pattern go: Fail-Fast Pattern python: Fail-Fast Pattern I go: Deadline Pattern python: Deadline Pattern go: Circuit-Breaker Pattern python: Circuit-Breaker Pattern go: Bulkheads Pattern python: Bulkheads Pattern go: Push & Pull Pattern python: Push & Pull Pattern python: Publish/Subscribe Pattern II python: Publish/Subscribe Pattern go: Futures & Promises Pattern python: Futures & Promises Pattern go: Worker Pool Pattern go:Pipeline Pattern python: Worker Pool Pattern python: Pipeline Pattern go: Fan-Out Pattern python: Fan-Out Pattern go: Fan-In Pattern python: Fan-In Pattern Fan-In go: Producer Consumer  Pattern python: Producer Consumer Pattern go: Parallelism Pattern python: Parallelism Pattern go: Reactor Pattern python: Reactor Pattern go: Generators Pattern python: speech to text offline python: Generators Pattern go: Coroutines Pattern python:Coroutines Pattern go: Broadcast Pattern python: Broadcast Pattern python: Bounded Parallelism Pattern go: Bounded Parallelism Pattern python: N-Barrier Pattern go: N-Barrier Pattern python: Semaphore Pattern go: Semaphore Pattern python: Read-Write Lock Pattern go: Read-Write Lock Pattern python: Monitor Pattern go: Monitor Pattern python: Mutex Pattern go: Lock/Mutex Pattern Python: Condition Variable Pattern go:Condition Variable Pattern python: Registry Pattern python: Interpreter Pattern go: Interpreter Pattern go: Registry Pattern go: Memento Pattern go: Command Pattern go: State Pattern go:Template Method Pattern go: Strategy Pattern go: Visitor Pattern go: Observer Pattern go: Mediator Pattern go: Iterator Pattern go: Chain of Responsibility Pattern go: Proxy Pattern go:Decorator Pattern go: Facade Pattern go: Flyweight Pattern go: Composite Pattern go: Singleton Pattern go: Prototype Pattern go: Bridge Pattern go: Adapter Pattern go: Builder Pattern 密码进行加盐哈希 using CSharp,Python,Go,Java go: Abstract Factory Pattern go: Model,Interface,DAL ,Factory,BLL using mysql go: Simple Factory Pattern go: Factory Method Pattern go: goLang 在Windows环境搭建Go语言开发环境 CSharp: Parallel Extensions cpp: class python: 蝴蝶跟随鼠标 javascript: 中国历史人物热力分布图using echart javascript: 中国历史人物热力图 python: 初养龙虾微信纯文字自动回复using workBuddy python: object 入门 python: Factory Method Pattern python: Simple Factory Pattern python: Abstract Factory Pattern 区域化 代码 python: Null Object Pattern python: Builder Pattern python: Adapter Pattern
go: Publish/Subscribe Pattern
®Geovin Du Dream Park™ · 2026-06-24 · via 博客园 - ®Geovin Du Dream Park™

项目结构:

image

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

订阅者服务

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

发布者服务

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

调用:

/*
# 版权所有  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系统运行结束 =====")
}

输出:

image

哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)