go: Composite Pattern

项目结构:

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)
相关推荐
敖正炀3 小时前
行为型模式-状态模式
设计模式
XMYX-03 小时前
18 - Go 等待协程:WaitGroup 使用与坑
开发语言·golang
我的征途是星辰大海。3 小时前
设计模式(学习笔记)(第一章)
笔记·学习·设计模式
XMYX-03 小时前
19 - Go 并发限制:限流与控制并发数
开发语言·golang
敖正炀3 小时前
创建型模式-抽象工厂模式
设计模式
敖正炀3 小时前
创建型模式-工厂方法模式
设计模式
敖正炀3 小时前
创造型模式-单例模式
设计模式
ximu_polaris4 小时前
设计模式(C++)-结构型模式-组合模式
c++·设计模式·组合模式
geovindu4 小时前
go: Singleton Pattern
单例模式·设计模式·golang