1
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
incomev1 "management/internal/erpserver/biz/v1/income"
|
||||
projectv1 "management/internal/erpserver/biz/v1/project"
|
||||
systemv1 "management/internal/erpserver/biz/v1/system"
|
||||
"management/internal/erpserver/store"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
@@ -33,20 +34,22 @@ type IBiz interface {
|
||||
|
||||
// biz 是 IBiz 的一个具体实现.
|
||||
type biz struct {
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
database store.IStore
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
// 确保 biz 实现了 IBiz 接口.
|
||||
var _ IBiz = (*biz)(nil)
|
||||
|
||||
// NewBiz 创建一个 IBiz 类型的实例.
|
||||
func NewBiz(store db.Store, redis redis.IRedis, session session.ISession) *biz {
|
||||
func NewBiz(database store.IStore, store db.Store, redis redis.IRedis, session session.ISession) *biz {
|
||||
return &biz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
database: database,
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +60,7 @@ func (b *biz) CommonV1() commonv1.CommonBiz {
|
||||
|
||||
// SystemV1 返回一个实现了 SystemBiz 接口的实例.
|
||||
func (b *biz) SystemV1() systemv1.SystemBiz {
|
||||
return systemv1.New(b.store, b.redis, b.session)
|
||||
return systemv1.New(b.database, b.store, b.redis, b.session)
|
||||
}
|
||||
|
||||
func (b *biz) ProjectV1() projectv1.ProjectBiz {
|
||||
|
||||
@@ -2,6 +2,7 @@ package system
|
||||
|
||||
import (
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/erpserver/store"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
@@ -18,23 +19,25 @@ type SystemBiz interface {
|
||||
}
|
||||
|
||||
type systemBiz struct {
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
database store.IStore
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
var _ SystemBiz = (*systemBiz)(nil)
|
||||
|
||||
func New(store db.Store, redis redis.IRedis, session session.ISession) *systemBiz {
|
||||
func New(database store.IStore, store db.Store, redis redis.IRedis, session session.ISession) *systemBiz {
|
||||
return &systemBiz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
database: database,
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *systemBiz) UserBiz() UserBiz {
|
||||
return NewUser(b.store, b.session)
|
||||
return NewUser(b.database, b.store, b.session)
|
||||
}
|
||||
|
||||
func (b *systemBiz) MenuBiz() MenuBiz {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/erpserver/model/form"
|
||||
"management/internal/erpserver/model/view"
|
||||
"management/internal/erpserver/store"
|
||||
"management/internal/pkg/crypto"
|
||||
"management/internal/pkg/know"
|
||||
"management/internal/pkg/rand"
|
||||
@@ -39,17 +41,19 @@ type UserExpansion interface {
|
||||
|
||||
// userBiz 是 UserBiz 接口的实现.
|
||||
type userBiz struct {
|
||||
store db.Store
|
||||
session session.ISession
|
||||
database store.IStore
|
||||
store db.Store
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
// 确保 userBiz 实现了 UserBiz 接口.
|
||||
var _ UserBiz = (*userBiz)(nil)
|
||||
|
||||
func NewUser(store db.Store, session session.ISession) *userBiz {
|
||||
func NewUser(database store.IStore, store db.Store, session session.ISession) *userBiz {
|
||||
return &userBiz{
|
||||
store: store,
|
||||
session: session,
|
||||
database: database,
|
||||
store: store,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +194,14 @@ func (b *userBiz) Login(ctx context.Context, req *form.Login) error {
|
||||
Browser: req.Browser,
|
||||
}
|
||||
|
||||
user, err := b.store.GetSysUserByEmail(ctx, req.Email)
|
||||
user, err := b.database.User().GetByEmail(ctx, req.Email)
|
||||
// user, err = b.store.GetSysUserByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
log.Message = err.Error()
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%+v", user)
|
||||
log.UserUuid = user.Uuid
|
||||
log.Username = user.Username
|
||||
|
||||
|
||||
40
internal/erpserver/model/system/user.go
Normal file
40
internal/erpserver/model/system/user.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey"`
|
||||
Uuid uuid.UUID `json:"uuid"`
|
||||
// 邮箱地址
|
||||
Email string `json:"email"`
|
||||
// 用户名称
|
||||
Username string `json:"username"`
|
||||
// 加密密码
|
||||
HashedPassword []byte `json:"hashed_password"`
|
||||
// 密码盐值
|
||||
Salt string `json:"salt"`
|
||||
// 头像
|
||||
Avatar string `json:"avatar"`
|
||||
// 性别
|
||||
Gender int32 `json:"gender"`
|
||||
// 部门
|
||||
DepartmentID int32 `json:"department_id"`
|
||||
// 角色
|
||||
RoleID int32 `json:"role_id"`
|
||||
// 状态
|
||||
Status int32 `json:"status"`
|
||||
// 密码修改时间
|
||||
ChangePasswordAt time.Time `json:"change_password_at"`
|
||||
// 创建时间
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
// 更新时间
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return "sys_user"
|
||||
}
|
||||
69
internal/erpserver/store/store.go
Normal file
69
internal/erpserver/store/store.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"management/internal/erpserver/store/system"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
// 全局变量,方便其它包直接调用已初始化好的 datastore 实例.
|
||||
engine *datastore
|
||||
)
|
||||
|
||||
// IStore 定义了 Store 层需要实现的方法.
|
||||
type IStore interface {
|
||||
DB(ctx context.Context) *gorm.DB
|
||||
TX(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
|
||||
User() system.UserStore
|
||||
}
|
||||
|
||||
// transactionKey 用于在 context.Context 中存储事务上下文的键.
|
||||
type transactionKey struct{}
|
||||
|
||||
// datastore 是 IStore 的具体实现.
|
||||
type datastore struct {
|
||||
core *gorm.DB
|
||||
}
|
||||
|
||||
// 确保 datastore 实现了 IStore 接口.
|
||||
var _ IStore = (*datastore)(nil)
|
||||
|
||||
// NewStore 创建一个 IStore 类型的实例.
|
||||
func NewStore(db *gorm.DB) *datastore {
|
||||
// 确保 engine 只被初始化一次
|
||||
once.Do(func() {
|
||||
engine = &datastore{db}
|
||||
})
|
||||
|
||||
return engine
|
||||
}
|
||||
|
||||
func (store *datastore) DB(ctx context.Context) *gorm.DB {
|
||||
db := store.core
|
||||
// 从上下文中提取事务实例
|
||||
if tx, ok := ctx.Value(transactionKey{}).(*gorm.DB); ok {
|
||||
db = tx
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (store *datastore) TX(ctx context.Context, fn func(ctx context.Context) error) error {
|
||||
return store.core.WithContext(ctx).Transaction(
|
||||
func(tx *gorm.DB) error {
|
||||
ctx = context.WithValue(ctx, transactionKey{}, tx)
|
||||
return fn(ctx)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Users 返回一个实现了 UserStore 接口的实例.
|
||||
func (store *datastore) User() system.UserStore {
|
||||
return system.NewUserStore(store.core)
|
||||
}
|
||||
63
internal/erpserver/store/system/user.go
Normal file
63
internal/erpserver/store/system/user.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserStore 定义了 user 模块在 store 层所实现的方法.
|
||||
type UserStore interface {
|
||||
Create(ctx context.Context, obj *system.User) error
|
||||
Update(ctx context.Context, obj *system.User) error
|
||||
Get(ctx context.Context, id int32) (*system.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (*system.User, error)
|
||||
|
||||
UserExpansion
|
||||
}
|
||||
|
||||
// UserExpansion 定义了用户操作的附加方法.
|
||||
type UserExpansion interface{}
|
||||
|
||||
// userStore 是 UserStore 接口的实现.
|
||||
type userStore struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// 确保 userStore 实现了 UserStore 接口.
|
||||
var _ UserStore = (*userStore)(nil)
|
||||
|
||||
// NewUserStore 创建 userStore 的实例.
|
||||
func NewUserStore(db *gorm.DB) *userStore {
|
||||
return &userStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *userStore) Create(ctx context.Context, obj *system.User) error {
|
||||
return s.db.WithContext(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (s *userStore) Update(ctx context.Context, obj *system.User) error {
|
||||
return s.db.WithContext(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (s *userStore) Get(ctx context.Context, id int32) (*system.User, error) {
|
||||
var user system.User
|
||||
err := s.db.WithContext(ctx).Where("id = ?", id).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *userStore) GetByEmail(ctx context.Context, email string) (*system.User, error) {
|
||||
var user system.User
|
||||
err := s.db.WithContext(ctx).Where("email = ?", email).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
92
internal/pkg/database/postgresql.go
Normal file
92
internal/pkg/database/postgresql.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// PostgreSQLOptions defines options for PostgreSQL database.
|
||||
type PostgreSQLOptions struct {
|
||||
Addr string
|
||||
Username string
|
||||
Password string
|
||||
Database string
|
||||
MaxIdleConnections int
|
||||
MaxOpenConnections int
|
||||
MaxConnectionLifeTime time.Duration
|
||||
// +optional
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
// DSN return DSN from PostgreSQLOptions.
|
||||
func (o *PostgreSQLOptions) DSN() string {
|
||||
splited := strings.Split(o.Addr, ":")
|
||||
host, port := splited[0], "5432"
|
||||
if len(splited) > 1 {
|
||||
port = splited[1]
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`user=%s password=%s host=%s port=%s dbname=%s sslmode=disable TimeZone=Asia/Shanghai`,
|
||||
o.Username,
|
||||
o.Password,
|
||||
host,
|
||||
port,
|
||||
o.Database,
|
||||
)
|
||||
}
|
||||
|
||||
// NewPostgreSQL create a new gorm db instance with the given options.
|
||||
func NewPostgreSQL(opts *PostgreSQLOptions) (*gorm.DB, error) {
|
||||
// Set default values to ensure all fields in opts are available.
|
||||
setPostgreSQLDefaults(opts)
|
||||
|
||||
db, err := gorm.Open(postgres.Open(opts.DSN()), &gorm.Config{
|
||||
// PrepareStmt executes the given query in cached statement.
|
||||
// This can improve performance.
|
||||
PrepareStmt: true,
|
||||
Logger: opts.Logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// SetMaxOpenConns sets the maximum number of open connections to the database.
|
||||
sqlDB.SetMaxOpenConns(opts.MaxOpenConnections)
|
||||
|
||||
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
|
||||
sqlDB.SetConnMaxLifetime(opts.MaxConnectionLifeTime)
|
||||
|
||||
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
|
||||
sqlDB.SetMaxIdleConns(opts.MaxIdleConnections)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// setPostgreSQLDefaults set available default values for some fields.
|
||||
func setPostgreSQLDefaults(opts *PostgreSQLOptions) {
|
||||
if opts.Addr == "" {
|
||||
opts.Addr = "127.0.0.1:5432"
|
||||
}
|
||||
if opts.MaxIdleConnections == 0 {
|
||||
opts.MaxIdleConnections = 100
|
||||
}
|
||||
if opts.MaxOpenConnections == 0 {
|
||||
opts.MaxOpenConnections = 100
|
||||
}
|
||||
if opts.MaxConnectionLifeTime == 0 {
|
||||
opts.MaxConnectionLifeTime = time.Duration(10) * time.Second
|
||||
}
|
||||
if opts.Logger == nil {
|
||||
opts.Logger = logger.Default
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user