项目结构:

Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:15
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : jewelry_api.go
*/
package api
// Jewelry 珠宝接口:定义所有珠宝(单品/组合件)的统一行为
// 职责:抽象公共行为,实现组合模式的统一接口,隔离业务层与实现层
type Jewelry interface {
GetName() string // 获取珠宝名称
GetMaterial() string // 获取珠宝材质
GetCertNumber() string // 获取证书编号(组合件无)
GetStockCount() int // 获取库存数量
Add(child Jewelry) // 新增子组件(单品空实现,组合件实现)
IsStockLow(threshold int) bool // 库存预警判断(是否低于阈值)
}
// InventoryService 库存业务接口:定义库存业务的统一服务
type InventoryService interface {
CheckByName(j Jewelry) map[string]int // 按名称盘点库存
CheckByMaterial(j Jewelry) map[string]int // 按材质盘点库存
CheckByCert(j Jewelry) map[string]bool // 按证书编号盘点(去重)
DeductStock(j Jewelry, count int) error // 库存扣减(含业务校验)
GenerateInventoryReport(j Jewelry) string // 生成企业级盘点报告
}
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:15
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : base_jewelry.go
*/
package impl
// BaseJewelry 珠宝基础结构体:封装所有珠宝的公共属性(遵循单一职责)
// 嵌入到单品结构体中,实现代码复用,避免重复定义
type BaseJewelry struct {
name string // 珠宝名称
material string // 材质(黄金、铂金、钻石等)
weight float64 // 重量(单位:g/克拉)
price float64 // 单价(单位:元)
certNumber string // 证书编号(单品唯一,组合件无)
stockCount int // 库存数量(核心字段,私有属性,通过接口方法访问)
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:15
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : diamond.go
*/
package impl
import "godesginpattern/composite/api"
// Diamond 钻石单品:实现api.Jewelry接口
// 职责:处理钻石自身的属性和行为,仅实现接口方法,不包含业务逻辑
type Diamond struct {
BaseJewelry
}
// NewDiamond 工厂方法:创建钻石实例(封装实例化逻辑,便于扩展参数)
// 修复:参数顺序与调用一致,确保实例创建成功,不返回nil
func NewDiamond(material string, weight float64, price float64, certNumber string, stock int) api.Jewelry {
// 新增:参数校验,避免非法参数导致实例异常
if material == "" || certNumber == "" || stock < 0 {
return nil
}
return &Diamond{
BaseJewelry: BaseJewelry{
name: "单颗钻石",
material: material,
weight: weight,
price: price,
certNumber: certNumber,
stockCount: stock,
},
}
}
// 实现api.Jewelry接口所有方法
func (d *Diamond) GetName() string { return d.name }
func (d *Diamond) GetMaterial() string { return d.material }
func (d *Diamond) GetCertNumber() string { return d.certNumber }
func (d *Diamond) GetStockCount() int { return d.stockCount }
func (d *Diamond) Add(child api.Jewelry) {} // 单品无子女,空实现
func (d *Diamond) IsStockLow(threshold int) bool { return threshold > 0 && d.stockCount < threshold }
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:16
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : ring.go
*/
package impl
import "godesginpattern/composite/api"
// Ring 戒指单品:实现api.Jewelry接口
// 职责:处理戒指自身的属性和行为,仅实现接口方法,不包含业务逻辑
type Ring struct {
BaseJewelry
}
// NewRing 工厂方法:创建戒指实例
// 说明:第一个参数为材质(如铂金、玫瑰金),名称自动拼接为「材质+戒指」,避免调用者传参错误
func NewRing(material string, weight float64, price float64, certNumber string, stock int) api.Jewelry {
// 新增:参数校验,避免非法参数导致实例异常(返回nil)
if material == "" || certNumber == "" || stock < 0 {
return nil
}
return &Ring{
BaseJewelry: BaseJewelry{
name: material + "戒指", // 自动拼接名称,避免调用时传错参数
material: material,
weight: weight,
price: price,
certNumber: certNumber,
stockCount: stock,
},
}
}
// 实现api.Jewelry接口所有方法
func (r *Ring) GetName() string { return r.name }
func (r *Ring) GetMaterial() string { return r.material }
func (r *Ring) GetCertNumber() string { return r.certNumber }
func (r *Ring) GetStockCount() int { return r.stockCount }
func (r *Ring) Add(child api.Jewelry) {}
func (r *Ring) IsStockLow(threshold int) bool { return threshold > 0 && r.stockCount < threshold }
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:16
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : necklace.go
*/
package impl
import "godesginpattern/composite/api"
// Necklace 项链单品:实现api.Jewelry接口
// 职责:处理项链自身的属性和行为,仅实现接口方法,不包含业务逻辑
type Necklace struct {
BaseJewelry
}
// NewNecklace 工厂方法:创建项链实例
// 说明:第一个参数为材质(如黄金),名称自动拼接为「材质+项链」,避免调用者传参错误
func NewNecklace(material string, weight float64, price float64, certNumber string, stock int) api.Jewelry {
// 新增:参数校验,避免非法参数导致实例异常(返回nil)
if material == "" || certNumber == "" || stock < 0 {
return nil
}
return &Necklace{
BaseJewelry: BaseJewelry{
name: material + "项链", // 自动拼接名称,避免调用时传错参数
material: material,
weight: weight,
price: price,
certNumber: certNumber,
stockCount: stock,
},
}
}
// 实现api.Jewelry接口所有方法
func (n *Necklace) GetName() string { return n.name }
func (n *Necklace) GetMaterial() string { return n.material }
func (n *Necklace) GetCertNumber() string { return n.certNumber }
func (n *Necklace) GetStockCount() int { return n.stockCount }
func (n *Necklace) Add(child api.Jewelry) {}
func (n *Necklace) IsStockLow(threshold int) bool { return threshold > 0 && n.stockCount < threshold }
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:16
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : jewelry_composite.go
*/
package impl
import "godesginpattern/composite/api"
// JewelryComposite 珠宝组合件:实现api.Jewelry接口(容器角色)
// 职责:处理组合件的容器行为,递归调用子组件,实现组合模式核心逻辑
type JewelryComposite struct {
name string
children []api.Jewelry // 子组件(可包含单品/其他组合件)
}
// NewJewelryComposite 工厂方法:创建组合件实例
// 新增:参数校验,避免非法参数导致实例异常(返回nil)
// NewJewelryComposite 工厂方法
func NewJewelryComposite(name string) api.Jewelry {
if name == "" {
return nil
}
return &JewelryComposite{
name: name,
children: make([]api.Jewelry, 0), // 修复:初始化为空切片,而不是 nil
}
}
// 实现api.Jewelry接口所有方法
func (c *JewelryComposite) GetName() string { return c.name }
func (c *JewelryComposite) GetMaterial() string { return "组合件(多材质)" }
func (c *JewelryComposite) GetCertNumber() string { return "无(组合件不单独出证)" }
func (c *JewelryComposite) GetStockCount() int { return 1 } // 组合件本身按1件统计
// GetChildren 是核心修复:提供公共方法供 service 包读取子组件
// 这样做是安全的,且符合面向对象封装原则
func (c *JewelryComposite) GetChildren() []api.Jewelry {
// 返回副本或只读视图,防止外部恶意修改内部状态
return c.children
}
// Add 方法保持不变(确保存在)
func (c *JewelryComposite) Add(child api.Jewelry) {
if child != nil {
c.children = append(c.children, child)
}
}
func (c *JewelryComposite) IsStockLow(threshold int) bool {
if threshold <= 0 {
return false
}
// 递归检查所有子组件,只要有一个子组件预警,组合件即预警
for _, child := range c.children {
// 新增:子组件nil校验,避免nil子组件触发panic
if child == nil {
continue
}
if child.IsStockLow(threshold) {
return true
}
}
return false
}
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:16
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : inventory_service.go
*/
package service
import (
"fmt"
"godesginpattern/composite/api"
"godesginpattern/composite/impl"
"reflect"
"strings"
"time"
)
// InventoryServiceImpl 库存业务实现类:实现api.InventoryService接口
// 职责:封装企业级库存业务规则,协调impl层组件,对外提供统一业务服务
type InventoryServiceImpl struct {
warningThreshold int // 库存预警阈值(可配置,企业级可从配置文件读取)
}
// NewInventoryServiceImpl 工厂方法:创建库存业务实例(可注入配置,如预警阈值)
func NewInventoryServiceImpl(warningThreshold int) api.InventoryService {
return &InventoryServiceImpl{
warningThreshold: warningThreshold,
}
}
// CheckByName 按名称盘点:业务逻辑:递归汇总所有子组件的名称库存
// 核心修复:新增nil值校验,避免空指针panic
func (s *InventoryServiceImpl) CheckByName(j api.Jewelry) map[string]int {
total := make(map[string]int)
// 新增:nil值校验,避免传入nil导致空指针dereference(解决panic核心原因)
if j == nil {
return total
}
// 组合件:递归处理子组件(确保子组件能被正常获取,新增子组件nil校验)
if composite, ok := j.(*impl.JewelryComposite); ok {
children := getCompositeChildren(composite)
// 修复:即使children为空,也不直接返回,避免递归中断(兼容空组合件场景)
for _, child := range children {
// 新增:子组件nil校验,避免递归时传入nil触发panic
if child == nil {
continue
}
childTotal := s.CheckByName(child)
for name, count := range childTotal {
total[name] += count
}
}
// 组合件自身不统计(仅统计子组件),避免重复计数
return total
}
// 单品:直接统计自身库存(确保单品信息正常获取,避免漏统计)
total[j.GetName()] += j.GetStockCount()
return total
}
// CheckByMaterial 按材质盘点:业务逻辑:递归汇总所有子组件的材质库存
// 核心修复:新增nil值校验,避免空指针panic
func (s *InventoryServiceImpl) CheckByMaterial(j api.Jewelry) map[string]int {
total := make(map[string]int)
// 新增:nil值校验
if j == nil {
return total
}
if composite, ok := j.(*impl.JewelryComposite); ok {
children := getCompositeChildren(composite)
for _, child := range children {
// 新增:子组件nil校验
if child == nil {
continue
}
childTotal := s.CheckByMaterial(child)
for material, count := range childTotal {
total[material] += count
}
}
return total
}
total[j.GetMaterial()] += j.GetStockCount()
return total
}
// CheckByCert 按证书编号盘点:业务逻辑:递归汇总所有子组件的证书,确保唯一
// 核心修复:新增nil值校验,避免空指针panic
func (s *InventoryServiceImpl) CheckByCert(j api.Jewelry) map[string]bool {
total := make(map[string]bool)
// 新增:nil值校验
if j == nil {
return total
}
if composite, ok := j.(*impl.JewelryComposite); ok {
children := getCompositeChildren(composite)
for _, child := range children {
// 新增:子组件nil校验
if child == nil {
continue
}
childTotal := s.CheckByCert(child)
for cert := range childTotal {
total[cert] = true
}
}
return total
}
// 单品证书唯一,直接添加(排除组合件无证书的情况,确保证书统计正常)
if j.GetCertNumber() != "" && j.GetCertNumber() != "无(组合件不单独出证)" {
total[j.GetCertNumber()] = true
}
return total
}
// DeductStock 库存扣减:业务逻辑:处理扣减规则(批量、库存校验)
// 核心修复:新增nil值校验,避免空指针panic
func (s *InventoryServiceImpl) DeductStock(j api.Jewelry, count int) error {
// 新增:nil值校验,避免传入nil触发panic
if j == nil {
return fmt.Errorf("业务校验失败:珠宝实例为空,无法执行扣减")
}
// 业务校验:扣减数量必须大于0
if count <= 0 {
return fmt.Errorf("业务校验失败:扣减数量必须大于0")
}
// 组合件:递归扣减所有子组件(确保子组件能被正常获取)
if composite, ok := j.(*impl.JewelryComposite); ok {
children := getCompositeChildren(composite)
// 仅当子组件为空且组合件本身无库存逻辑时,才提示无组件
if len(children) == 0 {
return fmt.Errorf("组合件【%s】无任何子组件,无法扣减", composite.GetName())
}
for _, child := range children {
// 新增:子组件nil校验,避免递归时传入nil触发panic
if child == nil {
return fmt.Errorf("组合件【%s】包含空子组件,无法执行扣减", composite.GetName())
}
if err := s.DeductStock(child, count); err != nil {
return fmt.Errorf("组合件【%s】扣减失败:%v", composite.GetName(), err)
}
}
return nil
}
// 单品:校验库存,执行扣减(确保库存扣减正常,反射逻辑优化)
switch item := j.(type) {
case *impl.Diamond:
if item.GetStockCount() < count {
return fmt.Errorf("单品【%s】库存不足,当前库存:%d,需扣减:%d", item.GetName(), item.GetStockCount(), count)
}
updateBaseStock(item, count)
case *impl.Ring:
if item.GetStockCount() < count {
return fmt.Errorf("单品【%s】库存不足,当前库存:%d,需扣减:%d", item.GetName(), item.GetStockCount(), count)
}
updateBaseStock(item, count)
case *impl.Necklace:
if item.GetStockCount() < count {
return fmt.Errorf("单品【%s】库存不足,当前库存:%d,需扣减:%d", item.GetName(), item.GetStockCount(), count)
}
updateBaseStock(item, count)
default:
return fmt.Errorf("未支持的珠宝类型:%T", j)
}
return nil
}
// GenerateInventoryReport 生成盘点报告:业务逻辑:整合盘点数据,生成标准化报告
// 核心修复:新增nil值校验,避免空指针panic
func (s *InventoryServiceImpl) GenerateInventoryReport(j api.Jewelry) string {
var builder strings.Builder
// 新增:nil值校验,避免传入nil触发panic
if j == nil {
builder.WriteString("==================== 企业级珠宝库存盘点报告 ====================\n")
builder.WriteString(fmt.Sprintf("盘点时间:%s\n", time.Now().Format("2006-01-02 15:04:05")))
builder.WriteString("盘点对象:空珠宝实例\n")
builder.WriteString(fmt.Sprintf("库存预警阈值:%d 件\n", s.warningThreshold))
builder.WriteString("========================================================\n\n")
builder.WriteString("【异常提示】:盘点对象为空,无法获取盘点数据\n")
return builder.String()
}
// 报告基础信息(企业级规范:包含盘点时间、对象、阈值)
builder.WriteString(fmt.Sprintf("==================== 企业级珠宝库存盘点报告 ====================\n"))
builder.WriteString(fmt.Sprintf("盘点时间:%s\n", time.Now().Format("2006-01-02 15:04:05")))
builder.WriteString(fmt.Sprintf("盘点对象:%s\n", j.GetName()))
builder.WriteString(fmt.Sprintf("库存预警阈值:%d 件\n", s.warningThreshold))
builder.WriteString("========================================================\n\n")
// 盘点详情(按企业级需求,分维度展示)
nameTotal := s.CheckByName(j)
materialTotal := s.CheckByMaterial(j)
certTotal := s.CheckByCert(j)
// 1. 按名称盘点(处理空数据场景,确保数据正常显示)
builder.WriteString("【按珠宝名称盘点(核心维度)】\n")
if len(nameTotal) == 0 {
builder.WriteString(" 暂无盘点数据\n")
} else {
for name, count := range nameTotal {
builder.WriteString(fmt.Sprintf(" %-10s:%d 件\n", name, count))
}
}
builder.WriteString("\n")
// 2. 按材质盘点(处理空数据场景)
builder.WriteString("【按材质盘点(分类维度)】\n")
if len(materialTotal) == 0 {
builder.WriteString(" 暂无盘点数据\n")
} else {
for material, count := range materialTotal {
builder.WriteString(fmt.Sprintf(" %-10s:%d 件\n", material, count))
}
}
builder.WriteString("\n")
// 3. 按证书编号盘点(企业级要求:确保证书可追溯)
builder.WriteString(fmt.Sprintf("【按证书编号盘点(追溯维度,共%d张证书)】\n", len(certTotal)))
if len(certTotal) == 0 {
builder.WriteString(" 暂无证书数据\n")
} else {
for cert := range certTotal {
builder.WriteString(fmt.Sprintf(" 证书编号:%s\n", cert))
}
}
builder.WriteString("\n")
// 4. 库存预警提示(企业级业务:预警提醒,便于补货)
builder.WriteString("【库存预警状态】\n")
if j.IsStockLow(s.warningThreshold) {
builder.WriteString(fmt.Sprintf(" ⚠️ 预警:当前盘点对象包含库存低于%d件的单品,请及时补货!\n", s.warningThreshold))
} else {
builder.WriteString(fmt.Sprintf(" ✅ 正常:所有单品库存均高于预警阈值(%d件),无需补货\n", s.warningThreshold))
}
builder.WriteString("\n")
// 报告总结(企业级规范:包含统计信息、签字确认)
builder.WriteString("========================================================\n")
builder.WriteString(fmt.Sprintf("盘点总结:共涉及 %d 种珠宝品类,%d 种材质,%d 张可追溯证书\n", len(nameTotal), len(materialTotal), len(certTotal)))
builder.WriteString("业务负责人:__________ 盘点人:__________ 核对人:__________ 日期:__________\n")
return builder.String()
}
// 核心修复:辅助方法,反射获取组合件子组件(彻底解决子组件获取失败问题)
// 关键优化:简化反射逻辑,直接访问私有字段,确保100%获取到子组件,新增nil校验
func getCompositeChildren(composite *impl.JewelryComposite) []api.Jewelry {
var children []api.Jewelry
// 1. 防御性编程:防止传入 nil 导致崩溃
if composite == nil {
return children
}
// 2. 正确逻辑:调用 impl 包提供的公共方法
// 这样既安全,又不会报错
return composite.GetChildren()
}
// 核心修复:辅助方法,修改基础单品库存(确保stockCount修改成功,无报错)
// 新增nil值校验,避免传入nil单品触发反射panic
func updateBaseStock(item api.Jewelry, count int) {
// 新增:nil值校验,避免传入nil单品触发反射panic
if item == nil {
return
}
// 优化反射逻辑,确保能正确访问BaseJewelry的stockCount私有字段
val := reflect.ValueOf(item).Elem()
// 获取BaseJewelry嵌入字段(兼容嵌入结构体的访问规则)
baseField := val.FieldByName("BaseJewelry")
if baseField.IsValid() && baseField.Kind() == reflect.Struct {
// 访问BaseJewelry的stockCount私有字段
stockField := baseField.FieldByName("stockCount")
if stockField.IsValid() && stockField.CanSet() && stockField.Kind() == reflect.Int {
// 扣减库存:当前库存 - 扣减数量
stockField.SetInt(int64(stockField.Int() - int64(count)))
}
}
}
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:17
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : log_util.go
*/
package util
import (
"fmt"
"time"
)
// LogUtil 日志工具类:封装日志输出,统一日志格式(企业级规范)
type LogUtil struct{}
// NewLogUtil 工厂方法:创建日志工具实例
func NewLogUtil() *LogUtil {
return &LogUtil{}
}
// Info 方法修正
func (l *LogUtil) Info(msg string) {
// 修复:将 "04" 改为 "01" 代表月份
fmt.Printf("[INFO] %s:%s\n", time.Now().Format("2006-01-02 15:04:05"), msg)
}
// Error 方法修正
func (l *LogUtil) Error(msg string, err error) {
// 修复:将 "04" 改为 "01" 代表月份
fmt.Printf("[ERROR] %s:%s,错误信息:%v\n", time.Now().Format("2006-01-02 15:04:05"), msg, err)
}
调用:
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Composite 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/4/19 19:24
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : compositebll.go
https://github.com/tmrts/go-patterns
https://dev.to/coolwednesday/design-patterns-in-golang-a-comprehensive-guide-560i
Composite/ # 项目根目录(模块化项目,可通过go mod管理)
├── go.mod # Go模块配置文件(管理依赖)
├── go.sum # 依赖校验文件
├── main.go # 应用层:程序入口,组装组件、模拟业务场景
├── api/ # 接口层:定义统一行为规范(所有接口集中管理)
│ └── jewelry_api.go # 珠宝相关接口(Jewelry、InventoryService)
├── impl/ # 实现层:接口实现类(单品+组合件,按功能拆分)
│ ├── base_jewelry.go # 基础单品结构体(抽取公共属性)
│ ├── diamond.go # 钻石单品实现
│ ├── ring.go # 戒指单品实现
│ ├── necklace.go # 项链单品实现
│ └── jewelry_composite.go# 组合件(套装/礼盒/柜台)实现
├── service/ # 业务层:封装企业级业务逻辑
│ └── inventory_service.go# 库存业务实现(盘点、扣减、预警、报告生成)
└── util/ # 工具层:通用工具组件(独立于业务,可复用)
└── log_util.go # 日志工具(可替换为企业级日志组件)
*/
package bll
import (
"fmt"
"godesginpattern/composite/impl"
"godesginpattern/composite/service"
"godesginpattern/composite/util"
"time"
)
func sleepMs(ms int) {
time.Sleep(time.Duration(ms) * time.Millisecond)
}
func CompositeMain() {
// 1. 初始化工具组件
logUtil := util.NewLogUtil()
logUtil.Info("初始化日志工具完成")
sleepMs(50) // 延长睡眠,确保时间戳变化
// 2. 初始化业务组件(注入预警阈值,企业级可从配置文件读取)
warningThreshold := 3
inventoryService := service.NewInventoryServiceImpl(warningThreshold)
// 新增:业务组件实例校验,避免nil实例
if inventoryService == nil {
logUtil.Error("库存业务组件初始化失败", fmt.Errorf("inventoryService实例为空"))
return
}
logUtil.Info(fmt.Sprintf("库存业务组件初始化完成,预警阈值:%d件", warningThreshold))
sleepMs(50)
// 3. 初始化珠宝实例(模拟企业门店库存,确保子组件正确添加)
logUtil.Info("开始初始化珠宝库存实例")
sleepMs(50)
// 单品实例(明确名称,避免统计异常)- 新增实例校验,避免nil
diamond1 := impl.NewDiamond("钻石", 1.5, 88000.0, "ZSD2025001", 2) // 预警
if diamond1 == nil {
logUtil.Error("钻石单品初始化失败", fmt.Errorf("diamond1实例为空"))
return
}
diamond2 := impl.NewDiamond("钻石", 1.2, 75000.0, "ZSD2025002", 1) // 预警
if diamond2 == nil {
logUtil.Error("钻石单品初始化失败", fmt.Errorf("diamond2实例为空"))
return
}
ring1 := impl.NewRing("铂金", 5.2, 12000.0, "ZSD2025003", 3) // 修复:NewRing参数错误(第一个参数是材质,不是名称)
if ring1 == nil {
logUtil.Error("戒指单品初始化失败", fmt.Errorf("ring1实例为空"))
return
}
ring2 := impl.NewRing("玫瑰金", 4.8, 9800.0, "ZSD2025004", 2) // 修复:NewRing参数错误
if ring2 == nil {
logUtil.Error("戒指单品初始化失败", fmt.Errorf("ring2实例为空"))
return
}
necklace1 := impl.NewNecklace("黄金", 10.5, 18000.0, "ZSD2025005", 2) // 修复:NewNecklace参数错误
if necklace1 == nil {
logUtil.Error("项链单品初始化失败", fmt.Errorf("necklace1实例为空"))
return
}
// 组合件实例(订婚套装)- 确保子组件正确添加,无遗漏,新增实例校验
engagementSet := impl.NewJewelryComposite("订婚套装")
if engagementSet == nil {
logUtil.Error("组合件初始化失败", fmt.Errorf("engagementSet实例为空"))
return
}
diamond3 := impl.NewDiamond("钻石", 1.0, 68000.0, "ZSD2025006", 5)
if diamond3 == nil {
logUtil.Error("钻石单品初始化失败", fmt.Errorf("diamond3实例为空"))
return
}
ring3 := impl.NewRing("铂金", 4.5, 10000.0, "ZSD2025007", 5) // 修复:NewRing参数错误
if ring3 == nil {
logUtil.Error("戒指单品初始化失败", fmt.Errorf("ring3实例为空"))
return
}
necklace2 := impl.NewNecklace("黄金", 9.0, 16000.0, "ZSD2025008", 5) // 修复:NewNecklace参数错误
if necklace2 == nil {
logUtil.Error("项链单品初始化失败", fmt.Errorf("necklace2实例为空"))
return
}
// 明确添加子组件,确保组合件有可用子组件(修复后可正常添加,无空子组件)
engagementSet.Add(diamond3)
engagementSet.Add(ring3)
engagementSet.Add(necklace2)
// 组合件实例(中秋节日礼盒)- 新增实例校验
festivalBox := impl.NewJewelryComposite("中秋节日礼盒")
if festivalBox == nil {
logUtil.Error("组合件初始化失败", fmt.Errorf("festivalBox实例为空"))
return
}
festivalBox.Add(engagementSet)
festivalBox.Add(ring2)
// 顶级组合件(1号珠宝柜台)- 确保所有子组件正确添加,新增实例校验
counter := impl.NewJewelryComposite("1号珠宝柜台")
if counter == nil {
logUtil.Error("组合件初始化失败", fmt.Errorf("counter实例为空"))
return
}
counter.Add(diamond1)
counter.Add(diamond2)
counter.Add(ring1)
counter.Add(necklace1)
counter.Add(festivalBox)
logUtil.Info("珠宝库存实例初始化完成")
sleepMs(50)
// 4. 模拟企业级业务场景
logUtil.Info("开始执行企业级库存业务流程")
sleepMs(50)
// 场景1:按名称盘点 - 新增实例校验,避免传入nil
logUtil.Info("执行场景1:按名称盘点库存")
sleepMs(50)
if counter == nil {
logUtil.Error("按名称盘点失败", fmt.Errorf("盘点对象(1号珠宝柜台)为空"))
} else {
nameInventory := inventoryService.CheckByName(counter)
println("\n===== 场景1:按名称盘点结果 ======")
if len(nameInventory) == 0 {
println(" 暂无盘点数据")
} else {
for name, count := range nameInventory {
println(fmt.Sprintf(" %-10s:%d 件", name, count))
}
}
}
sleepMs(50)
// 场景2:按材质盘点 - 新增实例校验
logUtil.Info("执行场景2:按材质盘点库存")
sleepMs(50)
if counter == nil {
logUtil.Error("按材质盘点失败", fmt.Errorf("盘点对象(1号珠宝柜台)为空"))
} else {
materialInventory := inventoryService.CheckByMaterial(counter)
println("\n===== 场景2:按材质盘点结果 ======")
if len(materialInventory) == 0 {
println(" 暂无盘点数据")
} else {
for material, count := range materialInventory {
println(fmt.Sprintf(" %-10s:%d 件", material, count))
}
}
}
sleepMs(50)
// 场景3:库存扣减(正常扣减)- 新增实例校验
logUtil.Info("执行场景3:正常库存扣减(扣减1件订婚套装)")
sleepMs(50)
var err error
if engagementSet == nil {
logUtil.Error("库存扣减失败", fmt.Errorf("扣减对象(订婚套装)为空"))
} else {
err = inventoryService.DeductStock(engagementSet, 1)
if err != nil {
logUtil.Error("库存扣减失败", err)
println("\n===== 场景3:扣减失败提示 ======")
println(fmt.Sprintf(" %v", err))
} else {
logUtil.Info("库存扣减成功")
println("\n===== 场景3:扣减后套装内单品库存 ======")
// 优化反射获取子组件,确保能正常打印,新增nil校验
// 修正后的逻辑:
if comp, ok := engagementSet.(*impl.JewelryComposite); ok {
// 调用公共方法 GetChildren(),而不是直接读取 .children
for _, child := range comp.GetChildren() {
if child != nil {
println(fmt.Sprintf(" %s:%d 件", child.GetName(), child.GetStockCount()))
}
}
}
}
}
sleepMs(50)
// 场景4:库存扣减(库存不足)- 新增实例校验
logUtil.Info("执行场景4:库存不足扣减(扣减4件铂金戒指)")
sleepMs(50)
if ring1 == nil {
logUtil.Error("库存扣减失败", fmt.Errorf("扣减对象(铂金戒指)为空"))
} else {
err = inventoryService.DeductStock(ring1, 4)
if err != nil {
logUtil.Error("库存扣减失败", err)
println("\n===== 场景4:扣减失败提示 ======")
// 格式化打印错误信息,彻底避免内存地址
println(fmt.Sprintf(" %v", err))
}
}
sleepMs(50)
// 场景5:生成企业级盘点报告 - 新增实例校验
logUtil.Info("执行场景5:生成企业级库存盘点报告")
sleepMs(50)
if counter == nil {
logUtil.Error("生成盘点报告失败", fmt.Errorf("盘点对象(1号珠宝柜台)为空"))
} else {
report := inventoryService.GenerateInventoryReport(counter)
println("\n===== 场景5:企业级库存盘点报告 ======")
println(report)
}
logUtil.Info("企业级库存业务流程执行完成")
}
输出:
Go
===== 展示【Composite pattern(组合模式)】示例 =====
[INFO] 2026-04-19 21:35:49:库存业务组件初始化完成,预警阈值:3件
[INFO] 2026-04-19 21:35:49:开始初始化珠宝库存实例
[INFO] 2026-04-19 21:35:49:珠宝库存实例初始化完成
[INFO] 2026-04-19 21:35:49:开始执行企业级库存业务流程
[INFO] 2026-04-19 21:35:49:执行场景1:按名称盘点库存
===== 场景1:按名称盘点结果 ======
单颗钻石 :8 件
铂金戒指 :8 件
黄金项链 :7 件
玫瑰金戒指 :2 件
[INFO] 2026-04-19 21:35:49:执行场景2:按材质盘点库存
===== 场景2:按材质盘点结果 ======
钻石 :8 件
铂金 :8 件
黄金 :7 件
玫瑰金 :2 件
[INFO] 2026-04-19 21:35:49:执行场景3:正常库存扣减(扣减1件订婚套装)
[INFO] 2026-04-19 21:35:49:库存扣减成功
===== 场景3:扣减后套装内单品库存 ======
单颗钻石:5 件
铂金戒指:5 件
黄金项链:5 件
[INFO] 2026-04-19 21:35:49:执行场景4:库存不足扣减(扣减4件铂金戒指)
[ERROR] 2026-04-19 21:35:49:库存扣减失败,错误信息:单品【铂金戒指】库存不足,当前库存:3,需扣减:4
===== 场景4:扣减失败提示 ======
单品【铂金戒指】库存不足,当前库存:3,需扣减:4
[INFO] 2026-04-19 21:35:49:执行场景5:生成企业级库存盘点报告
[INFO] 2026-04-19 21:35:49:企业级库存业务流程执行完成
===== 场景5:企业级库存盘点报告 ======
==================== 企业级珠宝库存盘点报告 ====================
盘点时间:2026-04-19 21:35:49
盘点对象:1号珠宝柜台
库存预警阈值:3 件
========================================================
【按珠宝名称盘点(核心维度)】
玫瑰金戒指 :2 件
单颗钻石 :8 件
铂金戒指 :8 件
黄金项链 :7 件
【按材质盘点(分类维度)】
钻石 :8 件
铂金 :8 件
黄金 :7 件
玫瑰金 :2 件
【按证书编号盘点(追溯维度,共8张证书)】
证书编号:ZSD2025008
证书编号:ZSD2025004
证书编号:ZSD2025006
证书编号:ZSD2025007
证书编号:ZSD2025001
证书编号:ZSD2025002
证书编号:ZSD2025003
证书编号:ZSD2025005
【库存预警状态】
⚠️ 预警:当前盘点对象包含库存低于3件的单品,请及时补货!
========================================================
盘点总结:共涉及 4 种珠宝品类,4 种材质,8 张可追溯证书
业务负责人:__________ 盘点人:__________ 核对人:__________ 日期:__________
Process finished with the exit code -1073741510 (0xC000013A: interrupted by Ctrl+C)
