feat(core): bind request services and implement registered tasks

Wire plugin services through Bind/InjectFrom and AppContext so HTTP and
workers resolve dependencies after Apply. Register TaskHandler objects
with persisted results, and implement send_email_code, mail:send,
cleanup_inactive_users, and dispatch_bot_msg.
This commit is contained in:
ryan
2026-09-02 16:59:00 +08:00
parent 30bbe965bf
commit 4f50f6a8f9
49 changed files with 1406 additions and 499 deletions
+24 -4
View File
@@ -8,8 +8,10 @@ import (
"Wavelet/core/contracts"
"Wavelet/pkg/util"
"context"
"errors"
"strings"
"sync"
"time"
"gorm.io/gorm"
)
@@ -27,10 +29,8 @@ func SetDBService(s contracts.DBService) {
}
func getDB(ctx context.Context) *gorm.DB {
if c, ok := ctx.(*core.Context); ok && c != nil {
if s, err := core.Inject[contracts.DBService](c); err == nil && s != nil {
return s.DB(ctx)
}
if s, err := core.InjectFrom[contracts.DBService](ctx); err == nil && s != nil {
return s.DB(ctx)
}
dbMu.RLock()
@@ -180,6 +180,26 @@ func DeleteUserWithRelations(ctx context.Context, id uint64) error {
})
}
// ListInactiveNeverLoggedInUserIDs returns non-admin users created before cutoff
// who have never logged in. Seeded admin/system accounts are excluded.
func ListInactiveNeverLoggedInUserIDs(ctx context.Context, cutoff time.Time) ([]uint64, error) {
db := getDB(ctx)
if db == nil {
return nil, errors.New("database not available")
}
var ids []uint64
unixEpoch := time.Unix(0, 0).UTC()
err := db.Model(&User{}).
Where("is_admin = ? AND username NOT IN ?", false, []string{"admin", "system"}).
Where("created_at < ?", cutoff).
Where("last_login_at IS NULL OR last_login_at < ?", unixEpoch).
Pluck("id", &ids).Error
if err != nil {
return nil, err
}
return ids, nil
}
// GetFirstAdminUser 获取第一个管理员用户
func GetFirstAdminUser(ctx context.Context) (*User, error) {
var u User