refactor(msg_gateway): decouple bot gateway and push notification architecture

- Split shared monolithic consts into bot, push, and errs with typed sentinel errors
- Restructure model layer into distinct bot and push subdomains
- Refactor DAO layer to enforce single-owner principle and remove cross-table raw SQL queries
- Decompose 1150+ line service/push.go into push_channel, push_event, push_trigger, push_worker, and push_template
- Clean up controller layer with generic request handlers and parameter validation in controller/base.go
- Streamline plugin.go to core Cordis lifecycle orchestration and remove re-export bloat
- Verify all unit tests, race tests, Cordis architecture rules, and Swagger generation pass cleanly
This commit is contained in:
ryan
2026-09-02 23:21:36 +08:00
parent 8395dd5019
commit 1e19d8114a
35 changed files with 2103 additions and 2333 deletions
@@ -0,0 +1,27 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package consts defines constants, error types, and identifiers for msg_gateway.
package consts
// Bot Channel type and scope constants.
const (
ChannelTypeTelegram = "telegram"
ChannelTypeQQ = "qq"
MessageChannelTypeTelegram = "telegram"
MessageChannelTypeQQ = "qq"
MessageOwnerScopeSystem = "system"
)
// Bot Task and Schedule identifier constants.
const (
TaskCleanupPairingCodes = "msg_gateway:cleanup_pairing_codes"
TaskDispatchBotMsg = "msg_gateway:dispatch_bot_msg"
TaskTypeDispatchBotMsg = "dispatch_bot_msg"
)
// Pairing code constants.
const (
CodeAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
CodeLength = 8
)
@@ -1,69 +1,10 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package consts defines constants, sentinel errors, and user-facing error messages
// for the msg_gateway plugin.
package consts
import "errors"
// Channel type and scope constants.
const (
ChannelTypeTelegram = "telegram"
ChannelTypeQQ = "qq"
MessageChannelTypeTelegram = "telegram"
MessageChannelTypeQQ = "qq"
MessageOwnerScopeSystem = "system"
TypeCustom = "custom"
TypeEmail = "email"
TypeTelegram = "telegram"
ChannelCustom = "custom"
ChannelEmail = "email"
ChannelLark = "lark"
ChannelDingTalk = "dingtalk"
ChannelTelegram = "telegram"
ChannelBark = "bark"
ChannelDiscord = "discord"
ChannelSlack = "slack"
ChannelPushover = "pushover"
DefaultLevelInfo = "INFO"
KeyTitle = "title"
KeyContent = "content"
KeyLevel = "level"
// KeyURL represents the URL field key.
KeyURL = "url"
// KeyToken represents the Token field key.
KeyToken = "token"
// KeyOther represents the Other field key.
KeyOther = "other"
// TypeText represents standard text input type.
TypeText = "text"
// TypePassword represents password input type.
TypePassword = "password"
// TypeTextarea represents textarea input type.
TypeTextarea = "textarea"
)
// Task and Schedule identifier constants.
const (
TaskPushNotification = "msg_gateway:push_notification"
TaskCleanupPairingCodes = "msg_gateway:cleanup_pairing_codes"
TaskDispatchBotMsg = "msg_gateway:dispatch_bot_msg"
TaskTypeDispatchBotMsg = "dispatch_bot_msg"
SendNotificationTask = "push:send"
TaskTypeSendNotification = "send_notification"
)
// Pairing code constants.
const (
CodeAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
CodeLength = 8
)
// Sentinel errors.
var (
ErrCodeInvalid = errors.New("invalid or expired pairing code")
@@ -73,14 +14,15 @@ var (
ErrBindingForbidden = errors.New("cannot unbind another user's binding")
ErrChannelIDRequired = errors.New("channel_id is required")
ErrChannelDisabled = errors.New("channel is not enabled")
ErrChannelNotFound = errors.New("channel not found")
ErrEventNotFound = errors.New("notification event not found")
ErrUserNotFound = errors.New("user not found")
ErrNoAdminUser = errors.New("no admin user found")
ErrTaskServiceNotAvail = errors.New("task service not available")
// ErrRecordNotFound maps GORM's missing-row sentinel at the DAO boundary so
// upper layers never import gorm. Its text matches gorm.ErrRecordNotFound verbatim.
ErrRecordNotFound = errors.New("record not found")
// ErrUnsupportedUserLookupField rejects a column name that the DAO is not
// allowed to interpolate into a WHERE clause.
ErrUnsupportedUserLookupField = errors.New("unsupported user lookup field")
)
// User-facing validation and error message constants.
@@ -89,7 +31,7 @@ const (
ErrTypeInvalid = "type must be telegram or qq"
ErrTelegramTokenRequired = "telegram bot secret is required" //nolint:gosec // user-facing validation text
ErrQQCredentialsRequired = "qq app id and secret are required" //nolint:gosec // user-facing validation text
ErrChannelNotFound = "channel not found"
ErrChannelNotFoundText = "channel not found"
ErrChannelProbeFailed = "channel probe failed"
ErrBotDispatchTextRequired = "message text is required"
ErrBotChannelNotRegistered = "channel adapter is not registered"
@@ -99,7 +41,6 @@ const (
ErrInvalidBindingID = "invalid binding id"
ErrInvalidChannelID = "invalid channel id"
ErrInvalidEventID = "invalid event id"
ErrEventNotFound = "notification event not found"
ErrValidationFailed = "validation failed"
ErrMissingTelegramToken = "missing telegram bot token"
@@ -120,8 +61,6 @@ const (
ErrEventKeyOrTaskType = "either event_key or task_type must be provided"
ErrUnsupportedEventKey = "unsupported built-in event key"
ErrTaskServiceUnavailable = "task service not available"
ErrUserNotFound = "user not found"
ErrNoAdminUser = "no admin user found"
ErrPayloadRequired = "payload is required"
ErrInvalidJSONFormat = "invalid json format"
@@ -0,0 +1,43 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package consts
// Push notification channel type constants.
const (
TypeCustom = "custom"
TypeEmail = "email"
TypeTelegram = "telegram"
ChannelCustom = "custom"
ChannelEmail = "email"
ChannelLark = "lark"
ChannelDingTalk = "dingtalk"
ChannelTelegram = "telegram"
ChannelBark = "bark"
ChannelDiscord = "discord"
ChannelSlack = "slack"
ChannelPushover = "pushover"
)
// Push message template and payload keys.
const (
DefaultLevelInfo = "INFO"
KeyTitle = "title"
KeyContent = "content"
KeyLevel = "level"
KeyURL = "url"
KeyToken = "token"
KeyOther = "other"
TypeText = "text"
TypePassword = "password"
TypeTextarea = "textarea"
)
// Push task identifier constants.
const (
TaskPushNotification = "msg_gateway:push_notification"
SendNotificationTask = "push:send"
TaskTypeSendNotification = "send_notification"
)