add cache supported

This commit is contained in:
Yavolte 2025-06-18 17:49:15 +08:00
parent cb46385644
commit 1d312c9741
18 changed files with 198 additions and 52 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -69,12 +70,12 @@ func (f *Formatter) Register() {
rest.DefaultFormatter.Register("menu", f.FormatMenu)
}
func NewFormatter(db *gorm.DB) *Formatter {
func NewFormatter(db *gorm.DB, ch cache.Cache) *Formatter {
return &Formatter{
db: db,
user: logic.NewUserLogic(db),
department: logic.NewDepartmentLogic(db),
role: logic.NewRoleLogic(db),
menu: logic.NewMenuLogic(db),
user: logic.NewUserLogic(db, ch),
department: logic.NewDepartmentLogic(db, ch),
role: logic.NewRoleLogic(db, ch),
menu: logic.NewMenuLogic(db, ch),
}
}

11
go.mod
View File

@ -5,8 +5,8 @@ go 1.23.0
toolchain go1.23.10
require (
git.nobla.cn/golang/aeus v0.0.7
git.nobla.cn/golang/rest v0.1.1
git.nobla.cn/golang/aeus v0.0.8
git.nobla.cn/golang/rest v0.1.2
github.com/envoyproxy/protoc-gen-validate v1.2.1
golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.6
@ -17,15 +17,11 @@ require (
require (
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/mssola/useragent v1.0.0
golang.org/x/sync v0.12.0
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb
google.golang.org/grpc v1.72.2
)
replace (
git.nobla.cn/golang/aeus v0.0.7 => /Users/yavolte/Workspace/golang/aeus
git.nobla.cn/golang/rest v0.1.1 => /Users/yavolte/Workspace/golang/rest
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
git.nobla.cn/golang/kos v0.1.32 // indirect
@ -56,7 +52,6 @@ require (
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

4
go.sum
View File

@ -1,7 +1,11 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.nobla.cn/golang/aeus v0.0.8 h1:mifdMOredbOfUNnP193+IjR1gR68O+2R3DVEhi3N4GE=
git.nobla.cn/golang/aeus v0.0.8/go.mod h1:oOEwqIp6AhKKqj6sLFO8x7IycOROYHCb/2/CjF4+9CU=
git.nobla.cn/golang/kos v0.1.32 h1:sFVCA7vKc8dPUd0cxzwExOSPX2mmMh2IuwL6cYS1pBc=
git.nobla.cn/golang/kos v0.1.32/go.mod h1:35Z070+5oB39WcVrh5DDlnVeftL/Ccmscw2MZFe9fUg=
git.nobla.cn/golang/rest v0.1.2 h1:vF5perbveRcCF5Pb60yxHjvGpx/zX+D1qzdceqIwg+E=
git.nobla.cn/golang/rest v0.1.2/go.mod h1:4viDk7VujDokpUeHQGbnSp2bkkVZEoIkWQIs/l/TTPQ=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=

View File

@ -7,6 +7,7 @@ import (
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -14,6 +15,7 @@ import (
type Department struct {
db *gorm.DB
cache cache.Cache
sqlDependency *dbcache.SqlDependency
}
@ -56,6 +58,7 @@ func (u *Department) GetLevelLabels(ctx context.Context) (values []*types.TypeVa
return u.RecursiveDepartment(ctx, 0, 0, values), nil
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
@ -67,6 +70,7 @@ func (u *Department) GetLabels(ctx context.Context) (values []*types.TypeValue[i
return rest.ModelTypes[int64](ctx, tx, &models.Department{}, "", "name", "id")
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
@ -80,14 +84,16 @@ func (u *Department) GetDepartments(ctx context.Context) (values []*models.Depar
return items, err
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
}
func NewDepartmentLogic(db *gorm.DB) *Department {
func NewDepartmentLogic(db *gorm.DB, ch cache.Cache) *Department {
return &Department{
db: db,
cache: ch,
sqlDependency: dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM departments"),
}
}

View File

@ -6,6 +6,7 @@ import (
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -13,6 +14,7 @@ import (
type Menu struct {
db *gorm.DB
cache cache.Cache
sqlDependency *dbcache.SqlDependency
}
@ -23,6 +25,7 @@ func (u *Menu) GetMenus(ctx context.Context) (values []*models.Menu, err error)
return items, err
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
}
@ -32,13 +35,15 @@ func (u *Menu) GetLabels(ctx context.Context) (values []*types.TypeValue[string]
return rest.ModelTypes[string](ctx, tx, &models.Menu{}, "", "label", "name")
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
}
func NewMenuLogic(db *gorm.DB) *Menu {
func NewMenuLogic(db *gorm.DB, ch cache.Cache) *Menu {
return &Menu{
db: db,
cache: ch,
sqlDependency: dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM menus"),
}
}

View File

@ -6,6 +6,7 @@ import (
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -13,6 +14,7 @@ import (
type Role struct {
db *gorm.DB
cache cache.Cache
sqlDependency *dbcache.SqlDependency
permissionSqlDependency *dbcache.SqlDependency
}
@ -22,13 +24,20 @@ func (u *Role) GetLabels(ctx context.Context) (values []*types.TypeValue[string]
return rest.ModelTypes[string](ctx, tx, &models.Role{}, "", "label", "name")
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
}
func (u *Role) GetPermissions(ctx context.Context, role string) (values []*models.Permission, err error) {
values, err = dbcache.TryCache(ctx, fmt.Sprintf("role:permissions-items:%s", role), func(tx *gorm.DB) ([]*models.Permission, error) {
cacheKey := "role:permissions:"
if role == "" {
cacheKey += "all"
} else {
cacheKey += role
}
values, err = dbcache.TryCache(ctx, cacheKey, func(tx *gorm.DB) ([]*models.Permission, error) {
var items []*models.Permission
if role == "" {
err = tx.Find(&items).Error
@ -40,14 +49,16 @@ func (u *Role) GetPermissions(ctx context.Context, role string) (values []*model
return items, err
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.permissionSqlDependency),
)
return
}
func NewRoleLogic(db *gorm.DB) *Role {
func NewRoleLogic(db *gorm.DB, ch cache.Cache) *Role {
return &Role{
db: db,
cache: ch,
sqlDependency: dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM roles"),
permissionSqlDependency: dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM permissions"),
}

View File

@ -7,6 +7,7 @@ import (
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -14,6 +15,7 @@ import (
type User struct {
db *gorm.DB
cache cache.Cache
sqlDependency *dbcache.SqlDependency
}
@ -27,7 +29,11 @@ func (u *User) GetPermissions(ctx context.Context, uid string) (permissions []st
Pluck("permission", &ss).
Error
return ss, err
}, dbcache.WithCacheDuration(time.Minute), dbcache.WithDB(u.db))
},
dbcache.WithCacheDuration(time.Minute),
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
)
return
}
@ -37,6 +43,7 @@ func (u *User) GetLabels(ctx context.Context) (values []*types.TypeValue[string]
return rest.ModelTypes[string](ctx, tx, &models.User{}, "", "username", "uid")
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
@ -50,14 +57,16 @@ func (u *User) GeUsers(ctx context.Context) (values []*models.User, err error) {
return items, err
},
dbcache.WithDB(u.db),
dbcache.WithCache(u.cache),
dbcache.WithDependency(u.sqlDependency),
)
return
}
func NewUserLogic(db *gorm.DB) *User {
func NewUserLogic(db *gorm.DB, ch cache.Cache) *User {
return &User{
db: db,
cache: ch,
sqlDependency: dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM users"),
}
}

View File

@ -6,6 +6,7 @@ import (
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus/middleware/auth"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/pkg/errors"
"gorm.io/gorm"
)
@ -36,9 +37,9 @@ func (p *PermissionChecker) CheckPermission(ctx context.Context, permission stri
return
}
func NewPermissionChecker(db *gorm.DB) *PermissionChecker {
func NewPermissionChecker(db *gorm.DB, ch cache.Cache) *PermissionChecker {
return &PermissionChecker{
db: db,
logic: logic.NewUserLogic(db),
logic: logic.NewUserLogic(db, ch),
}
}

View File

@ -57,7 +57,6 @@ func WithDependency(d CacheDependency) CacheOption {
func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, error), cbs ...CacheOption) (result T, err error) {
var (
none T
value any
val any
hasDependValue bool
dependValue string
@ -73,21 +72,20 @@ func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, er
return none, errors.Format(errors.Unavailable, "db instance unavailable")
}
//从缓存加载数据
if value, err = opts.cache.Get(ctx, key); err == nil {
if entry, ok := value.(*cacheEntry[T]); ok {
if time.Now().Unix()-entry.CreatedAt <= 1 {
entry := &cacheEntry[T]{}
if err = opts.cache.Load(ctx, key, entry); err == nil {
if time.Now().Unix()-entry.CreatedAt <= 1 {
return entry.Value, nil
}
if opts.dependency == nil {
return entry.Value, nil
}
if dependValue, err = opts.dependency.GetValue(ctx, opts.db); err == nil {
hasDependValue = true
if entry.CompareValue == dependValue {
return entry.Value, nil
}
if opts.dependency == nil {
return entry.Value, nil
}
if dependValue, err = opts.dependency.GetValue(ctx, opts.db); err == nil {
hasDependValue = true
if entry.CompareValue == dependValue {
return entry.Value, nil
} else {
opts.cache.Delete(ctx, key)
}
} else {
opts.cache.Delete(ctx, key)
}
}
}
@ -98,7 +96,7 @@ func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, er
if !hasDependValue && opts.dependency != nil {
dependValue, err = opts.dependency.GetValue(ctx, tx)
}
opts.cache.Put(ctx, key, &cacheEntry[T]{
opts.cache.Store(ctx, key, &cacheEntry[T]{
CompareValue: dependValue,
Value: result,
CreatedAt: time.Now().Unix(),

View File

@ -0,0 +1,42 @@
package tokenize
import (
"context"
"time"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/pkg/errors"
)
// Tokenize 实现简单的session管理, 结合AuthService和JWT middleware可以实现登录和登出等操作
type Tokenize struct {
cache cache.Cache
}
func (t *Tokenize) buildKey(token string) string {
return "user:session:" + token
}
func (t *Tokenize) Put(ctx context.Context, token string, ttl int64) error {
return t.cache.Store(ctx, t.buildKey(token), token, time.Second*time.Duration(ttl))
}
func (t *Tokenize) Del(ctx context.Context, token string) error {
return t.cache.Delete(ctx, t.buildKey(token))
}
func (t *Tokenize) Validate(ctx context.Context, token string) (err error) {
var ok bool
if ok, err = t.cache.Exists(ctx, t.buildKey(token)); err == nil {
if !ok {
return errors.ErrAccessDenied
}
}
return
}
func New(c cache.Cache) *Tokenize {
return &Tokenize{
cache: c,
}
}

View File

@ -380,6 +380,6 @@ func Init(ctx context.Context, cbs ...Option) (err error) {
}
//注册渲染器
NewFormatter(opts.db).Register()
NewFormatter(opts.db, opts.cache).Register()
return
}

View File

@ -11,6 +11,7 @@ import (
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus-admin/types"
"git.nobla.cn/golang/aeus/metadata"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/pkg/errors"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/mssola/useragent"
@ -23,6 +24,8 @@ type (
secret []byte
method string
ttl int64
cache cache.Cache
tokenStore TokenStore
turnstileValidateUrl string
turnstileSiteKey string
}
@ -31,6 +34,7 @@ type (
Secret string `json:"secret"`
Response string `json:"response"`
}
turnstileResponse struct {
Success bool `json:"success"`
Hostname string `json:"hostname"`
@ -40,6 +44,11 @@ type (
AuthOption func(o *authOptions)
TokenStore interface {
Put(ctx context.Context, token string, ttl int64) error
Del(ctx context.Context, token string) error
}
AuthService struct {
opts *authOptions
}
@ -51,6 +60,18 @@ func WithAuthDB(db *gorm.DB) AuthOption {
}
}
func WithAuthCache(cache cache.Cache) AuthOption {
return func(o *authOptions) {
o.cache = cache
}
}
func WithTokenStore(store TokenStore) AuthOption {
return func(o *authOptions) {
o.tokenStore = store
}
}
func WithAuthSecret(secret []byte) AuthOption {
return func(o *authOptions) {
o.secret = secret
@ -156,6 +177,9 @@ func (s *AuthService) Login(ctx context.Context, req *pb.LoginRequest) (res *pb.
loginModel.Uid = model.Uid
loginModel.AccessToken = res.Token
md := metadata.FromContext(ctx)
if s.opts.tokenStore != nil {
s.opts.tokenStore.Put(ctx, res.Token, s.opts.ttl)
}
if userAgent, ok := md.Get("User-Agent"); ok {
ua := useragent.New(userAgent)
loginModel.Os = ua.OS()
@ -169,7 +193,9 @@ func (s *AuthService) Login(ctx context.Context, req *pb.LoginRequest) (res *pb.
}
func (s *AuthService) Logout(ctx context.Context, req *pb.LogoutRequest) (res *pb.LogoutResponse, err error) {
if s.opts.tokenStore != nil {
err = s.opts.tokenStore.Del(ctx, req.Token)
}
return
}

View File

@ -8,13 +8,15 @@ import (
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
)
type (
departmentOptions struct {
db *gorm.DB
db *gorm.DB
cache cache.Cache
}
DepartmentOption func(o *departmentOptions)
@ -25,6 +27,12 @@ type (
}
)
func WithDepartmentCache(cache cache.Cache) DepartmentOption {
return func(o *departmentOptions) {
o.cache = cache
}
}
func WithDepartmentDB(db *gorm.DB) DepartmentOption {
return func(o *departmentOptions) {
o.db = db
@ -124,7 +132,7 @@ func NewDepartmentService(cbs ...DepartmentOption) *DepartmentService {
}
return &DepartmentService{
opts: opts,
user: logic.NewUserLogic(opts.db),
logic: logic.NewDepartmentLogic(opts.db),
user: logic.NewUserLogic(opts.db, opts.cache),
logic: logic.NewDepartmentLogic(opts.db, opts.cache),
}
}

View File

@ -6,12 +6,14 @@ import (
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus/pkg/cache"
"gorm.io/gorm"
)
type (
menuOptions struct {
db *gorm.DB
db *gorm.DB
cache cache.Cache
}
MenuOption func(o *menuOptions)
@ -21,6 +23,12 @@ type (
}
)
func WithMenuCache(cache cache.Cache) MenuOption {
return func(o *menuOptions) {
o.cache = cache
}
}
func WithMenuDB(db *gorm.DB) MenuOption {
return func(o *menuOptions) {
o.db = db
@ -63,6 +71,6 @@ func NewMenuService(cbs ...MenuOption) *MenuService {
}
return &MenuService{
opts: opts,
logic: logic.NewMenuLogic(opts.db),
logic: logic.NewMenuLogic(opts.db, opts.cache),
}
}

View File

@ -6,13 +6,15 @@ import (
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
)
type (
roleOptions struct {
db *gorm.DB
db *gorm.DB
cache cache.Cache
}
RoleOption func(o *roleOptions)
@ -22,6 +24,12 @@ type (
}
)
func WithRoleCache(cache cache.Cache) RoleOption {
return func(o *roleOptions) {
o.cache = cache
}
}
func WithRoleDB(db *gorm.DB) RoleOption {
return func(o *roleOptions) {
o.db = db
@ -83,6 +91,6 @@ func NewRoleService(cbs ...RoleOption) *RoleService {
}
return &RoleService{
opts: opts,
logic: logic.NewRoleLogic(opts.db),
logic: logic.NewRoleLogic(opts.db, opts.cache),
}
}

View File

@ -5,12 +5,14 @@ import (
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus/pkg/cache"
"gorm.io/gorm"
)
type (
settingOptions struct {
db *gorm.DB
db *gorm.DB
cache cache.Cache
}
SettingOption func(o *settingOptions)
@ -19,6 +21,12 @@ type SettingService struct {
opts *settingOptions
}
func WithSettingCache(cache cache.Cache) SettingOption {
return func(o *settingOptions) {
o.cache = cache
}
}
func WithSettingDB(db *gorm.DB) SettingOption {
return func(o *settingOptions) {
o.db = db

View File

@ -11,6 +11,7 @@ import (
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/middleware/auth"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/pkg/errors"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
@ -18,7 +19,8 @@ import (
type (
userOptions struct {
db *gorm.DB
db *gorm.DB
cache cache.Cache
}
UserOption func(o *userOptions)
@ -38,6 +40,12 @@ func WithUserDB(db *gorm.DB) UserOption {
}
}
func WithUserCache(cache cache.Cache) UserOption {
return func(o *userOptions) {
o.cache = cache
}
}
func (s *UserService) getUidFromContext(ctx context.Context) (string, error) {
if claims, ok := auth.FromContext(ctx); !ok {
return "", errors.ErrAccessDenied
@ -299,9 +307,9 @@ func NewUserService(cbs ...UserOption) *UserService {
}
return &UserService{
opts: opts,
user: logic.NewUserLogic(opts.db),
role: logic.NewRoleLogic(opts.db),
menu: logic.NewMenuLogic(opts.db),
department: logic.NewDepartmentLogic(opts.db),
user: logic.NewUserLogic(opts.db, opts.cache),
role: logic.NewRoleLogic(opts.db, opts.cache),
menu: logic.NewMenuLogic(opts.db, opts.cache),
department: logic.NewDepartmentLogic(opts.db, opts.cache),
}
}

View File

@ -1,6 +1,7 @@
package aeusadmin
import (
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/transport/http"
"git.nobla.cn/golang/rest"
"git.nobla.cn/golang/rest/types"
@ -21,6 +22,7 @@ var (
type (
options struct {
db *gorm.DB
cache cache.Cache
domain string
moduleName string
apiPrefix string //接口前缀
@ -63,6 +65,12 @@ func WithDB(db *gorm.DB) Option {
}
}
func WithCache(cache cache.Cache) Option {
return func(o *options) {
o.cache = cache
}
}
func WithoutDefault() Option {
return func(o *options) {
o.disableDefault = true