feat(架构): 完善基础架构设计
This commit is contained in:
587
internal/shared/hooks/hook_system.go
Normal file
587
internal/shared/hooks/hook_system.go
Normal file
@@ -0,0 +1,587 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// HookPriority 钩子优先级
|
||||
type HookPriority int
|
||||
|
||||
const (
|
||||
// PriorityLowest 最低优先级
|
||||
PriorityLowest HookPriority = 0
|
||||
// PriorityLow 低优先级
|
||||
PriorityLow HookPriority = 25
|
||||
// PriorityNormal 普通优先级
|
||||
PriorityNormal HookPriority = 50
|
||||
// PriorityHigh 高优先级
|
||||
PriorityHigh HookPriority = 75
|
||||
// PriorityHighest 最高优先级
|
||||
PriorityHighest HookPriority = 100
|
||||
)
|
||||
|
||||
// HookFunc 钩子函数类型
|
||||
type HookFunc func(ctx context.Context, data interface{}) error
|
||||
|
||||
// Hook 钩子定义
|
||||
type Hook struct {
|
||||
Name string
|
||||
Func HookFunc
|
||||
Priority HookPriority
|
||||
Async bool
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// HookResult 钩子执行结果
|
||||
type HookResult struct {
|
||||
HookName string `json:"hook_name"`
|
||||
Success bool `json:"success"`
|
||||
Duration time.Duration `json:"duration"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// HookConfig 钩子配置
|
||||
type HookConfig struct {
|
||||
// 默认超时时间
|
||||
DefaultTimeout time.Duration
|
||||
// 是否记录执行时间
|
||||
TrackDuration bool
|
||||
// 错误处理策略
|
||||
ErrorStrategy ErrorStrategy
|
||||
}
|
||||
|
||||
// ErrorStrategy 错误处理策略
|
||||
type ErrorStrategy int
|
||||
|
||||
const (
|
||||
// ContinueOnError 遇到错误继续执行
|
||||
ContinueOnError ErrorStrategy = iota
|
||||
// StopOnError 遇到错误停止执行
|
||||
StopOnError
|
||||
// CollectErrors 收集所有错误
|
||||
CollectErrors
|
||||
)
|
||||
|
||||
// DefaultHookConfig 默认钩子配置
|
||||
func DefaultHookConfig() HookConfig {
|
||||
return HookConfig{
|
||||
DefaultTimeout: 30 * time.Second,
|
||||
TrackDuration: true,
|
||||
ErrorStrategy: ContinueOnError,
|
||||
}
|
||||
}
|
||||
|
||||
// HookSystem 钩子系统
|
||||
type HookSystem struct {
|
||||
hooks map[string][]*Hook
|
||||
config HookConfig
|
||||
logger *zap.Logger
|
||||
mutex sync.RWMutex
|
||||
stats map[string]*HookStats
|
||||
}
|
||||
|
||||
// HookStats 钩子统计
|
||||
type HookStats struct {
|
||||
TotalExecutions int `json:"total_executions"`
|
||||
Successes int `json:"successes"`
|
||||
Failures int `json:"failures"`
|
||||
TotalDuration time.Duration `json:"total_duration"`
|
||||
AverageDuration time.Duration `json:"average_duration"`
|
||||
LastExecution time.Time `json:"last_execution"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
}
|
||||
|
||||
// NewHookSystem 创建钩子系统
|
||||
func NewHookSystem(config HookConfig, logger *zap.Logger) *HookSystem {
|
||||
return &HookSystem{
|
||||
hooks: make(map[string][]*Hook),
|
||||
config: config,
|
||||
logger: logger,
|
||||
stats: make(map[string]*HookStats),
|
||||
}
|
||||
}
|
||||
|
||||
// Register 注册钩子
|
||||
func (hs *HookSystem) Register(event string, hook *Hook) error {
|
||||
if hook.Name == "" {
|
||||
return fmt.Errorf("hook name cannot be empty")
|
||||
}
|
||||
|
||||
if hook.Func == nil {
|
||||
return fmt.Errorf("hook function cannot be nil")
|
||||
}
|
||||
|
||||
if hook.Timeout == 0 {
|
||||
hook.Timeout = hs.config.DefaultTimeout
|
||||
}
|
||||
|
||||
hs.mutex.Lock()
|
||||
defer hs.mutex.Unlock()
|
||||
|
||||
// 检查是否已经注册了同名钩子
|
||||
for _, existingHook := range hs.hooks[event] {
|
||||
if existingHook.Name == hook.Name {
|
||||
return fmt.Errorf("hook %s already registered for event %s", hook.Name, event)
|
||||
}
|
||||
}
|
||||
|
||||
hs.hooks[event] = append(hs.hooks[event], hook)
|
||||
|
||||
// 按优先级排序
|
||||
sort.Slice(hs.hooks[event], func(i, j int) bool {
|
||||
return hs.hooks[event][i].Priority > hs.hooks[event][j].Priority
|
||||
})
|
||||
|
||||
// 初始化统计
|
||||
hookKey := fmt.Sprintf("%s.%s", event, hook.Name)
|
||||
hs.stats[hookKey] = &HookStats{}
|
||||
|
||||
hs.logger.Info("Registered hook",
|
||||
zap.String("event", event),
|
||||
zap.String("hook_name", hook.Name),
|
||||
zap.Int("priority", int(hook.Priority)),
|
||||
zap.Bool("async", hook.Async))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterFunc 注册钩子函数(简化版)
|
||||
func (hs *HookSystem) RegisterFunc(event, name string, priority HookPriority, fn HookFunc) error {
|
||||
hook := &Hook{
|
||||
Name: name,
|
||||
Func: fn,
|
||||
Priority: priority,
|
||||
Async: false,
|
||||
Timeout: hs.config.DefaultTimeout,
|
||||
}
|
||||
|
||||
return hs.Register(event, hook)
|
||||
}
|
||||
|
||||
// RegisterAsyncFunc 注册异步钩子函数
|
||||
func (hs *HookSystem) RegisterAsyncFunc(event, name string, priority HookPriority, fn HookFunc) error {
|
||||
hook := &Hook{
|
||||
Name: name,
|
||||
Func: fn,
|
||||
Priority: priority,
|
||||
Async: true,
|
||||
Timeout: hs.config.DefaultTimeout,
|
||||
}
|
||||
|
||||
return hs.Register(event, hook)
|
||||
}
|
||||
|
||||
// Unregister 取消注册钩子
|
||||
func (hs *HookSystem) Unregister(event, hookName string) error {
|
||||
hs.mutex.Lock()
|
||||
defer hs.mutex.Unlock()
|
||||
|
||||
hooks := hs.hooks[event]
|
||||
for i, hook := range hooks {
|
||||
if hook.Name == hookName {
|
||||
// 删除钩子
|
||||
hs.hooks[event] = append(hooks[:i], hooks[i+1:]...)
|
||||
|
||||
// 删除统计
|
||||
hookKey := fmt.Sprintf("%s.%s", event, hookName)
|
||||
delete(hs.stats, hookKey)
|
||||
|
||||
hs.logger.Info("Unregistered hook",
|
||||
zap.String("event", event),
|
||||
zap.String("hook_name", hookName))
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("hook %s not found for event %s", hookName, event)
|
||||
}
|
||||
|
||||
// Trigger 触发事件
|
||||
func (hs *HookSystem) Trigger(ctx context.Context, event string, data interface{}) ([]HookResult, error) {
|
||||
hs.mutex.RLock()
|
||||
hooks := make([]*Hook, len(hs.hooks[event]))
|
||||
copy(hooks, hs.hooks[event])
|
||||
hs.mutex.RUnlock()
|
||||
|
||||
if len(hooks) == 0 {
|
||||
hs.logger.Debug("No hooks registered for event", zap.String("event", event))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hs.logger.Debug("Triggering event",
|
||||
zap.String("event", event),
|
||||
zap.Int("hook_count", len(hooks)))
|
||||
|
||||
results := make([]HookResult, 0, len(hooks))
|
||||
var errors []error
|
||||
|
||||
for _, hook := range hooks {
|
||||
result := hs.executeHook(ctx, event, hook, data)
|
||||
results = append(results, result)
|
||||
|
||||
if !result.Success {
|
||||
err := fmt.Errorf("hook %s failed: %s", hook.Name, result.Error)
|
||||
errors = append(errors, err)
|
||||
|
||||
// 根据错误策略决定是否继续
|
||||
if hs.config.ErrorStrategy == StopOnError {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理错误
|
||||
if len(errors) > 0 {
|
||||
switch hs.config.ErrorStrategy {
|
||||
case StopOnError:
|
||||
return results, errors[0]
|
||||
case CollectErrors:
|
||||
return results, fmt.Errorf("multiple hook errors: %v", errors)
|
||||
case ContinueOnError:
|
||||
// 继续执行,但记录错误
|
||||
hs.logger.Warn("Some hooks failed but continuing execution",
|
||||
zap.String("event", event),
|
||||
zap.Int("error_count", len(errors)))
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// executeHook 执行单个钩子
|
||||
func (hs *HookSystem) executeHook(ctx context.Context, event string, hook *Hook, data interface{}) HookResult {
|
||||
hookKey := fmt.Sprintf("%s.%s", event, hook.Name)
|
||||
start := time.Now()
|
||||
|
||||
result := HookResult{
|
||||
HookName: hook.Name,
|
||||
Success: false,
|
||||
}
|
||||
|
||||
// 更新统计
|
||||
defer func() {
|
||||
result.Duration = time.Since(start)
|
||||
hs.updateStats(hookKey, result)
|
||||
}()
|
||||
|
||||
if hook.Async {
|
||||
// 异步执行
|
||||
go func() {
|
||||
hs.doExecuteHook(ctx, hook, data)
|
||||
}()
|
||||
result.Success = true // 异步执行总是认为成功
|
||||
return result
|
||||
}
|
||||
|
||||
// 同步执行
|
||||
err := hs.doExecuteHook(ctx, hook, data)
|
||||
if err != nil {
|
||||
result.Error = err.Error()
|
||||
hs.logger.Error("Hook execution failed",
|
||||
zap.String("event", event),
|
||||
zap.String("hook_name", hook.Name),
|
||||
zap.Error(err))
|
||||
} else {
|
||||
result.Success = true
|
||||
hs.logger.Debug("Hook executed successfully",
|
||||
zap.String("event", event),
|
||||
zap.String("hook_name", hook.Name))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// doExecuteHook 实际执行钩子
|
||||
func (hs *HookSystem) doExecuteHook(ctx context.Context, hook *Hook, data interface{}) error {
|
||||
// 设置超时上下文
|
||||
hookCtx, cancel := context.WithTimeout(ctx, hook.Timeout)
|
||||
defer cancel()
|
||||
|
||||
// 在goroutine中执行,以便处理超时
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
errChan <- fmt.Errorf("hook panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
errChan <- hook.Func(hookCtx, data)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case <-hookCtx.Done():
|
||||
return fmt.Errorf("hook execution timeout after %v", hook.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// updateStats 更新统计信息
|
||||
func (hs *HookSystem) updateStats(hookKey string, result HookResult) {
|
||||
hs.mutex.Lock()
|
||||
defer hs.mutex.Unlock()
|
||||
|
||||
stats, exists := hs.stats[hookKey]
|
||||
if !exists {
|
||||
stats = &HookStats{}
|
||||
hs.stats[hookKey] = stats
|
||||
}
|
||||
|
||||
stats.TotalExecutions++
|
||||
stats.LastExecution = time.Now()
|
||||
|
||||
if result.Success {
|
||||
stats.Successes++
|
||||
} else {
|
||||
stats.Failures++
|
||||
stats.LastError = result.Error
|
||||
}
|
||||
|
||||
if hs.config.TrackDuration {
|
||||
stats.TotalDuration += result.Duration
|
||||
stats.AverageDuration = stats.TotalDuration / time.Duration(stats.TotalExecutions)
|
||||
}
|
||||
}
|
||||
|
||||
// GetHooks 获取事件的所有钩子
|
||||
func (hs *HookSystem) GetHooks(event string) []*Hook {
|
||||
hs.mutex.RLock()
|
||||
defer hs.mutex.RUnlock()
|
||||
|
||||
hooks := make([]*Hook, len(hs.hooks[event]))
|
||||
copy(hooks, hs.hooks[event])
|
||||
return hooks
|
||||
}
|
||||
|
||||
// GetEvents 获取所有注册的事件
|
||||
func (hs *HookSystem) GetEvents() []string {
|
||||
hs.mutex.RLock()
|
||||
defer hs.mutex.RUnlock()
|
||||
|
||||
events := make([]string, 0, len(hs.hooks))
|
||||
for event := range hs.hooks {
|
||||
events = append(events, event)
|
||||
}
|
||||
|
||||
sort.Strings(events)
|
||||
return events
|
||||
}
|
||||
|
||||
// GetStats 获取钩子统计信息
|
||||
func (hs *HookSystem) GetStats() map[string]*HookStats {
|
||||
hs.mutex.RLock()
|
||||
defer hs.mutex.RUnlock()
|
||||
|
||||
stats := make(map[string]*HookStats)
|
||||
for key, stat := range hs.stats {
|
||||
statCopy := *stat
|
||||
stats[key] = &statCopy
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
// GetEventStats 获取特定事件的统计信息
|
||||
func (hs *HookSystem) GetEventStats(event string) map[string]*HookStats {
|
||||
allStats := hs.GetStats()
|
||||
eventStats := make(map[string]*HookStats)
|
||||
|
||||
prefix := event + "."
|
||||
for key, stat := range allStats {
|
||||
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
|
||||
hookName := key[len(prefix):]
|
||||
eventStats[hookName] = stat
|
||||
}
|
||||
}
|
||||
|
||||
return eventStats
|
||||
}
|
||||
|
||||
// Clear 清除所有钩子
|
||||
func (hs *HookSystem) Clear() {
|
||||
hs.mutex.Lock()
|
||||
defer hs.mutex.Unlock()
|
||||
|
||||
hs.hooks = make(map[string][]*Hook)
|
||||
hs.stats = make(map[string]*HookStats)
|
||||
|
||||
hs.logger.Info("Cleared all hooks")
|
||||
}
|
||||
|
||||
// ClearEvent 清除特定事件的所有钩子
|
||||
func (hs *HookSystem) ClearEvent(event string) {
|
||||
hs.mutex.Lock()
|
||||
defer hs.mutex.Unlock()
|
||||
|
||||
// 删除钩子
|
||||
delete(hs.hooks, event)
|
||||
|
||||
// 删除统计
|
||||
prefix := event + "."
|
||||
for key := range hs.stats {
|
||||
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
|
||||
delete(hs.stats, key)
|
||||
}
|
||||
}
|
||||
|
||||
hs.logger.Info("Cleared hooks for event", zap.String("event", event))
|
||||
}
|
||||
|
||||
// Count 获取钩子总数
|
||||
func (hs *HookSystem) Count() int {
|
||||
hs.mutex.RLock()
|
||||
defer hs.mutex.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, hooks := range hs.hooks {
|
||||
total += len(hooks)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
// EventCount 获取特定事件的钩子数量
|
||||
func (hs *HookSystem) EventCount(event string) int {
|
||||
hs.mutex.RLock()
|
||||
defer hs.mutex.RUnlock()
|
||||
|
||||
return len(hs.hooks[event])
|
||||
}
|
||||
|
||||
// 实现Service接口
|
||||
|
||||
// Name 返回服务名称
|
||||
func (hs *HookSystem) Name() string {
|
||||
return "hook-system"
|
||||
}
|
||||
|
||||
// Initialize 初始化钩子系统
|
||||
func (hs *HookSystem) Initialize(ctx context.Context) error {
|
||||
hs.logger.Info("Hook system initialized")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start 启动钩子系统
|
||||
func (hs *HookSystem) Start(ctx context.Context) error {
|
||||
hs.logger.Info("Hook system started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// HealthCheck 健康检查
|
||||
func (hs *HookSystem) HealthCheck(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown 关闭钩子系统
|
||||
func (hs *HookSystem) Shutdown(ctx context.Context) error {
|
||||
hs.logger.Info("Hook system shutdown")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 便捷方法
|
||||
|
||||
// OnUserCreated 用户创建事件钩子
|
||||
func (hs *HookSystem) OnUserCreated(name string, priority HookPriority, fn HookFunc) error {
|
||||
return hs.RegisterFunc("user.created", name, priority, fn)
|
||||
}
|
||||
|
||||
// OnUserUpdated 用户更新事件钩子
|
||||
func (hs *HookSystem) OnUserUpdated(name string, priority HookPriority, fn HookFunc) error {
|
||||
return hs.RegisterFunc("user.updated", name, priority, fn)
|
||||
}
|
||||
|
||||
// OnUserDeleted 用户删除事件钩子
|
||||
func (hs *HookSystem) OnUserDeleted(name string, priority HookPriority, fn HookFunc) error {
|
||||
return hs.RegisterFunc("user.deleted", name, priority, fn)
|
||||
}
|
||||
|
||||
// OnOrderCreated 订单创建事件钩子
|
||||
func (hs *HookSystem) OnOrderCreated(name string, priority HookPriority, fn HookFunc) error {
|
||||
return hs.RegisterFunc("order.created", name, priority, fn)
|
||||
}
|
||||
|
||||
// OnOrderCompleted 订单完成事件钩子
|
||||
func (hs *HookSystem) OnOrderCompleted(name string, priority HookPriority, fn HookFunc) error {
|
||||
return hs.RegisterFunc("order.completed", name, priority, fn)
|
||||
}
|
||||
|
||||
// TriggerUserCreated 触发用户创建事件
|
||||
func (hs *HookSystem) TriggerUserCreated(ctx context.Context, user interface{}) ([]HookResult, error) {
|
||||
return hs.Trigger(ctx, "user.created", user)
|
||||
}
|
||||
|
||||
// TriggerUserUpdated 触发用户更新事件
|
||||
func (hs *HookSystem) TriggerUserUpdated(ctx context.Context, user interface{}) ([]HookResult, error) {
|
||||
return hs.Trigger(ctx, "user.updated", user)
|
||||
}
|
||||
|
||||
// TriggerUserDeleted 触发用户删除事件
|
||||
func (hs *HookSystem) TriggerUserDeleted(ctx context.Context, user interface{}) ([]HookResult, error) {
|
||||
return hs.Trigger(ctx, "user.deleted", user)
|
||||
}
|
||||
|
||||
// HookBuilder 钩子构建器
|
||||
type HookBuilder struct {
|
||||
hook *Hook
|
||||
}
|
||||
|
||||
// NewHookBuilder 创建钩子构建器
|
||||
func NewHookBuilder(name string, fn HookFunc) *HookBuilder {
|
||||
return &HookBuilder{
|
||||
hook: &Hook{
|
||||
Name: name,
|
||||
Func: fn,
|
||||
Priority: PriorityNormal,
|
||||
Async: false,
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithPriority 设置优先级
|
||||
func (hb *HookBuilder) WithPriority(priority HookPriority) *HookBuilder {
|
||||
hb.hook.Priority = priority
|
||||
return hb
|
||||
}
|
||||
|
||||
// WithTimeout 设置超时时间
|
||||
func (hb *HookBuilder) WithTimeout(timeout time.Duration) *HookBuilder {
|
||||
hb.hook.Timeout = timeout
|
||||
return hb
|
||||
}
|
||||
|
||||
// Async 设置为异步执行
|
||||
func (hb *HookBuilder) Async() *HookBuilder {
|
||||
hb.hook.Async = true
|
||||
return hb
|
||||
}
|
||||
|
||||
// Build 构建钩子
|
||||
func (hb *HookBuilder) Build() *Hook {
|
||||
return hb.hook
|
||||
}
|
||||
|
||||
// TypedHookFunc 类型化钩子函数
|
||||
type TypedHookFunc[T any] func(ctx context.Context, data T) error
|
||||
|
||||
// RegisterTypedFunc 注册类型化钩子函数
|
||||
func RegisterTypedFunc[T any](hs *HookSystem, event, name string, priority HookPriority, fn TypedHookFunc[T]) error {
|
||||
hookFunc := func(ctx context.Context, data interface{}) error {
|
||||
typedData, ok := data.(T)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid data type for hook %s, expected %s", name, reflect.TypeOf((*T)(nil)).Elem().Name())
|
||||
}
|
||||
return fn(ctx, typedData)
|
||||
}
|
||||
|
||||
return hs.RegisterFunc(event, name, priority, hookFunc)
|
||||
}
|
||||
Reference in New Issue
Block a user