139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/glog"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 内置事件常量
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const (
|
|
// Relay 相关(AI 代理核心流程)
|
|
HookRelayBeforeRequest = "relay.before_request" // 请求转发前(可修改请求、中断转发)
|
|
HookRelayAfterResponse = "relay.after_response" // 响应返回后(可修改响应、记录日志)
|
|
HookRelayOnError = "relay.on_error" // 转发失败(告警、重试决策)
|
|
|
|
// 计费相关
|
|
HookBillingBeforeDeduct = "billing.before_deduct" // 预扣前(可修改价格、跳过计费)
|
|
HookBillingAfterSettle = "billing.after_settle" // 结算后(用量分析、额度告警)
|
|
HookBillingOnRefund = "billing.on_refund" // 退款时(退款通知)
|
|
|
|
// 渠道相关
|
|
HookChannelBeforeSelect = "channel.before_select" // 渠道选择前(影响调度决策)
|
|
HookChannelOnHealthCheck = "channel.on_health_check" // 健康检查(自定义检查逻辑)
|
|
|
|
// 租户相关
|
|
HookTenantOnCreate = "tenant.on_create" // 租户创建后(初始化租户数据)
|
|
HookTenantOnSuspend = "tenant.on_suspend" // 租户暂停(清理资源)
|
|
|
|
// API Key 相关
|
|
HookApiKeyOnCreate = "apikey.on_create" // Key 创建后(通知、初始化)
|
|
HookApiKeyOnVerify = "apikey.on_verify" // Key 验证时(自定义验证逻辑)
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HookEmitter 事件发射器
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// HookEmitter 全局事件发射器。
|
|
type HookEmitter struct {
|
|
handlers map[string][]hookEntry
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type hookEntry struct {
|
|
PluginName string
|
|
Priority int
|
|
Handler HookHandler
|
|
}
|
|
|
|
var globalEmitter = &HookEmitter{
|
|
handlers: make(map[string][]hookEntry),
|
|
}
|
|
|
|
// GlobalEmitter 获取全局事件发射器。
|
|
func GlobalEmitter() *HookEmitter {
|
|
return globalEmitter
|
|
}
|
|
|
|
// RegisterHook 注册事件处理器(由插件系统在启动时调用)。
|
|
func (e *HookEmitter) RegisterHook(pluginName, event string, priority int, handler HookHandler) {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
e.handlers[event] = append(e.handlers[event], hookEntry{
|
|
PluginName: pluginName,
|
|
Priority: priority,
|
|
Handler: handler,
|
|
})
|
|
}
|
|
|
|
// Emit 同步发射事件(按优先级顺序执行,可被中断)。
|
|
func (e *HookEmitter) Emit(ctx context.Context, event string, data g.Map) (HookResult, error) {
|
|
e.mu.RLock()
|
|
handlers := make([]hookEntry, len(e.handlers[event]))
|
|
copy(handlers, e.handlers[event])
|
|
e.mu.RUnlock()
|
|
|
|
// 零开销快速路径:无订阅者直接返回
|
|
if len(handlers) == 0 {
|
|
return HookResult{Data: data}, nil
|
|
}
|
|
|
|
// 按优先级排序(数字越小越先执行)
|
|
sort.Slice(handlers, func(i, j int) bool {
|
|
return handlers[i].Priority < handlers[j].Priority
|
|
})
|
|
|
|
result := HookResult{Data: data}
|
|
for _, entry := range handlers {
|
|
if result.Aborted {
|
|
break
|
|
}
|
|
payload := HookPayload{Event: event, Data: result.Data}
|
|
hr, err := entry.Handler(ctx, payload)
|
|
if err != nil {
|
|
glog.Warningf(ctx, "plugin hook error: plugin=%s event=%s err=%v",
|
|
entry.PluginName, event, err)
|
|
continue
|
|
}
|
|
if hr.Aborted {
|
|
result.Aborted = true
|
|
}
|
|
if hr.Data != nil {
|
|
for k, v := range hr.Data {
|
|
result.Data[k] = v
|
|
}
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// EmitAsync 异步发射事件(不阻塞主流程,适用于日志、通知等场景)。
|
|
func (e *HookEmitter) EmitAsync(ctx context.Context, event string, data g.Map) {
|
|
go func() {
|
|
_, _ = e.Emit(ctx, event, data)
|
|
}()
|
|
}
|
|
|
|
// RemoveHooks 移除指定插件的所有 Hook 注册。
|
|
func (e *HookEmitter) RemoveHooks(pluginName string) {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
for event, entries := range e.handlers {
|
|
filtered := make([]hookEntry, 0, len(entries))
|
|
for _, entry := range entries {
|
|
if entry.PluginName != pluginName {
|
|
filtered = append(filtered, entry)
|
|
}
|
|
}
|
|
e.handlers[event] = filtered
|
|
}
|
|
}
|