v2
This commit is contained in:
@@ -25,6 +25,40 @@ type Config struct {
|
||||
Smb Smb `mapstructure:"smb" json:"smb" yaml:"smb"`
|
||||
}
|
||||
|
||||
func New(path string) (*Config, error) {
|
||||
fp := "."
|
||||
fn := ConfigDefaultFile
|
||||
if len(path) > 0 {
|
||||
fp, fn = filepath.Split(path)
|
||||
if len(fp) == 0 {
|
||||
fp = "."
|
||||
}
|
||||
}
|
||||
|
||||
v := viper.New()
|
||||
v.AddConfigPath(fp)
|
||||
v.SetConfigName(fn)
|
||||
v.SetConfigType("yaml")
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.WatchConfig()
|
||||
|
||||
var conf *Config
|
||||
v.OnConfigChange(func(e fsnotify.Event) {
|
||||
fmt.Println("config file changed:", e.Name)
|
||||
if err := v.Unmarshal(&conf); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
|
||||
if err := v.Unmarshal(&conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func Init(path string) error {
|
||||
fp := "."
|
||||
fn := ConfigDefaultFile
|
||||
|
||||
@@ -5,6 +5,7 @@ type SearchDto struct {
|
||||
SearchTimeEnd string `json:"searchTimeEnd"`
|
||||
SearchStatus int `json:"searchStatus"`
|
||||
SearchName string `json:"searchName"`
|
||||
SearchID int64 `json:"searchID"`
|
||||
SearchKey string `json:"searchKey"`
|
||||
SearchParentID int `json:"searchParentId"`
|
||||
SearchDepartmentID int `json:"searchDepartmentId"`
|
||||
|
||||
@@ -12,6 +12,7 @@ type DTreeDto struct {
|
||||
Title string `json:"title"`
|
||||
Last bool `json:"last"`
|
||||
ParentId string `json:"parentId"`
|
||||
Spread bool `json:"spread"`
|
||||
|
||||
Children []*DTreeDto `json:"children"`
|
||||
}
|
||||
|
||||
@@ -19,3 +19,9 @@ type XmSelectStrDto struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type XmSelectTreeDto struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Children []*XmSelectTreeDto `json:"children"`
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ import (
|
||||
|
||||
// ****************** conn ******************
|
||||
|
||||
func newDsn() string {
|
||||
func newDsn(conf config.DB) string {
|
||||
return fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
||||
config.File.DB.Username,
|
||||
config.File.DB.Password,
|
||||
config.File.DB.Host,
|
||||
config.File.DB.Port,
|
||||
config.File.DB.DBName)
|
||||
conf.Username,
|
||||
conf.Password,
|
||||
conf.Host,
|
||||
conf.Port,
|
||||
conf.DBName)
|
||||
}
|
||||
|
||||
// ****************** errors ******************
|
||||
@@ -69,9 +69,26 @@ type SQLStore struct {
|
||||
*Queries
|
||||
}
|
||||
|
||||
func NewIStore(ctx context.Context, conf config.DB) (Store, error) {
|
||||
pool, err := pgxpool.New(ctx, newDsn(conf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = pool.Ping(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SQLStore{
|
||||
connPool: pool,
|
||||
Queries: New(pool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewStore creates a new store
|
||||
func NewStore(ctx context.Context) error {
|
||||
pool, err := pgxpool.New(ctx, newDsn())
|
||||
pool, err := pgxpool.New(ctx, newDsn(config.File.DB))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
46
internal/erpserver/biz/biz.go
Normal file
46
internal/erpserver/biz/biz.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
db "management/internal/db/sqlc"
|
||||
commonv1 "management/internal/erpserver/biz/v1/common"
|
||||
systemv1 "management/internal/erpserver/biz/v1/system"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
// IBiz 定义了业务层需要实现的方法.
|
||||
type IBiz interface {
|
||||
// 获取公共业务接口.
|
||||
CommonV1() commonv1.CommonBiz
|
||||
// 获取系统业务接口.
|
||||
SystemV1() systemv1.SystemBiz
|
||||
}
|
||||
|
||||
// biz 是 IBiz 的一个具体实现.
|
||||
type biz struct {
|
||||
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 {
|
||||
return &biz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
// CommonV1 返回一个实现了 CommonBiz 接口的实例.
|
||||
func (b *biz) CommonV1() commonv1.CommonBiz {
|
||||
return commonv1.New()
|
||||
}
|
||||
|
||||
// SystemV1 返回一个实现了 SystemBiz 接口的实例.
|
||||
func (b *biz) SystemV1() systemv1.SystemBiz {
|
||||
return systemv1.New(b.store, b.redis, b.session)
|
||||
}
|
||||
36
internal/erpserver/biz/v1/common/captcha.go
Normal file
36
internal/erpserver/biz/v1/common/captcha.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
||||
// CaptchaBiz 定义处理验证码请求所需的方法.
|
||||
type CaptchaBiz interface {
|
||||
Generate(height int, width int, length int, maxSkew float64, dotCount int) (id, b64s, answer string, err error)
|
||||
Verify(id, answer string, clear bool) bool
|
||||
}
|
||||
|
||||
// captchaBiz 是 CaptchaBiz 接口的实现.
|
||||
type captchaBiz struct{}
|
||||
|
||||
// 确保 captchaBiz 实现了 CaptchaBiz 接口.
|
||||
var _ CaptchaBiz = (*captchaBiz)(nil)
|
||||
|
||||
func NewCaptcha() *captchaBiz {
|
||||
return &captchaBiz{}
|
||||
}
|
||||
|
||||
var captchaStore base64Captcha.Store = base64Captcha.DefaultMemStore
|
||||
|
||||
func (b *captchaBiz) Generate(height int, width int, length int, maxSkew float64, dotCount int) (id, b64s, answer string, err error) {
|
||||
driver := base64Captcha.NewDriverDigit(height, width, length, maxSkew, dotCount)
|
||||
// driver := base64Captcha.NewDriverString(config.File.Captcha.ImgHeight,
|
||||
// config.File.Captcha.ImgWidth,
|
||||
// 6, 1, keyLong, source, nil, nil, nil)
|
||||
cp := base64Captcha.NewCaptcha(driver, captchaStore)
|
||||
return cp.Generate()
|
||||
}
|
||||
|
||||
func (b *captchaBiz) Verify(id, answer string, clear bool) bool {
|
||||
return captchaStore.Verify(id, answer, clear)
|
||||
}
|
||||
17
internal/erpserver/biz/v1/common/common.go
Normal file
17
internal/erpserver/biz/v1/common/common.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package common
|
||||
|
||||
type CommonBiz interface {
|
||||
CaptchaBiz() CaptchaBiz
|
||||
}
|
||||
|
||||
type commonBiz struct{}
|
||||
|
||||
var _ CommonBiz = (*commonBiz)(nil)
|
||||
|
||||
func New() *commonBiz {
|
||||
return &commonBiz{}
|
||||
}
|
||||
|
||||
func (b *commonBiz) CaptchaBiz() CaptchaBiz {
|
||||
return NewCaptcha()
|
||||
}
|
||||
31
internal/erpserver/biz/v1/system/audit.go
Normal file
31
internal/erpserver/biz/v1/system/audit.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
db "management/internal/db/sqlc"
|
||||
)
|
||||
|
||||
type AuditBiz interface {
|
||||
Create(ctx context.Context, arg *db.CreateSysAuditLogParams) error
|
||||
|
||||
AuditExpansion
|
||||
}
|
||||
|
||||
type AuditExpansion interface{}
|
||||
|
||||
type auditBiz struct {
|
||||
store db.Store
|
||||
}
|
||||
|
||||
var _ AuditBiz = (*auditBiz)(nil)
|
||||
|
||||
func NewAudit(store db.Store) *auditBiz {
|
||||
return &auditBiz{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *auditBiz) Create(ctx context.Context, arg *db.CreateSysAuditLogParams) error {
|
||||
return b.store.CreateSysAuditLog(ctx, arg)
|
||||
}
|
||||
63
internal/erpserver/biz/v1/system/config.go
Normal file
63
internal/erpserver/biz/v1/system/config.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/global/keys"
|
||||
"management/internal/global/pearadmin"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type ConfigBiz interface {
|
||||
ConfigExpansion
|
||||
}
|
||||
|
||||
type ConfigExpansion interface {
|
||||
Pear(ctx context.Context) (*dto.PearConfig, error)
|
||||
}
|
||||
|
||||
type configBiz struct {
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
var _ ConfigBiz = (*configBiz)(nil)
|
||||
|
||||
func NewConfig(store db.Store, redis redis.IRedis, session session.ISession) *configBiz {
|
||||
return &configBiz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *configBiz) Pear(ctx context.Context) (*dto.PearConfig, error) {
|
||||
// 判断redis是否存储
|
||||
key := keys.GetManageKey(ctx, keys.PearAdmin)
|
||||
bs, err := b.redis.GetBytes(ctx, key)
|
||||
if err == nil {
|
||||
var res *dto.PearConfig
|
||||
if err := json.Unmarshal(bs, &res); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
conf, err := b.store.GetSysConfigByKey(ctx, pearadmin.PearKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pear dto.PearConfig
|
||||
if err := json.Unmarshal(conf.Value, &pear); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = b.redis.Set(ctx, key, conf.Value, time.Hour*6)
|
||||
return &pear, nil
|
||||
}
|
||||
183
internal/erpserver/biz/v1/system/department.go
Normal file
183
internal/erpserver/biz/v1/system/department.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/erpserver/model/view"
|
||||
"management/internal/global/keys"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type DepartmentBiz interface {
|
||||
Create(ctx context.Context, arg *db.CreateSysDepartmentParams) (*db.SysDepartment, error)
|
||||
Update(ctx context.Context, arg *db.UpdateSysDepartmentParams) (*db.SysDepartment, error)
|
||||
All(ctx context.Context) ([]*db.SysDepartment, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*db.SysDepartment, int64, error)
|
||||
Get(ctx context.Context, id int32) (*db.SysDepartment, error)
|
||||
Refresh(ctx context.Context) ([]*db.SysDepartment, error)
|
||||
RebuildParentPath(ctx context.Context) error
|
||||
|
||||
Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error)
|
||||
XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, error)
|
||||
|
||||
DepartmentExpansion
|
||||
}
|
||||
|
||||
type DepartmentExpansion interface{}
|
||||
|
||||
type departmentBiz struct {
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
var _ DepartmentBiz = (*departmentBiz)(nil)
|
||||
|
||||
func NewDepartment(store db.Store, redis redis.IRedis, session session.ISession) *departmentBiz {
|
||||
return &departmentBiz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *departmentBiz) All(ctx context.Context) ([]*db.SysDepartment, error) {
|
||||
key := keys.GetManageKey(ctx, keys.AllDepartments)
|
||||
bs, err := redis.GetBytes(ctx, key)
|
||||
if err == nil {
|
||||
var res []*db.SysDepartment
|
||||
if err := json.Unmarshal(bs, &res); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return b.Refresh(ctx)
|
||||
}
|
||||
|
||||
func (b *departmentBiz) List(ctx context.Context, q dto.SearchDto) ([]*db.SysDepartment, int64, error) {
|
||||
countArg := &db.CountSysDepartmentConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
Name: q.SearchName,
|
||||
}
|
||||
|
||||
dataArg := &db.ListSysDepartmentConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
Name: q.SearchName,
|
||||
Skip: (int32(q.Page) - 1) * int32(q.Rows),
|
||||
Size: int32(q.Rows),
|
||||
}
|
||||
count, err := b.store.CountSysDepartmentCondition(ctx, countArg)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
departs, err := b.store.ListSysDepartmentCondition(ctx, dataArg)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return departs, count, nil
|
||||
}
|
||||
|
||||
func (b *departmentBiz) Get(ctx context.Context, id int32) (*db.SysDepartment, error) {
|
||||
return b.store.GetSysDepartment(ctx, id)
|
||||
}
|
||||
|
||||
func (b *departmentBiz) Create(ctx context.Context, arg *db.CreateSysDepartmentParams) (*db.SysDepartment, error) {
|
||||
return b.store.CreateSysDepartment(ctx, arg)
|
||||
}
|
||||
|
||||
func (b *departmentBiz) Update(ctx context.Context, arg *db.UpdateSysDepartmentParams) (*db.SysDepartment, error) {
|
||||
return b.store.UpdateSysDepartment(ctx, arg)
|
||||
}
|
||||
|
||||
func (b *departmentBiz) Refresh(ctx context.Context) ([]*db.SysDepartment, error) {
|
||||
all, err := b.store.AllSysDepartment(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(all)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := keys.GetManageKey(ctx, keys.AllDepartments)
|
||||
err = redis.Set(ctx, key, bs, time.Hour*6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func (h *departmentBiz) RebuildParentPath(ctx context.Context) error {
|
||||
return h.store.SysDepartmentRebuildPath(ctx)
|
||||
}
|
||||
|
||||
func (h *departmentBiz) Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error) {
|
||||
all, err := h.All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toLayuiTree(id, all), nil
|
||||
}
|
||||
|
||||
func (h *departmentBiz) XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, error) {
|
||||
all, err := h.All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toXmSelectTree(id, all), nil
|
||||
}
|
||||
|
||||
func toXmSelectTree(parentId int32, data []*db.SysDepartment) []*view.XmSelectTree {
|
||||
var res []*view.XmSelectTree
|
||||
for _, v := range data {
|
||||
if v.ParentID == parentId {
|
||||
item := view.XmSelectTree{
|
||||
Name: v.Name,
|
||||
Value: strconv.FormatInt(int64(v.ID), 10),
|
||||
Children: toXmSelectTree(v.ID, data),
|
||||
}
|
||||
res = append(res, &item)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func toLayuiTree(parentId int32, data []*db.SysDepartment) []*view.LayuiTree {
|
||||
var res []*view.LayuiTree
|
||||
for _, v := range data {
|
||||
if v.ParentID == parentId {
|
||||
item := view.LayuiTree{}
|
||||
item.ID = strconv.FormatInt(int64(v.ID), 10)
|
||||
item.Title = v.Name
|
||||
item.Children = toLayuiTree(v.ID, data)
|
||||
if v.ParentID == 0 {
|
||||
item.Spread = true
|
||||
}
|
||||
res = append(res, &item)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
311
internal/erpserver/biz/v1/system/menu.go
Normal file
311
internal/erpserver/biz/v1/system/menu.go
Normal file
@@ -0,0 +1,311 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/global/keys"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type MenuBiz interface {
|
||||
MenuExpansion
|
||||
}
|
||||
|
||||
type MenuExpansion interface {
|
||||
GetSysMenuByUrl(ctx context.Context, url string) (*db.SysMenu, error)
|
||||
ListOwnerMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error)
|
||||
RecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error)
|
||||
SetRecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error)
|
||||
MapOwnerMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error)
|
||||
SetOwnerMapMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error)
|
||||
}
|
||||
|
||||
type menuBiz struct {
|
||||
store db.Store
|
||||
redis redis.IRedis
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
var _ MenuBiz = (*menuBiz)(nil)
|
||||
|
||||
func NewMenu(store db.Store, redis redis.IRedis, session session.ISession) *menuBiz {
|
||||
return &menuBiz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *menuBiz) GetSysMenuByUrl(ctx context.Context, url string) (*db.SysMenu, error) {
|
||||
return b.store.GetSysMenuByUrl(ctx, url)
|
||||
}
|
||||
|
||||
func (b *menuBiz) ListOwnerMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) {
|
||||
// 判断redis是否存储
|
||||
key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID)
|
||||
bs, err := b.redis.GetBytes(ctx, key)
|
||||
if err == nil {
|
||||
var res []*dto.OwnerMenuDto
|
||||
if err := json.Unmarshal(bs, &res); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return b.SetOwnerListMenuByRoleID(ctx, roleID)
|
||||
}
|
||||
|
||||
func (b *menuBiz) SetOwnerListMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) {
|
||||
menus, err := b.ownerMenusByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []*dto.OwnerMenuDto
|
||||
for _, menu := range menus {
|
||||
res = append(res, &dto.OwnerMenuDto{
|
||||
ID: menu.ID,
|
||||
DisplayName: menu.DisplayName,
|
||||
Url: menu.Url,
|
||||
ParentID: menu.ParentID,
|
||||
Avatar: menu.Avatar,
|
||||
Style: menu.Style,
|
||||
IsList: menu.IsList,
|
||||
})
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID)
|
||||
_ = redis.Set(ctx, key, bs, time.Hour*6)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (b *menuBiz) RecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) {
|
||||
// 判断redis是否存储
|
||||
key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID)
|
||||
bs, err := b.redis.GetBytes(ctx, key)
|
||||
if err == nil {
|
||||
var res []*dto.MenuUIDto
|
||||
if err := json.Unmarshal(bs, &res); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return b.SetRecursiveSysMenus(ctx, roleID)
|
||||
}
|
||||
|
||||
func (b *menuBiz) SetRecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) {
|
||||
// 判断当前用户是否有vip角色
|
||||
role, err := b.store.GetSysRole(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var menus []*db.SysMenu
|
||||
if role.Vip {
|
||||
// vip 用户
|
||||
all, err := b.store.RecursiveSysMenus(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menus = convertToMenuUIDto(all)
|
||||
|
||||
} else {
|
||||
// not vip
|
||||
all, err := b.store.RecursiveSysMenusByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menus = convertToMenuUIDto2(all)
|
||||
}
|
||||
menuList := uniqueSysMenus(menus)
|
||||
if len(menuList) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tree := convertToUITree(menuList, 0)
|
||||
bs, err := json.Marshal(tree)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID)
|
||||
_ = redis.Set(ctx, key, bs, time.Hour*6)
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func (b *menuBiz) MapOwnerMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) {
|
||||
// 判断redis是否存储
|
||||
key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID)
|
||||
bs, err := b.redis.GetBytes(ctx, key)
|
||||
if err == nil {
|
||||
var res map[string]*dto.OwnerMenuDto
|
||||
if err := json.Unmarshal(bs, &res); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return b.SetOwnerMapMenuByRoleID(ctx, roleID)
|
||||
}
|
||||
|
||||
func (b *menuBiz) SetOwnerMapMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) {
|
||||
result := make(map[string]*dto.OwnerMenuDto)
|
||||
menus, err := b.ownerMenusByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, menu := range menus {
|
||||
result[menu.Url] = &dto.OwnerMenuDto{
|
||||
ID: menu.ID,
|
||||
DisplayName: menu.DisplayName,
|
||||
Url: menu.Url,
|
||||
ParentID: menu.ParentID,
|
||||
Avatar: menu.Avatar,
|
||||
Style: menu.Style,
|
||||
IsList: menu.IsList,
|
||||
}
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID)
|
||||
_ = redis.Set(ctx, key, bs, time.Hour*6)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *menuBiz) ownerMenusByRoleID(ctx context.Context, roleID int32) ([]*db.SysMenu, error) {
|
||||
// 判断当前用户是否有vip角色
|
||||
role, err := b.store.GetSysRole(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var e error
|
||||
var menus []*db.SysMenu
|
||||
if role.Vip {
|
||||
// vip 用户
|
||||
menus, e = b.store.AllSysMenu(ctx)
|
||||
if e != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
// not vip
|
||||
menus, e = b.store.ListSysMenuByRoleID(ctx, roleID)
|
||||
if e != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
func convertToMenuUIDto(data []*db.RecursiveSysMenusRow) []*db.SysMenu {
|
||||
var res []*db.SysMenu
|
||||
|
||||
for _, item := range data {
|
||||
temp := &db.SysMenu{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
DisplayName: item.DisplayName,
|
||||
Url: item.Url,
|
||||
Type: item.Type,
|
||||
ParentID: item.ParentID,
|
||||
ParentPath: item.ParentPath,
|
||||
Avatar: item.Avatar,
|
||||
Style: item.Style,
|
||||
Visible: item.Visible,
|
||||
IsList: item.IsList,
|
||||
Status: item.Status,
|
||||
Sort: item.Sort,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
}
|
||||
res = append(res, temp)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func convertToMenuUIDto2(data []*db.RecursiveSysMenusByRoleIDRow) []*db.SysMenu {
|
||||
var res []*db.SysMenu
|
||||
|
||||
for _, item := range data {
|
||||
temp := &db.SysMenu{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
DisplayName: item.DisplayName,
|
||||
Url: item.Url,
|
||||
Type: item.Type,
|
||||
ParentID: item.ParentID,
|
||||
ParentPath: item.ParentPath,
|
||||
Avatar: item.Avatar,
|
||||
Style: item.Style,
|
||||
Visible: item.Visible,
|
||||
IsList: item.IsList,
|
||||
Status: item.Status,
|
||||
Sort: item.Sort,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
}
|
||||
res = append(res, temp)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func convertToUITree(data []*db.SysMenu, parentID int32) []*dto.MenuUIDto {
|
||||
var root []*dto.MenuUIDto
|
||||
for _, item := range data {
|
||||
if item.ParentID == parentID {
|
||||
if item.IsList {
|
||||
temp := &dto.MenuUIDto{
|
||||
ID: strings.ToLower(item.Url),
|
||||
Title: item.DisplayName,
|
||||
Icon: item.Avatar,
|
||||
Type: 1,
|
||||
OpenType: "_iframe",
|
||||
// OpenType: "_component",
|
||||
Href: item.Url,
|
||||
}
|
||||
root = append(root, temp)
|
||||
} else {
|
||||
temp := &dto.MenuUIDto{
|
||||
ID: strconv.Itoa(int(item.ID)),
|
||||
Title: item.DisplayName,
|
||||
Icon: item.Avatar,
|
||||
Type: 0,
|
||||
}
|
||||
temp.Children = convertToUITree(data, item.ID)
|
||||
root = append(root, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
func uniqueSysMenus(sm []*db.SysMenu) []*db.SysMenu {
|
||||
res := make([]*db.SysMenu, 0) // 返回的新切片
|
||||
m1 := make(map[int32]byte) // 用来去重的临时map
|
||||
for _, v := range sm {
|
||||
if _, ok := m1[v.ID]; !ok {
|
||||
m1[v.ID] = 1
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
51
internal/erpserver/biz/v1/system/system.go
Normal file
51
internal/erpserver/biz/v1/system/system.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/pkg/redis"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type SystemBiz interface {
|
||||
UserBiz() UserBiz
|
||||
MenuBiz() MenuBiz
|
||||
DepartmentBiz() DepartmentBiz
|
||||
AuditBiz() AuditBiz
|
||||
ConfigBiz() ConfigBiz
|
||||
}
|
||||
|
||||
type systemBiz struct {
|
||||
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 {
|
||||
return &systemBiz{
|
||||
store: store,
|
||||
redis: redis,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *systemBiz) UserBiz() UserBiz {
|
||||
return NewUser(b.store, b.session)
|
||||
}
|
||||
|
||||
func (b *systemBiz) MenuBiz() MenuBiz {
|
||||
return NewMenu(b.store, b.redis, b.session)
|
||||
}
|
||||
|
||||
func (b *systemBiz) DepartmentBiz() DepartmentBiz {
|
||||
return NewDepartment(b.store, b.redis, b.session)
|
||||
}
|
||||
|
||||
func (b *systemBiz) AuditBiz() AuditBiz {
|
||||
return NewAudit(b.store)
|
||||
}
|
||||
|
||||
func (b *systemBiz) ConfigBiz() ConfigBiz {
|
||||
return NewConfig(b.store, b.redis, b.session)
|
||||
}
|
||||
117
internal/erpserver/biz/v1/system/user.go
Normal file
117
internal/erpserver/biz/v1/system/user.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/erpserver/model/req"
|
||||
"management/internal/global/know"
|
||||
"management/internal/pkg/crypto"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
// UserBiz 定义处理用户请求所需的方法.
|
||||
type UserBiz interface {
|
||||
Create(ctx context.Context, req *db.CreateSysUserParams) (*db.SysUser, error)
|
||||
|
||||
UserExpansion
|
||||
}
|
||||
|
||||
// UserExpansion 定义用户操作的扩展方法.
|
||||
type UserExpansion interface {
|
||||
Login(ctx context.Context, req *req.Login) error
|
||||
}
|
||||
|
||||
// userBiz 是 UserBiz 接口的实现.
|
||||
type userBiz struct {
|
||||
store db.Store
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
// 确保 userBiz 实现了 UserBiz 接口.
|
||||
var _ UserBiz = (*userBiz)(nil)
|
||||
|
||||
func NewUser(store db.Store, session session.ISession) *userBiz {
|
||||
return &userBiz{
|
||||
store: store,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *userBiz) Create(ctx context.Context, req *db.CreateSysUserParams) (*db.SysUser, error) {
|
||||
return b.store.CreateSysUser(ctx, req)
|
||||
}
|
||||
|
||||
func (b *userBiz) Login(ctx context.Context, req *req.Login) error {
|
||||
log := &db.CreateSysUserLoginLogParams{
|
||||
CreatedAt: time.Now(),
|
||||
Email: req.Email,
|
||||
IsSuccess: false,
|
||||
RefererUrl: req.Referrer,
|
||||
Url: req.Url,
|
||||
Os: req.Os,
|
||||
Ip: req.Ip,
|
||||
Browser: req.Browser,
|
||||
}
|
||||
|
||||
user, err := b.store.GetSysUserByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
log.Message = err.Error()
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return err
|
||||
}
|
||||
log.UserUuid = user.Uuid
|
||||
log.Username = user.Username
|
||||
|
||||
err = crypto.BcryptComparePassword(user.HashedPassword, req.Password+user.Salt)
|
||||
if err != nil {
|
||||
log.Message = "compare password failed"
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return errors.New(log.Message)
|
||||
}
|
||||
|
||||
// 登陆成功
|
||||
|
||||
if user.RoleID == 0 {
|
||||
log.Message = "账号没有配置角色, 请联系管理员"
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return errors.New(log.Message)
|
||||
}
|
||||
|
||||
sysRole, err := b.store.GetSysRole(ctx, user.RoleID)
|
||||
if err != nil {
|
||||
log.Message = "账号配置的角色错误, 请联系管理员"
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return errors.New(log.Message)
|
||||
}
|
||||
|
||||
auth := dto.AuthorizeUser{
|
||||
ID: user.ID,
|
||||
Uuid: user.Uuid,
|
||||
Email: user.Email,
|
||||
Username: user.Username,
|
||||
RoleID: sysRole.ID,
|
||||
RoleName: sysRole.Name,
|
||||
OS: log.Os,
|
||||
IP: log.Ip,
|
||||
Browser: log.Browser,
|
||||
}
|
||||
|
||||
gob, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
log.Message = err.Error()
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return err
|
||||
}
|
||||
|
||||
b.session.Put(ctx, know.StoreName, gob)
|
||||
|
||||
log.IsSuccess = true
|
||||
log.Message = "登陆成功"
|
||||
_ = b.store.CreateSysUserLoginLog(ctx, log)
|
||||
return nil
|
||||
}
|
||||
56
internal/erpserver/handler/common/captcha.go
Normal file
56
internal/erpserver/handler/common/captcha.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"management/internal/config"
|
||||
commonv1 "management/internal/erpserver/biz/v1/common"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type CaptchaHandler interface {
|
||||
Captcha(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// captchaHandler 是 CaptchaHandler 接口的实现.
|
||||
type captchaHandler struct {
|
||||
conf *config.Captcha
|
||||
render tpl.Renderer
|
||||
biz commonv1.CaptchaBiz
|
||||
}
|
||||
|
||||
// 确保 captchaHandler 实现了 CaptchaHandler 接口.
|
||||
var _ CaptchaHandler = (*captchaHandler)(nil)
|
||||
|
||||
func NewCaptchaHandler(conf *config.Captcha, render tpl.Renderer, biz commonv1.CaptchaBiz) *captchaHandler {
|
||||
return &captchaHandler{
|
||||
conf: conf,
|
||||
render: render,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
type CaptchaResponse struct {
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
PicPath string `json:"pic_path"`
|
||||
CaptchaLength int `json:"captcha_length"`
|
||||
OpenCaptcha int `json:"open_captcha"`
|
||||
}
|
||||
|
||||
func (h *captchaHandler) Captcha(w http.ResponseWriter, r *http.Request) {
|
||||
keyLong := h.conf.KeyLong
|
||||
oc := h.conf.OpenCaptcha
|
||||
id, b64s, _, err := h.biz.Generate(h.conf.ImgHeight, h.conf.ImgWidth, keyLong, 0.7, 80)
|
||||
if err != nil {
|
||||
h.render.JSON(w, tpl.Response{Success: false, Message: "获取验证码失败"})
|
||||
return
|
||||
}
|
||||
|
||||
rsp := CaptchaResponse{
|
||||
CaptchaID: id,
|
||||
PicPath: b64s,
|
||||
CaptchaLength: keyLong,
|
||||
OpenCaptcha: oc,
|
||||
}
|
||||
h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: rsp})
|
||||
}
|
||||
31
internal/erpserver/handler/common/common.go
Normal file
31
internal/erpserver/handler/common/common.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"management/internal/config"
|
||||
commonv1 "management/internal/erpserver/biz/v1/common"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type CommonHandler interface {
|
||||
CaptchaHandler() CaptchaHandler
|
||||
}
|
||||
|
||||
type commonHandler struct {
|
||||
conf *config.Config
|
||||
render tpl.Renderer
|
||||
biz commonv1.CommonBiz
|
||||
}
|
||||
|
||||
var _ CommonHandler = (*commonHandler)(nil)
|
||||
|
||||
func NewCommonHandler(conf *config.Config, render tpl.Renderer, biz commonv1.CommonBiz) *commonHandler {
|
||||
return &commonHandler{
|
||||
conf: conf,
|
||||
render: render,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *commonHandler) CaptchaHandler() CaptchaHandler {
|
||||
return NewCaptchaHandler(&h.conf.Captcha, h.render, h.biz.CaptchaBiz())
|
||||
}
|
||||
55
internal/erpserver/handler/handler.go
Normal file
55
internal/erpserver/handler/handler.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"management/internal/config"
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/erpserver/handler/common"
|
||||
"management/internal/erpserver/handler/system"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
// IHandler 定义了Handler需要实现的方法.
|
||||
type IHandler interface {
|
||||
// 获取 Common Handler 接口.
|
||||
CommonHandler() common.CommonHandler
|
||||
|
||||
// 获取首页
|
||||
Home(w http.ResponseWriter, req *http.Request)
|
||||
|
||||
// 获取 System Handler 接口.
|
||||
SystemHandler() system.SystemHandler
|
||||
}
|
||||
|
||||
// handler 是 IHandler 的一个具体实现.
|
||||
type handler struct {
|
||||
conf *config.Config
|
||||
render tpl.Renderer
|
||||
session session.ISession
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
// 确保 handler 实现了 IHandler 接口.
|
||||
var _ IHandler = (*handler)(nil)
|
||||
|
||||
// NewHandler 创建一个 IHandler 类型的实例.
|
||||
func NewHandler(conf *config.Config, render tpl.Renderer, session session.ISession, biz biz.IBiz) *handler {
|
||||
return &handler{
|
||||
conf: conf,
|
||||
render: render,
|
||||
session: session,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
// CommonHandler 返回一个实现了 CommonHandler 接口的实例.
|
||||
func (h *handler) CommonHandler() common.CommonHandler {
|
||||
return common.NewCommonHandler(h.conf, h.render, h.biz.CommonV1())
|
||||
}
|
||||
|
||||
// SystemHandler 返回一个实现了 SystemHandler 接口的实例.
|
||||
func (h *handler) SystemHandler() system.SystemHandler {
|
||||
return system.NewSystemHandler(h.render, h.session, h.biz)
|
||||
}
|
||||
7
internal/erpserver/handler/home.go
Normal file
7
internal/erpserver/handler/home.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package handler
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (h *handler) Home(w http.ResponseWriter, r *http.Request) {
|
||||
h.render.HTML(w, r, "home/home.tmpl", nil)
|
||||
}
|
||||
50
internal/erpserver/handler/system/config.go
Normal file
50
internal/erpserver/handler/system/config.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type ConfigHandler interface {
|
||||
// Add(w http.ResponseWriter, r *http.Request)
|
||||
// Edit(w http.ResponseWriter, r *http.Request)
|
||||
// Save(w http.ResponseWriter, r *http.Request)
|
||||
// List(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
ConfigExpansion
|
||||
}
|
||||
|
||||
type ConfigExpansion interface {
|
||||
Pear(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// configHandler 是 ConfigHandler 接口的实现.
|
||||
type configHandler struct {
|
||||
render tpl.Renderer
|
||||
session session.ISession
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
// 确保 userHandler 实现了 ConfigHandler 接口.
|
||||
var _ ConfigHandler = (*configHandler)(nil)
|
||||
|
||||
func NewConfigHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *configHandler {
|
||||
return &configHandler{
|
||||
render: render,
|
||||
session: session,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *configHandler) Pear(w http.ResponseWriter, r *http.Request) {
|
||||
pear, err := h.biz.SystemV1().ConfigBiz().Pear(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSON(w, pear)
|
||||
}
|
||||
206
internal/erpserver/handler/system/department.go
Normal file
206
internal/erpserver/handler/system/department.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/pkg/convertor"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type DepartmentHandler interface {
|
||||
List(w http.ResponseWriter, r *http.Request)
|
||||
Add(w http.ResponseWriter, r *http.Request)
|
||||
AddChildren(w http.ResponseWriter, r *http.Request)
|
||||
Edit(w http.ResponseWriter, r *http.Request)
|
||||
Save(w http.ResponseWriter, r *http.Request)
|
||||
Tree(w http.ResponseWriter, r *http.Request)
|
||||
Refresh(w http.ResponseWriter, r *http.Request)
|
||||
RebuildParentPath(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type departmentHandler struct {
|
||||
render tpl.Renderer
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
var _ DepartmentHandler = (*departmentHandler)(nil)
|
||||
|
||||
func NewDepartmentHandler(render tpl.Renderer, biz biz.IBiz) *departmentHandler {
|
||||
return &departmentHandler{
|
||||
render: render,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *departmentHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
h.render.HTML(w, r, "department/list.tmpl", nil)
|
||||
return
|
||||
} else if r.Method == http.MethodPost {
|
||||
var q dto.SearchDto
|
||||
q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999)
|
||||
q.SearchParentID = convertor.ConvertInt(r.PostFormValue("parentId"), 0)
|
||||
q.SearchName = r.PostFormValue("name")
|
||||
q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0)
|
||||
q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1)
|
||||
q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10)
|
||||
res, count, err := h.biz.SystemV1().DepartmentBiz().List(r.Context(), q)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := tpl.ResponseList{
|
||||
Code: 0,
|
||||
Message: "ok",
|
||||
Count: count,
|
||||
Data: res,
|
||||
}
|
||||
h.render.JSON(w, data)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func (h *departmentHandler) Add(w http.ResponseWriter, r *http.Request) {
|
||||
h.render.HTML(w, r, "department/edit.tmpl", map[string]any{
|
||||
"Item": &db.SysDepartment{Sort: 6666},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *departmentHandler) AddChildren(w http.ResponseWriter, r *http.Request) {
|
||||
vars := r.URL.Query()
|
||||
parentID := convertor.QueryInt(vars, "parentID", 0)
|
||||
vm := &db.SysDepartment{ParentID: int32(parentID), Sort: 6666}
|
||||
h.render.HTML(w, r, "department/edit.tmpl", map[string]any{
|
||||
"Item": vm,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *departmentHandler) Edit(w http.ResponseWriter, r *http.Request) {
|
||||
vars := r.URL.Query()
|
||||
id := convertor.QueryInt[int32](vars, "id", 0)
|
||||
vm := &db.SysDepartment{Sort: 6666}
|
||||
if id > 0 {
|
||||
vm, _ = h.biz.SystemV1().DepartmentBiz().Get(r.Context(), id)
|
||||
}
|
||||
h.render.HTML(w, r, "department/edit.tmpl", map[string]any{
|
||||
"Item": vm,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *departmentHandler) Save(w http.ResponseWriter, r *http.Request) {
|
||||
id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0)
|
||||
ParentID := convertor.ConvertInt[int32](r.PostFormValue("ParentID"), 0)
|
||||
name := r.PostFormValue("Name")
|
||||
sort := convertor.ConvertInt[int32](r.PostFormValue("Sort"), 6666)
|
||||
status := convertor.ConvertInt[int32](r.PostFormValue("Status"), 9999)
|
||||
|
||||
ctx := r.Context()
|
||||
var parent *db.SysDepartment
|
||||
if ParentID > 0 {
|
||||
var err error
|
||||
parent, err = h.biz.SystemV1().DepartmentBiz().Get(ctx, ParentID)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, "父级节点错误")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if id == 0 {
|
||||
arg := db.CreateSysDepartmentParams{
|
||||
Name: name,
|
||||
ParentID: ParentID,
|
||||
ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID),
|
||||
Status: status,
|
||||
Sort: sort,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
_, err := h.biz.SystemV1().DepartmentBiz().Create(ctx, &arg)
|
||||
if err != nil {
|
||||
if db.IsUniqueViolation(err) {
|
||||
h.render.JSONERR(w, "部门名称已存在")
|
||||
return
|
||||
}
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSONOK(w, "添加成功")
|
||||
} else {
|
||||
res, err := h.biz.SystemV1().DepartmentBiz().Get(ctx, id)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
arg := &db.UpdateSysDepartmentParams{
|
||||
ID: res.ID,
|
||||
Name: name,
|
||||
ParentID: ParentID,
|
||||
ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID),
|
||||
Status: status,
|
||||
Sort: sort,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
_, err = h.biz.SystemV1().DepartmentBiz().Update(ctx, arg)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSONOK(w, "更新成功")
|
||||
}
|
||||
}
|
||||
|
||||
func (h *departmentHandler) Tree(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
vars := r.URL.Query()
|
||||
if vars.Get("type") == "xmselect" {
|
||||
res, err := h.biz.SystemV1().DepartmentBiz().XmSelect(ctx, 0)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSON(w, res)
|
||||
return
|
||||
} else {
|
||||
res, err := h.biz.SystemV1().DepartmentBiz().Tree(ctx, 0)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSON(w, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *departmentHandler) Refresh(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := h.biz.SystemV1().DepartmentBiz().Refresh(r.Context())
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSONOK(w, "刷新成功")
|
||||
}
|
||||
|
||||
func (h *departmentHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
err := h.biz.SystemV1().DepartmentBiz().RebuildParentPath(ctx)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSONOK(w, "重建成功")
|
||||
}
|
||||
53
internal/erpserver/handler/system/menu.go
Normal file
53
internal/erpserver/handler/system/menu.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/global/know"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type MenuHandler interface {
|
||||
MenuExpansion
|
||||
}
|
||||
|
||||
type MenuExpansion interface {
|
||||
Menus(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type menuHandler struct {
|
||||
render tpl.Renderer
|
||||
session session.ISession
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
var _ MenuHandler = (*menuHandler)(nil)
|
||||
|
||||
func NewMenuHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *menuHandler {
|
||||
return &menuHandler{
|
||||
render: render,
|
||||
session: session,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *menuHandler) Menus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
b := h.session.GetBytes(ctx, know.StoreName)
|
||||
var u dto.AuthorizeUser
|
||||
if err := json.Unmarshal(b, &u); err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
menus, err := h.biz.SystemV1().MenuBiz().RecursiveSysMenus(ctx, u.RoleID)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSON(w, menus)
|
||||
}
|
||||
46
internal/erpserver/handler/system/system.go
Normal file
46
internal/erpserver/handler/system/system.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/tpl"
|
||||
)
|
||||
|
||||
type SystemHandler interface {
|
||||
UserHandler() UserHandler
|
||||
MenuHandler() MenuHandler
|
||||
DepartmentHandler() DepartmentHandler
|
||||
ConfigHandler() ConfigHandler
|
||||
}
|
||||
|
||||
type systemHandler struct {
|
||||
render tpl.Renderer
|
||||
session session.ISession
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
var _ SystemHandler = (*systemHandler)(nil)
|
||||
|
||||
func NewSystemHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *systemHandler {
|
||||
return &systemHandler{
|
||||
render: render,
|
||||
session: session,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *systemHandler) UserHandler() UserHandler {
|
||||
return NewUserHandler(h.render, h.session, h.biz)
|
||||
}
|
||||
|
||||
func (h *systemHandler) MenuHandler() MenuHandler {
|
||||
return NewMenuHandler(h.render, h.session, h.biz)
|
||||
}
|
||||
|
||||
func (h *systemHandler) DepartmentHandler() DepartmentHandler {
|
||||
return NewDepartmentHandler(h.render, h.biz)
|
||||
}
|
||||
|
||||
func (h *systemHandler) ConfigHandler() ConfigHandler {
|
||||
return NewConfigHandler(h.render, h.session, h.biz)
|
||||
}
|
||||
127
internal/erpserver/handler/system/user.go
Normal file
127
internal/erpserver/handler/system/user.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/erpserver/biz"
|
||||
"management/internal/erpserver/model/req"
|
||||
"management/internal/global/know"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/tpl"
|
||||
|
||||
"github.com/zhang2092/browser"
|
||||
)
|
||||
|
||||
type UserHandler interface {
|
||||
Add(w http.ResponseWriter, r *http.Request)
|
||||
Edit(w http.ResponseWriter, r *http.Request)
|
||||
Save(w http.ResponseWriter, r *http.Request)
|
||||
List(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
UserExpansion
|
||||
}
|
||||
|
||||
type UserExpansion interface {
|
||||
Login(w http.ResponseWriter, r *http.Request)
|
||||
Logout(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// userHandler 是 UserHandler 接口的实现.
|
||||
type userHandler struct {
|
||||
render tpl.Renderer
|
||||
session session.ISession
|
||||
biz biz.IBiz
|
||||
}
|
||||
|
||||
// 确保 userHandler 实现了 UserHandler 接口.
|
||||
var _ UserHandler = (*userHandler)(nil)
|
||||
|
||||
func NewUserHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *userHandler {
|
||||
return &userHandler{
|
||||
render: render,
|
||||
session: session,
|
||||
biz: biz,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *userHandler) Add(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func (h *userHandler) Edit(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func (h *userHandler) Save(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func (h *userHandler) List(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func (h *userHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if r.Method == http.MethodGet {
|
||||
var user dto.AuthorizeUser
|
||||
u := h.session.GetBytes(ctx, know.StoreName)
|
||||
if err := json.Unmarshal(u, &user); err == nil {
|
||||
// 判断租户是否一致, 一致则刷新令牌,跳转到首页
|
||||
if err := h.session.RenewToken(ctx); err == nil {
|
||||
h.session.Put(ctx, know.StoreName, u)
|
||||
http.Redirect(w, r, "/home.html", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
h.session.Destroy(ctx)
|
||||
h.render.HTML(w, r, "oauth/login.tmpl", nil)
|
||||
return
|
||||
} else if r.Method == http.MethodPost {
|
||||
req := &req.Login{
|
||||
Email: strings.TrimSpace(r.PostFormValue("email")),
|
||||
Password: strings.TrimSpace(r.PostFormValue("password")),
|
||||
CaptchaID: strings.TrimSpace(r.PostFormValue("captcha_id")),
|
||||
Captcha: strings.TrimSpace(r.PostFormValue("captcha")),
|
||||
Ip: r.RemoteAddr,
|
||||
Referrer: r.Header.Get("Referer"),
|
||||
Url: r.URL.RequestURI(),
|
||||
}
|
||||
|
||||
if len(req.Email) == 0 {
|
||||
h.render.JSON(w, tpl.Response{Success: false, Message: "请填写邮箱"})
|
||||
return
|
||||
}
|
||||
if len(req.Password) == 0 {
|
||||
h.render.JSON(w, tpl.Response{Success: false, Message: "请填写密码"})
|
||||
return
|
||||
}
|
||||
if len(req.Captcha) == 0 {
|
||||
h.render.JSON(w, tpl.Response{Success: false, Message: "请填写验证码"})
|
||||
return
|
||||
}
|
||||
if !h.biz.CommonV1().CaptchaBiz().Verify(req.CaptchaID, req.Captcha, true) {
|
||||
h.render.JSON(w, tpl.Response{Success: false, Message: "验证码错误"})
|
||||
return
|
||||
}
|
||||
|
||||
br, err := browser.NewBrowser(r.Header.Get("User-Agent"))
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, "平台信息获取错误")
|
||||
return
|
||||
}
|
||||
|
||||
req.Os = br.Platform().Name()
|
||||
req.Browser = br.Name()
|
||||
err = h.biz.SystemV1().UserBiz().Login(ctx, req)
|
||||
if err != nil {
|
||||
h.render.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.render.JSONOK(w, "login successful")
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func (h *userHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
h.session.Destroy(r.Context())
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
147
internal/erpserver/http.go
Normal file
147
internal/erpserver/http.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package erpserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"management/internal/erpserver/handler"
|
||||
mw "management/internal/pkg/middleware"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
)
|
||||
|
||||
func NewRouter(handler handler.IHandler, mw mw.IMiddleware) *chi.Mux {
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
// r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
staticServer := http.FileServer(http.Dir("./web/statics/"))
|
||||
r.Handle("/statics/*", http.StripPrefix("/statics", staticServer))
|
||||
|
||||
uploadServer := http.FileServer(http.Dir("./upload/"))
|
||||
r.Handle("/upload/*", http.StripPrefix("/upload", uploadServer))
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(mw.NoSurf) // CSRF
|
||||
r.Use(mw.LoadSession) // Session
|
||||
|
||||
r.Get("/captcha", handler.CommonHandler().CaptchaHandler().Captcha)
|
||||
|
||||
r.Get("/", handler.SystemHandler().UserHandler().Login)
|
||||
r.Post("/login", handler.SystemHandler().UserHandler().Login)
|
||||
r.Get("/logout", handler.SystemHandler().UserHandler().Logout)
|
||||
|
||||
// r.With(auth.Authorize, mw.Audit).Post("/upload/img", commonhandler.UploadImg)
|
||||
// r.With(auth.Authorize, mw.Audit).Post("/upload/file", commonhandler.UploadFile)
|
||||
// r.With(auth.Authorize, mw.Audit).Post("/upload/mutilfile", commonhandler.UploadMutilFiles)
|
||||
|
||||
r.With(mw.Authorize, mw.Audit).Get("/home.html", handler.Home)
|
||||
r.With(mw.Authorize).Get("/pear.json", handler.SystemHandler().ConfigHandler().Pear)
|
||||
|
||||
r.Route("/system", func(r chi.Router) {
|
||||
r.Use(mw.Authorize)
|
||||
|
||||
// r.Route("/config", func(r chi.Router) {
|
||||
// r.Use(mw.Audit)
|
||||
// r.Get("/list", configHandler.List)
|
||||
// r.Post("/list", configHandler.PostList)
|
||||
// r.Get("/add", configHandler.Add)
|
||||
// r.Get("/edit", configHandler.Edit)
|
||||
// r.Post("/save", configHandler.Save)
|
||||
// r.Post("/reset_pear", configHandler.ResetPear)
|
||||
// r.Post("/refresh", configHandler.Refresh)
|
||||
// })
|
||||
|
||||
r.Route("/department", func(r chi.Router) {
|
||||
r.Use(mw.Audit)
|
||||
r.Get("/list", handler.SystemHandler().DepartmentHandler().List)
|
||||
r.Post("/list", handler.SystemHandler().DepartmentHandler().List)
|
||||
r.Get("/add", handler.SystemHandler().DepartmentHandler().Add)
|
||||
r.Get("/add_children", handler.SystemHandler().DepartmentHandler().AddChildren)
|
||||
r.Get("/edit", handler.SystemHandler().DepartmentHandler().Edit)
|
||||
r.Post("/save", handler.SystemHandler().DepartmentHandler().Save)
|
||||
r.Post("/tree", handler.SystemHandler().DepartmentHandler().Tree)
|
||||
r.Post("/refresh", handler.SystemHandler().DepartmentHandler().Refresh)
|
||||
r.Post("/rebuild_parent_path", handler.SystemHandler().DepartmentHandler().RebuildParentPath)
|
||||
})
|
||||
|
||||
// r.Route("/user", func(r chi.Router) {
|
||||
// r.Use(mw.Audit)
|
||||
// userHandler := systemhandler.NewSysUserHandler()
|
||||
// r.Get("/list", userHandler.List)
|
||||
// r.Post("/list", userHandler.PostList)
|
||||
// r.Get("/add", userHandler.Add)
|
||||
// r.Get("/edit", userHandler.Edit)
|
||||
// r.Post("/save", userHandler.Save)
|
||||
// r.Get("/profile", userHandler.Profile)
|
||||
// r.Post("/xmselect", userHandler.XmSelect)
|
||||
// })
|
||||
|
||||
// r.Route("/login_log", func(r chi.Router) {
|
||||
// // r.Use(mw.Audit)
|
||||
// userLoginLogHandler := systemhandler.NewSysUserLoginLogHandler()
|
||||
// r.Get("/list", userLoginLogHandler.List)
|
||||
// r.Post("/list", userLoginLogHandler.PostList)
|
||||
// })
|
||||
|
||||
// r.Route("/audit_log", func(r chi.Router) {
|
||||
// // r.Use(mw.Audit)
|
||||
// userAuditLogHandler := systemhandler.NewSysAuditLogHandler()
|
||||
// r.Get("/list", userAuditLogHandler.List)
|
||||
// r.Post("/list", userAuditLogHandler.PostList)
|
||||
// })
|
||||
|
||||
// r.Route("/role", func(r chi.Router) {
|
||||
// r.Use(mw.Audit)
|
||||
// roleHandler := systemhandler.NewSysRoleHandler()
|
||||
// r.Get("/list", roleHandler.List)
|
||||
// r.Post("/list", roleHandler.PostList)
|
||||
// r.Get("/add", roleHandler.Add)
|
||||
// r.Get("/edit", roleHandler.Edit)
|
||||
// r.Post("/save", roleHandler.Save)
|
||||
// r.Post("/dtree", roleHandler.DTree)
|
||||
// r.Post("/refresh", roleHandler.Refresh)
|
||||
// r.Post("/rebuild_parent_path", roleHandler.RebuildParentPath)
|
||||
// r.Post("/refresh_role_menus", roleHandler.RefreshRoleMenus)
|
||||
// r.Post("/xm_select", roleHandler.XmSelect)
|
||||
// r.Get("/set_menu", roleHandler.SetMenu)
|
||||
// r.Post("/set_menu", roleHandler.PostSetMenu)
|
||||
// })
|
||||
|
||||
r.Get("/menus", handler.SystemHandler().MenuHandler().Menus)
|
||||
// r.Route("/menu", func(r chi.Router) {
|
||||
// r.Use(mw.Audit)
|
||||
// r.Get("/tree", menuHandler.Tree)
|
||||
// r.Get("/list", menuHandler.List)
|
||||
// r.Post("/list", menuHandler.PostList)
|
||||
// r.Get("/add", menuHandler.Add)
|
||||
// r.Get("/add_children", menuHandler.AddChildren)
|
||||
// r.Get("/edit", menuHandler.Edit)
|
||||
// r.Post("/save", menuHandler.Save)
|
||||
// r.Post("/xm_select_tree", menuHandler.XmSelectTree)
|
||||
// r.Post("/refresh_cache", menuHandler.Refresh)
|
||||
// })
|
||||
|
||||
// // 类别
|
||||
// r.Route("/category", func(r chi.Router) {
|
||||
// r.Use(mw.Audit)
|
||||
// categoryHandler := categoryhandler.NewCategoryHandler()
|
||||
// r.Get("/list", categoryHandler.List)
|
||||
// r.Post("/list", categoryHandler.PostList)
|
||||
// r.Get("/add", categoryHandler.Add)
|
||||
// r.Get("/add_children", categoryHandler.AddChildren)
|
||||
// r.Get("/edit", categoryHandler.Edit)
|
||||
// r.Post("/save", categoryHandler.Save)
|
||||
// r.Post("/dtree", categoryHandler.DTree)
|
||||
// r.Post("/xmselect", categoryHandler.XmSelect)
|
||||
// r.Post("/refresh", categoryHandler.Refresh)
|
||||
// r.Post("/rebuild_parent_path", categoryHandler.RebuildParentPath)
|
||||
// })
|
||||
})
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
15
internal/erpserver/model/req/user.go
Normal file
15
internal/erpserver/model/req/user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package req
|
||||
|
||||
type Login struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
|
||||
// 平台信息
|
||||
Os string `json:"os"`
|
||||
Ip string `json:"ip"`
|
||||
Browser string `json:"browser"`
|
||||
Referrer string `json:"referrer"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
14
internal/erpserver/model/view/system.go
Normal file
14
internal/erpserver/model/view/system.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package view
|
||||
|
||||
type LayuiTree struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Spread bool `json:"spread"`
|
||||
Children []*LayuiTree `json:"children"`
|
||||
}
|
||||
|
||||
type XmSelectTree struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Children []*XmSelectTree `json:"children"`
|
||||
}
|
||||
@@ -10,4 +10,36 @@ const (
|
||||
IncomeCategory = "income_category"
|
||||
|
||||
ExpenseCategory = "expense_category"
|
||||
|
||||
CookieName = "authorize"
|
||||
StoreName = "authorize_user"
|
||||
)
|
||||
|
||||
var (
|
||||
// pear admin 配置
|
||||
PearAdmin = "m:pearjson"
|
||||
|
||||
// 所有类别
|
||||
AllCategories = "m:category:all"
|
||||
// 所有类别 简单信息
|
||||
AllCategorySimple = "m:categorysimple:all"
|
||||
// 类别列表 根据 父id 获取
|
||||
ListCategoriesByParentID = "m:category:parent_id:%d"
|
||||
|
||||
// 所有部门
|
||||
AllDepartments = "m:department:all"
|
||||
|
||||
// 所有菜单
|
||||
AllMenus = "m:menus:all"
|
||||
// 递归菜单
|
||||
RecursiveMenus = "m:rec_menus:%d"
|
||||
// 根据用户ID获取菜单
|
||||
AdminMenus = "m:admin_menus:%d"
|
||||
// 登陆用户的菜单
|
||||
OwnerMenus = "m:owner_menus:%d"
|
||||
// 登陆用户的菜单
|
||||
OwnerMenusMap = "m:owner_menus_map:%d"
|
||||
|
||||
// 所有角色
|
||||
AllRoles = "m:role:all"
|
||||
)
|
||||
|
||||
@@ -2,12 +2,9 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/global/auth"
|
||||
"management/internal/pkg/session"
|
||||
systemservice "management/internal/service/system"
|
||||
)
|
||||
|
||||
@@ -61,23 +58,23 @@ func Authorize(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) {
|
||||
if exists := session.Exists(ctx, auth.StoreName); exists {
|
||||
b := session.GetBytes(ctx, auth.StoreName)
|
||||
var user dto.AuthorizeUser
|
||||
if err := json.Unmarshal(b, &user); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return &user, true
|
||||
}
|
||||
// if exists := session.Exists(ctx, auth.StoreName); exists {
|
||||
// b := session.GetBytes(ctx, auth.StoreName)
|
||||
// var user dto.AuthorizeUser
|
||||
// if err := json.Unmarshal(b, &user); err != nil {
|
||||
// return nil, false
|
||||
// }
|
||||
// return &user, true
|
||||
// }
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func AuthUser(ctx context.Context) dto.AuthorizeUser {
|
||||
var user dto.AuthorizeUser
|
||||
if exists := session.Exists(ctx, auth.StoreName); exists {
|
||||
b := session.GetBytes(ctx, auth.StoreName)
|
||||
_ = json.Unmarshal(b, &user)
|
||||
}
|
||||
// if exists := session.Exists(ctx, auth.StoreName); exists {
|
||||
// b := session.GetBytes(ctx, auth.StoreName)
|
||||
// _ = json.Unmarshal(b, &user)
|
||||
// }
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ import (
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
func LoadSession(next http.Handler) http.Handler {
|
||||
func LoadSession(session session.ISession, next http.Handler) http.Handler {
|
||||
return session.LoadAndSave(next)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package convertor
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ConvertInt[T int | int16 | int32 | int64](value string, defaultValue T) T {
|
||||
i, err := strconv.Atoi(value)
|
||||
@@ -9,3 +12,17 @@ func ConvertInt[T int | int16 | int32 | int64](value string, defaultValue T) T {
|
||||
}
|
||||
return T(i)
|
||||
}
|
||||
|
||||
func QueryInt[T int | int16 | int32 | int64](vars url.Values, key string, defaultValue T) T {
|
||||
v := vars.Get(key)
|
||||
if len(v) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return T(i)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,27 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func New(prod bool) {
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
logRotate := &lumberjack.Logger{
|
||||
Filename: "./log/run.log", // 日志文件的位置
|
||||
MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位)
|
||||
MaxBackups: 100, // 保留旧文件的最大个数
|
||||
MaxAge: 30, // 保留旧文件的最大天数
|
||||
Compress: true,
|
||||
}
|
||||
zerolog.TimeFieldFormat = time.DateTime
|
||||
log.Logger = log.With().Caller().Logger()
|
||||
|
||||
if prod {
|
||||
log.Logger = log.Output(logRotate)
|
||||
} else {
|
||||
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.DateTime}
|
||||
multi := zerolog.MultiLevelWriter(consoleWriter, logRotate)
|
||||
log.Logger = log.Output(multi)
|
||||
}
|
||||
}
|
||||
|
||||
func Init() {
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
logRotate := &lumberjack.Logger{
|
||||
|
||||
73
internal/pkg/middleware/audit.go
Normal file
73
internal/pkg/middleware/audit.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
db "management/internal/db/sqlc"
|
||||
|
||||
"github.com/zhang2092/browser"
|
||||
)
|
||||
|
||||
func (m *middleware) Audit(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
defer func(res http.ResponseWriter, req *http.Request) {
|
||||
// 记录审计日志
|
||||
go m.writeLog(req, start)
|
||||
}(w, r)
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
func (m *middleware) writeLog(req *http.Request, start time.Time) {
|
||||
end := time.Now()
|
||||
duration := end.Sub(start)
|
||||
var params string
|
||||
method := req.Method
|
||||
if method == "GET" {
|
||||
params = req.URL.Query().Encode()
|
||||
} else if method == "POST" {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
if strings.Contains(contentType, "application/json") {
|
||||
body := make([]byte, req.ContentLength)
|
||||
req.Body.Read(body)
|
||||
params = string(body)
|
||||
} else if strings.Contains(contentType, "application/x-www-form-urlencoded") {
|
||||
params = req.Form.Encode()
|
||||
}
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
au := m.AuthUser(ctx)
|
||||
arg := &db.CreateSysAuditLogParams{
|
||||
CreatedAt: time.Now(),
|
||||
Email: au.Email,
|
||||
Username: au.Username,
|
||||
UserUuid: au.Uuid,
|
||||
StartAt: start,
|
||||
EndAt: end,
|
||||
Duration: strconv.FormatInt(duration.Milliseconds(), 10),
|
||||
Url: req.URL.RequestURI(),
|
||||
Method: method,
|
||||
Parameters: params,
|
||||
RefererUrl: req.Header.Get("Referer"),
|
||||
Ip: req.RemoteAddr,
|
||||
Remark: "",
|
||||
}
|
||||
br, err := browser.NewBrowser(req.Header.Get("User-Agent"))
|
||||
if err == nil {
|
||||
arg.Os = br.Platform().Name()
|
||||
arg.Browser = br.Name()
|
||||
}
|
||||
|
||||
c, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||
defer cancel()
|
||||
|
||||
_ = m.biz.AuditBiz().Create(c, arg)
|
||||
}
|
||||
81
internal/pkg/middleware/authorize.go
Normal file
81
internal/pkg/middleware/authorize.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/global/auth"
|
||||
)
|
||||
|
||||
var defaultMenus = map[string]bool{
|
||||
"/home.html": true,
|
||||
"/system/menus": true,
|
||||
"/upload/img": true,
|
||||
"/upload/file": true,
|
||||
"/upload/mutilfile": true,
|
||||
"/pear.json": true,
|
||||
}
|
||||
|
||||
func (m *middleware) Authorize(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user, ok := m.isLogin(ctx)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
http.Error(w, "user not found", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 登陆成功 判断权限
|
||||
|
||||
// 默认权限判断
|
||||
path := r.URL.Path
|
||||
if b, ok := defaultMenus[path]; ok && b {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
menus, err := m.biz.MenuBiz().MapOwnerMenuByRoleID(ctx, user.RoleID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := menus[path]; ok {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
func (m *middleware) isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) {
|
||||
if exists := m.session.Exists(ctx, auth.StoreName); exists {
|
||||
b := m.session.GetBytes(ctx, auth.StoreName)
|
||||
var user dto.AuthorizeUser
|
||||
if err := json.Unmarshal(b, &user); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return &user, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *middleware) AuthUser(ctx context.Context) dto.AuthorizeUser {
|
||||
var user dto.AuthorizeUser
|
||||
if exists := m.session.Exists(ctx, auth.StoreName); exists {
|
||||
b := m.session.GetBytes(ctx, auth.StoreName)
|
||||
_ = json.Unmarshal(b, &user)
|
||||
}
|
||||
return user
|
||||
}
|
||||
29
internal/pkg/middleware/middleware.go
Normal file
29
internal/pkg/middleware/middleware.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
systemv1 "management/internal/erpserver/biz/v1/system"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type IMiddleware interface {
|
||||
Audit(next http.Handler) http.Handler
|
||||
NoSurf(next http.Handler) http.Handler
|
||||
LoadSession(next http.Handler) http.Handler
|
||||
Authorize(next http.Handler) http.Handler
|
||||
}
|
||||
|
||||
type middleware struct {
|
||||
biz systemv1.SystemBiz
|
||||
session session.ISession
|
||||
}
|
||||
|
||||
var _ IMiddleware = (*middleware)(nil)
|
||||
|
||||
func New(biz systemv1.SystemBiz, session session.ISession) IMiddleware {
|
||||
return &middleware{
|
||||
biz: biz,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
11
internal/pkg/middleware/nocsrf.go
Normal file
11
internal/pkg/middleware/nocsrf.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
func (m *middleware) NoSurf(next http.Handler) http.Handler {
|
||||
return nosurf.New(next)
|
||||
}
|
||||
9
internal/pkg/middleware/session.go
Normal file
9
internal/pkg/middleware/session.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (m *middleware) LoadSession(next http.Handler) http.Handler {
|
||||
return m.session.LoadAndSave(next)
|
||||
}
|
||||
@@ -13,31 +13,42 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var (
|
||||
engine *redis.Client
|
||||
ErrRedisKeyNotFound = errors.New("redis key not found")
|
||||
)
|
||||
var ErrRedisKeyNotFound = errors.New("redis key not found")
|
||||
|
||||
// func GetRedis() *redis.Client {
|
||||
// return rd
|
||||
// }
|
||||
type IRedis interface {
|
||||
Encode(a any) ([]byte, error)
|
||||
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error
|
||||
Del(ctx context.Context, keys ...string) error
|
||||
Get(ctx context.Context, key string) (string, error)
|
||||
GetBytes(ctx context.Context, key string) ([]byte, error)
|
||||
Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd
|
||||
Keys(ctx context.Context, pattern string) ([]string, error)
|
||||
ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error)
|
||||
}
|
||||
|
||||
func Init() error {
|
||||
type redisCache struct {
|
||||
engine *redis.Client
|
||||
}
|
||||
|
||||
var _ IRedis = (*redisCache)(nil)
|
||||
|
||||
func New(conf config.Redis) (*redisCache, error) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", config.File.Redis.Host, config.File.Redis.Port),
|
||||
Password: config.File.Redis.Password,
|
||||
DB: config.File.Redis.DB,
|
||||
Addr: fmt.Sprintf("%s:%d", conf.Host, conf.Port),
|
||||
Password: conf.Password,
|
||||
DB: conf.DB,
|
||||
})
|
||||
_, err := rdb.Ping(context.Background()).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
engine = rdb
|
||||
return nil
|
||||
return &redisCache{
|
||||
engine: rdb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Encode(a any) ([]byte, error) {
|
||||
func (r *redisCache) Encode(a any) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
if err := gob.NewEncoder(&b).Encode(a); err != nil {
|
||||
return nil, err
|
||||
@@ -47,18 +58,18 @@ func Encode(a any) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Set 设置值
|
||||
func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
return engine.Set(ctx, key, value, expiration).Err()
|
||||
func (r *redisCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
return r.engine.Set(ctx, key, value, expiration).Err()
|
||||
}
|
||||
|
||||
// Del 删除键值
|
||||
func Del(ctx context.Context, keys ...string) error {
|
||||
return engine.Del(ctx, keys...).Err()
|
||||
func (r *redisCache) Del(ctx context.Context, keys ...string) error {
|
||||
return r.engine.Del(ctx, keys...).Err()
|
||||
}
|
||||
|
||||
// Get 获取值
|
||||
func Get(ctx context.Context, key string) (string, error) {
|
||||
val, err := engine.Get(ctx, key).Result()
|
||||
func (r *redisCache) Get(ctx context.Context, key string) (string, error) {
|
||||
val, err := r.engine.Get(ctx, key).Result()
|
||||
if err == redis.Nil {
|
||||
return "", ErrRedisKeyNotFound
|
||||
} else if err != nil {
|
||||
@@ -69,8 +80,8 @@ func Get(ctx context.Context, key string) (string, error) {
|
||||
}
|
||||
|
||||
// GetBytes 获取值
|
||||
func GetBytes(ctx context.Context, key string) ([]byte, error) {
|
||||
val, err := engine.Get(ctx, key).Bytes()
|
||||
func (r *redisCache) GetBytes(ctx context.Context, key string) ([]byte, error) {
|
||||
val, err := r.engine.Get(ctx, key).Bytes()
|
||||
if err == redis.Nil {
|
||||
return nil, ErrRedisKeyNotFound
|
||||
} else if err != nil {
|
||||
@@ -80,16 +91,16 @@ func GetBytes(ctx context.Context, key string) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd {
|
||||
return engine.Scan(ctx, cursor, match, count)
|
||||
func (r *redisCache) Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd {
|
||||
return r.engine.Scan(ctx, cursor, match, count)
|
||||
}
|
||||
|
||||
func Keys(ctx context.Context, pattern string) ([]string, error) {
|
||||
return engine.Keys(ctx, pattern).Result()
|
||||
func (r *redisCache) Keys(ctx context.Context, pattern string) ([]string, error) {
|
||||
return r.engine.Keys(ctx, pattern).Result()
|
||||
}
|
||||
|
||||
func ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error) {
|
||||
all, err := engine.Keys(ctx, pattern).Result()
|
||||
func (r *redisCache) ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error) {
|
||||
all, err := r.engine.Keys(ctx, pattern).Result()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -104,7 +115,7 @@ func ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]
|
||||
for {
|
||||
var scanResult []string
|
||||
var err error
|
||||
scanResult, cursor, err = engine.Scan(ctx, cursor, pattern, int64(pageSize)).Result()
|
||||
scanResult, cursor, err = r.engine.Scan(ctx, cursor, pattern, int64(pageSize)).Result()
|
||||
if err != nil {
|
||||
return nil, count, err
|
||||
}
|
||||
@@ -124,3 +135,36 @@ func ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]
|
||||
}
|
||||
return keys[startIndex:endIndex], count, nil
|
||||
}
|
||||
|
||||
// ==========================
|
||||
func Encode(a any) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Del(ctx context.Context, keys ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Get(ctx context.Context, key string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func GetBytes(ctx context.Context, key string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Keys(ctx context.Context, pattern string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
// import (
|
||||
// "context"
|
||||
// "time"
|
||||
|
||||
"management/internal/pkg/redis"
|
||||
)
|
||||
// "management/internal/pkg/redis"
|
||||
// )
|
||||
|
||||
var (
|
||||
storePrefix = "scs:session:"
|
||||
ctx = context.Background()
|
||||
DefaultRedisStore = newRedisStore()
|
||||
)
|
||||
// var (
|
||||
// storePrefix = "scs:session:"
|
||||
// ctx = context.Background()
|
||||
// DefaultRedisStore = newRedisStore()
|
||||
// )
|
||||
|
||||
type redisStore struct{}
|
||||
// type redisStore struct{}
|
||||
|
||||
func newRedisStore() *redisStore {
|
||||
return &redisStore{}
|
||||
}
|
||||
// func newRedisStore() *redisStore {
|
||||
// return &redisStore{}
|
||||
// }
|
||||
|
||||
// Delete should remove the session token and corresponding data from the
|
||||
// session store. If the token does not exist then Delete should be a no-op
|
||||
// and return nil (not an error).
|
||||
func (s *redisStore) Delete(token string) error {
|
||||
return redis.Del(ctx, storePrefix+token)
|
||||
}
|
||||
// // Delete should remove the session token and corresponding data from the
|
||||
// // session store. If the token does not exist then Delete should be a no-op
|
||||
// // and return nil (not an error).
|
||||
// func (s *redisStore) Delete(token string) error {
|
||||
// return redis.Del(ctx, storePrefix+token)
|
||||
// }
|
||||
|
||||
// Find should return the data for a session token from the store. If the
|
||||
// session token is not found or is expired, the found return value should
|
||||
// be false (and the err return value should be nil). Similarly, tampered
|
||||
// or malformed tokens should result in a found return value of false and a
|
||||
// nil err value. The err return value should be used for system errors only.
|
||||
func (s *redisStore) Find(token string) (b []byte, found bool, err error) {
|
||||
val, err := redis.GetBytes(ctx, storePrefix+token)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
} else {
|
||||
return val, true, nil
|
||||
}
|
||||
}
|
||||
// // Find should return the data for a session token from the store. If the
|
||||
// // session token is not found or is expired, the found return value should
|
||||
// // be false (and the err return value should be nil). Similarly, tampered
|
||||
// // or malformed tokens should result in a found return value of false and a
|
||||
// // nil err value. The err return value should be used for system errors only.
|
||||
// func (s *redisStore) Find(token string) (b []byte, found bool, err error) {
|
||||
// val, err := redis.GetBytes(ctx, storePrefix+token)
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// } else {
|
||||
// return val, true, nil
|
||||
// }
|
||||
// }
|
||||
|
||||
// Commit should add the session token and data to the store, with the given
|
||||
// expiry time. If the session token already exists, then the data and
|
||||
// expiry time should be overwritten.
|
||||
func (s *redisStore) Commit(token string, b []byte, expiry time.Time) error {
|
||||
// TODO: 这边可以调整时间
|
||||
exp, err := time.ParseInLocation(time.DateTime, time.Now().Format("2006-01-02")+" 23:59:59", time.Local)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Commit should add the session token and data to the store, with the given
|
||||
// // expiry time. If the session token already exists, then the data and
|
||||
// // expiry time should be overwritten.
|
||||
// func (s *redisStore) Commit(token string, b []byte, expiry time.Time) error {
|
||||
// // TODO: 这边可以调整时间
|
||||
// exp, err := time.ParseInLocation(time.DateTime, time.Now().Format("2006-01-02")+" 23:59:59", time.Local)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
t := time.Now()
|
||||
expired := exp.Sub(t)
|
||||
return redis.Set(ctx, storePrefix+token, b, expired)
|
||||
}
|
||||
// t := time.Now()
|
||||
// expired := exp.Sub(t)
|
||||
// return redis.Set(ctx, storePrefix+token, b, expired)
|
||||
// }
|
||||
|
||||
// All should return a map containing data for all active sessions (i.e.
|
||||
// sessions which have not expired). The map key should be the session
|
||||
// token and the map value should be the session data. If no active
|
||||
// sessions exist this should return an empty (not nil) map.
|
||||
func (s *redisStore) All() (map[string][]byte, error) {
|
||||
sessions := make(map[string][]byte)
|
||||
// // All should return a map containing data for all active sessions (i.e.
|
||||
// // sessions which have not expired). The map key should be the session
|
||||
// // token and the map value should be the session data. If no active
|
||||
// // sessions exist this should return an empty (not nil) map.
|
||||
// func (s *redisStore) All() (map[string][]byte, error) {
|
||||
// sessions := make(map[string][]byte)
|
||||
|
||||
iter := redis.Scan(ctx, 0, storePrefix+"*", 0).Iterator()
|
||||
for iter.Next(ctx) {
|
||||
key := iter.Val()
|
||||
token := key[len(storePrefix):]
|
||||
data, exists, err := s.Find(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// iter := redis.Scan(ctx, 0, storePrefix+"*", 0).Iterator()
|
||||
// for iter.Next(ctx) {
|
||||
// key := iter.Val()
|
||||
// token := key[len(storePrefix):]
|
||||
// data, exists, err := s.Find(token)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
if exists {
|
||||
sessions[token] = data
|
||||
}
|
||||
}
|
||||
if err := iter.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if exists {
|
||||
// sessions[token] = data
|
||||
// }
|
||||
// }
|
||||
// if err := iter.Err(); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
// return sessions, nil
|
||||
// }
|
||||
|
||||
@@ -5,55 +5,68 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"management/internal/config"
|
||||
db "management/internal/db/sqlc"
|
||||
|
||||
"github.com/alexedwards/scs/pgxstore"
|
||||
"github.com/alexedwards/scs/v2"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
var sessionManager *scs.SessionManager
|
||||
type ISession interface {
|
||||
Destroy(ctx context.Context) error
|
||||
LoadAndSave(next http.Handler) http.Handler
|
||||
Put(ctx context.Context, key string, val any)
|
||||
GetBytes(ctx context.Context, key string) []byte
|
||||
Exists(ctx context.Context, key string) bool
|
||||
RenewToken(ctx context.Context) error
|
||||
}
|
||||
|
||||
func Init() {
|
||||
sessionManager = scs.New()
|
||||
type session struct {
|
||||
sessionManager *scs.SessionManager
|
||||
}
|
||||
|
||||
func New(pool *pgxpool.Pool, prod bool) ISession {
|
||||
sessionManager := scs.New()
|
||||
sessionManager.Lifetime = 24 * time.Hour
|
||||
sessionManager.IdleTimeout = 2 * time.Hour
|
||||
sessionManager.Cookie.Name = "token"
|
||||
sessionManager.Cookie.HttpOnly = true
|
||||
sessionManager.Cookie.Persist = true
|
||||
sessionManager.Cookie.SameSite = http.SameSiteStrictMode
|
||||
sessionManager.Cookie.Secure = config.File.App.Prod
|
||||
sessionManager.Cookie.Secure = prod
|
||||
|
||||
// postgres
|
||||
// github.com/alexedwards/scs/postgresstore
|
||||
// sessionManager.Store = postgresstore.New(db)
|
||||
// pgx
|
||||
// github.com/alexedwards/scs/pgxstore
|
||||
sessionManager.Store = pgxstore.New(db.Engine.Pool())
|
||||
sessionManager.Store = pgxstore.New(pool)
|
||||
// redis
|
||||
// sessionManager.Store = newRedisStore()
|
||||
|
||||
return &session{
|
||||
sessionManager: sessionManager,
|
||||
}
|
||||
}
|
||||
|
||||
func Destroy(ctx context.Context) error {
|
||||
return sessionManager.Destroy(ctx)
|
||||
func (s *session) Destroy(ctx context.Context) error {
|
||||
return s.sessionManager.Destroy(ctx)
|
||||
}
|
||||
|
||||
func LoadAndSave(next http.Handler) http.Handler {
|
||||
return sessionManager.LoadAndSave(next)
|
||||
func (s *session) LoadAndSave(next http.Handler) http.Handler {
|
||||
return s.sessionManager.LoadAndSave(next)
|
||||
}
|
||||
|
||||
func Put(ctx context.Context, key string, val interface{}) {
|
||||
sessionManager.Put(ctx, key, val)
|
||||
func (s *session) Put(ctx context.Context, key string, val any) {
|
||||
s.sessionManager.Put(ctx, key, val)
|
||||
}
|
||||
|
||||
func GetBytes(ctx context.Context, key string) []byte {
|
||||
return sessionManager.GetBytes(ctx, key)
|
||||
func (s *session) GetBytes(ctx context.Context, key string) []byte {
|
||||
return s.sessionManager.GetBytes(ctx, key)
|
||||
}
|
||||
|
||||
func Exists(ctx context.Context, key string) bool {
|
||||
return sessionManager.Exists(ctx, key)
|
||||
func (s *session) Exists(ctx context.Context, key string) bool {
|
||||
return s.sessionManager.Exists(ctx, key)
|
||||
}
|
||||
|
||||
func RenewToken(ctx context.Context) error {
|
||||
return sessionManager.RenewToken(ctx)
|
||||
func (s *session) RenewToken(ctx context.Context) error {
|
||||
return s.sessionManager.RenewToken(ctx)
|
||||
}
|
||||
|
||||
48
internal/pkg/tpl/html.go
Normal file
48
internal/pkg/tpl/html.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
)
|
||||
|
||||
type TemplateConfig struct {
|
||||
Root string
|
||||
Extension string
|
||||
Layout string
|
||||
Partial string
|
||||
}
|
||||
|
||||
type HtmlData struct {
|
||||
IsAuthenticated bool
|
||||
AuthorizeUser dto.AuthorizeUser
|
||||
AuthorizeMenus []*dto.OwnerMenuDto
|
||||
Data any
|
||||
}
|
||||
|
||||
func (r *render) HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) {
|
||||
name := strings.ReplaceAll(tpl, "/", "_")
|
||||
t, ok := r.templates[name]
|
||||
if !ok {
|
||||
http.Error(w, "template is empty", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
hd := r.setDefaultData(req, data)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := t.ExecuteTemplate(buf, filepath.Base(tpl), hd)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = buf.WriteTo(w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
91
internal/pkg/tpl/html_btn.go
Normal file
91
internal/pkg/tpl/html_btn.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
)
|
||||
|
||||
func (r *render) btnFuncs() map[string]any {
|
||||
res := make(map[string]any, 3)
|
||||
|
||||
res["genBtn"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(btns) == 0 {
|
||||
return template.HTML("")
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range btns {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="button" class="layui-btn ` + btn.Style + `" lay-event="` + firstLower(action) + `" lay-on="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["genLink"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(btns) == 0 {
|
||||
return template.HTML("")
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range btns {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="button" style="font-size:12px !important;" class="layui-btn ` + btn.Style + `" lay-event="` + firstLower(action) + `" lay-on="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["previewPicture"] = func(name string) template.HTML {
|
||||
var res string
|
||||
res += `<a><img src="https://school-1251542740.cos.ap-shanghai.myqcloud.com{{` + name + `}}" data-type="1" height="30" width="30" class="preview-all screenshot" onclick="previewPicture('https://school-1251542740.cos.ap-shanghai.myqcloud.com/{{` + name + `}}')"/></a>`
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["submitBtn"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(btns) == 0 {
|
||||
return template.HTML("")
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range btns {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="submit" class="layui-btn ` + btn.Style + `" lay-submit lay-filter="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
57
internal/pkg/tpl/html_method.go
Normal file
57
internal/pkg/tpl/html_method.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (r *render) Methods() map[string]any {
|
||||
res := make(map[string]any, 1)
|
||||
|
||||
res["dateFormat"] = func(dt time.Time) template.HTML {
|
||||
return template.HTML(dt.Format(time.DateTime))
|
||||
}
|
||||
|
||||
res["today"] = func() template.HTML {
|
||||
return template.HTML(time.Now().Format("2006-01-02"))
|
||||
}
|
||||
|
||||
res["threeMonth"] = func() template.HTML {
|
||||
return template.HTML(time.Now().AddDate(0, 3, 0).Format("2006-01-02"))
|
||||
}
|
||||
|
||||
res["yearBegin"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, -int(dt.Month())+1, -dt.Day()+1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 00:00:00")
|
||||
}
|
||||
|
||||
res["monthBegin"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, 0, -dt.Day()+1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 00:00:00")
|
||||
}
|
||||
|
||||
res["monthEnd"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, 0, -dt.Day()+1).AddDate(0, 1, -1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 23:59:59")
|
||||
}
|
||||
|
||||
res["trimSpace"] = func(s string) template.HTML {
|
||||
return template.HTML(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
res["expandTags"] = func(s []string) template.HTML {
|
||||
if len(s) == 0 {
|
||||
return template.HTML("")
|
||||
}
|
||||
if len(s) == 1 && s[0] == "all" {
|
||||
return template.HTML("")
|
||||
}
|
||||
return template.HTML(strings.Join(s, ","))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
57
internal/pkg/tpl/json.go
Normal file
57
internal/pkg/tpl/json.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"msg"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type ResponseDtree struct {
|
||||
Status ResponseDtreeStatus `json:"status"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type ResponseDtreeStatus struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ResponseList struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
Count int64 `json:"count"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func (r *render) JSONF(w http.ResponseWriter, success bool, message string) {
|
||||
r.JSON(w, Response{Success: success, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONOK(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: true, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONERR(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: false, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSON(w http.ResponseWriter, data any) {
|
||||
v, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = w.Write(v)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
46
internal/pkg/tpl/render.go
Normal file
46
internal/pkg/tpl/render.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
systemv1 "management/internal/erpserver/biz/v1/system"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type Renderer interface {
|
||||
HTML(w http.ResponseWriter, req *http.Request, name string, data map[string]any)
|
||||
JSON(w http.ResponseWriter, data any)
|
||||
JSONF(w http.ResponseWriter, success bool, message string)
|
||||
JSONOK(w http.ResponseWriter, message string)
|
||||
JSONERR(w http.ResponseWriter, message string)
|
||||
}
|
||||
|
||||
type render struct {
|
||||
session session.ISession
|
||||
config *TemplateConfig
|
||||
templates map[string]*template.Template
|
||||
|
||||
menuBiz systemv1.MenuBiz
|
||||
}
|
||||
|
||||
func New(session session.ISession, menuBiz systemv1.MenuBiz) (Renderer, error) {
|
||||
render := &render{
|
||||
session: session,
|
||||
menuBiz: menuBiz,
|
||||
config: &TemplateConfig{
|
||||
Root: ".",
|
||||
Extension: ".tmpl",
|
||||
Layout: "base",
|
||||
Partial: "partial",
|
||||
},
|
||||
}
|
||||
|
||||
templates, err := render.createTemplateCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
render.templates = templates
|
||||
return render, nil
|
||||
}
|
||||
181
internal/pkg/tpl/util.go
Normal file
181
internal/pkg/tpl/util.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/global/auth"
|
||||
templates "management/web/templates/manage"
|
||||
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
func (r *render) setDefaultData(req *http.Request, data map[string]any) map[string]any {
|
||||
if data == nil {
|
||||
data = make(map[string]any)
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
isAuth := r.session.Exists(ctx, auth.StoreName)
|
||||
data["IsAuthenticated"] = isAuth
|
||||
if isAuth {
|
||||
var authUser dto.AuthorizeUser
|
||||
u := r.session.GetBytes(ctx, auth.StoreName)
|
||||
_ = json.Unmarshal(u, &authUser)
|
||||
|
||||
data["AuthorizeMenus"] = r.getCurrentPathBtns(ctx, authUser.RoleID, req.URL.Path)
|
||||
}
|
||||
token := nosurf.Token(req)
|
||||
data["CsrfToken"] = token
|
||||
data["CsrfTokenField"] = template.HTML(fmt.Sprintf(`<input type="hidden" name="csrf_token" value="%s" />`, token))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func (r *render) getCurrentPathBtns(ctx context.Context, roleID int32, path string) []*dto.OwnerMenuDto {
|
||||
var res []*dto.OwnerMenuDto
|
||||
|
||||
// 获取当前path的菜单
|
||||
menu, err := r.menuBiz.GetSysMenuByUrl(ctx, path)
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
menus, err := r.menuBiz.ListOwnerMenuByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
|
||||
for _, item := range menus {
|
||||
if menu.IsList {
|
||||
if item.ParentID == menu.ID || item.ID == menu.ID {
|
||||
res = append(res, item)
|
||||
}
|
||||
} else {
|
||||
if item.ParentID == menu.ParentID {
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *render) createTemplateCache() (map[string]*template.Template, error) {
|
||||
cache := make(map[string]*template.Template)
|
||||
pages, err := getFiles(r.config.Root, r.config.Extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layoutAndPartial, err := r.getLayoutAndPartials()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, page := range pages {
|
||||
if strings.HasPrefix(page, "base") || strings.HasSuffix(page, "partial") {
|
||||
continue
|
||||
}
|
||||
|
||||
name := filepath.Base(page)
|
||||
pathArr := strings.Split(page, "/")
|
||||
dir := pathArr[len(pathArr)-2 : len(pathArr)-1]
|
||||
templateName := fmt.Sprintf("%s_%s", dir[0], name)
|
||||
ts := template.Must(template.New(templateName).Funcs(r.btnFuncs()).Funcs(r.Methods()).ParseFS(templates.TemplateFS, page))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts, err = ts.ParseFS(templates.TemplateFS, layoutAndPartial...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cache[templateName] = ts
|
||||
}
|
||||
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (r *render) getLayoutAndPartials() ([]string, error) {
|
||||
layouts, err := getFiles(r.config.Layout, r.config.Extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partials, err := getFiles(r.config.Partial, r.config.Extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return slices.Concat(layouts, partials), nil
|
||||
}
|
||||
|
||||
func getFiles(path string, stuffix string) ([]string, error) {
|
||||
files := make([]string, 0)
|
||||
b, err := pathExists(templates.TemplateFS, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !b {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
err = fs.WalkDir(templates.TemplateFS, path, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(path, stuffix) {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
// if info == nil {
|
||||
// return err
|
||||
// }
|
||||
// if info.IsDir() {
|
||||
// return nil
|
||||
// }
|
||||
// // 将模板后缀的文件放到列表
|
||||
// if strings.HasSuffix(path, stuffix) {
|
||||
// files = append(files, path)
|
||||
// }
|
||||
// return nil
|
||||
// })
|
||||
return files, err
|
||||
}
|
||||
|
||||
func pathExists(fs fs.FS, path string) (bool, error) {
|
||||
_, err := fs.Open(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
func firstLower(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/pkg/convertor"
|
||||
"management/internal/router/manage/util"
|
||||
categoryservice "management/internal/service/category"
|
||||
"management/internal/tpl"
|
||||
@@ -28,12 +29,12 @@ func (h *CategoryHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *CategoryHandler) PostList(w http.ResponseWriter, r *http.Request) {
|
||||
var q dto.SearchDto
|
||||
q.SearchStatus = util.ConvertInt(r.PostFormValue("SearchStatus"), 9999)
|
||||
q.SearchParentID = util.ConvertInt(r.PostFormValue("SearchParentID"), 0)
|
||||
q.SearchName = r.PostFormValue("SearchName")
|
||||
q.SearchKey = r.PostFormValue("SearchKey")
|
||||
q.Page = util.ConvertInt(r.PostFormValue("page"), 1)
|
||||
q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10)
|
||||
q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999)
|
||||
q.SearchParentID = convertor.ConvertInt(r.PostFormValue("parentId"), 0)
|
||||
q.SearchName = r.PostFormValue("name")
|
||||
q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0)
|
||||
q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1)
|
||||
q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10)
|
||||
res, count, err := categoryservice.ListCategoriesCondition(r.Context(), q)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -194,19 +195,32 @@ func (h *CategoryHandler) DTree(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *CategoryHandler) XmSelect(w http.ResponseWriter, r *http.Request) {
|
||||
all, err := categoryservice.ListByLetter(r.Context(), r.URL.Query().Get("letter"))
|
||||
letter := r.URL.Query().Get("letter")
|
||||
ctx := r.Context()
|
||||
if len(letter) > 0 {
|
||||
all, err := categoryservice.ListByLetter(ctx, letter)
|
||||
if err != nil {
|
||||
tpl.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var res []*dto.XmSelectStrDto
|
||||
for _, v := range all {
|
||||
res = append(res, &dto.XmSelectStrDto{
|
||||
Name: v.Name,
|
||||
Value: strconv.FormatInt(int64(v.ID), 10),
|
||||
})
|
||||
}
|
||||
tpl.JSON(w, res)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := categoryservice.XmSelectCategory(ctx, 0)
|
||||
if err != nil {
|
||||
tpl.JSONERR(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var res []*dto.XmSelectStrDto
|
||||
for _, v := range all {
|
||||
res = append(res, &dto.XmSelectStrDto{
|
||||
Name: v.Name,
|
||||
Value: strconv.FormatInt(int64(v.ID), 10),
|
||||
})
|
||||
}
|
||||
tpl.JSON(w, res)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ import (
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
authglobal "management/internal/global/auth"
|
||||
"management/internal/pkg/crypto"
|
||||
"management/internal/pkg/session"
|
||||
captchaservice "management/internal/service/captcha"
|
||||
systemservice "management/internal/service/system"
|
||||
"management/internal/tpl"
|
||||
@@ -19,19 +17,19 @@ import (
|
||||
)
|
||||
|
||||
func Login(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var user dto.AuthorizeUser
|
||||
u := session.GetBytes(ctx, authglobal.StoreName)
|
||||
if err := json.Unmarshal(u, &user); err == nil {
|
||||
// 判断租户是否一致, 一致则刷新令牌,跳转到首页
|
||||
if err := session.RenewToken(ctx); err == nil {
|
||||
session.Put(ctx, authglobal.StoreName, u)
|
||||
http.Redirect(w, r, "/home.html", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
// ctx := r.Context()
|
||||
// var user dto.AuthorizeUser
|
||||
// u := session.GetBytes(ctx, authglobal.StoreName)
|
||||
// if err := json.Unmarshal(u, &user); err == nil {
|
||||
// // 判断租户是否一致, 一致则刷新令牌,跳转到首页
|
||||
// if err := session.RenewToken(ctx); err == nil {
|
||||
// session.Put(ctx, authglobal.StoreName, u)
|
||||
// http.Redirect(w, r, "/home.html", http.StatusFound)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
session.Destroy(ctx)
|
||||
// session.Destroy(ctx)
|
||||
tpl.HTML(w, r, "oauth/login.tmpl", nil)
|
||||
}
|
||||
|
||||
@@ -122,7 +120,7 @@ func PostLogin(w http.ResponseWriter, r *http.Request) {
|
||||
Browser: log.Browser,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(auth)
|
||||
_, err = json.Marshal(auth)
|
||||
if err != nil {
|
||||
log.Message = err.Error()
|
||||
_ = systemservice.CreateSysUserLoginLog(ctx, log)
|
||||
@@ -130,15 +128,15 @@ func PostLogin(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
session.Put(ctx, authglobal.StoreName, b)
|
||||
// session.Put(ctx, authglobal.StoreName, b)
|
||||
|
||||
log.IsSuccess = true
|
||||
log.Message = "登陆成功"
|
||||
_ = systemservice.CreateSysUserLoginLog(ctx, log)
|
||||
tpl.JSON(w, tpl.Response{Success: true, Message: "login successful"})
|
||||
tpl.JSONOK(w, "login successful")
|
||||
}
|
||||
|
||||
func Logout(w http.ResponseWriter, r *http.Request) {
|
||||
session.Destroy(r.Context())
|
||||
// session.Destroy(r.Context())
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"management/internal/middleware/manage/audit"
|
||||
"management/internal/middleware/manage/auth"
|
||||
"management/internal/middleware/manage/nosurf"
|
||||
"management/internal/middleware/manage/session"
|
||||
|
||||
budgethandler "management/internal/router/manage/budget"
|
||||
cachehandler "management/internal/router/manage/cache"
|
||||
@@ -38,8 +37,8 @@ func NewRouter() *chi.Mux {
|
||||
r.Handle("/upload/*", http.StripPrefix("/upload", uploadServer))
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(nosurf.NoSurf) // CSRF
|
||||
r.Use(session.LoadSession) // Session
|
||||
r.Use(nosurf.NoSurf) // CSRF
|
||||
// r.Use(session.LoadSession) // Session
|
||||
|
||||
r.Get("/captcha", commonhandler.Captcha)
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/global/auth"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/router/manage/util"
|
||||
systemservice "management/internal/service/system"
|
||||
"management/internal/tpl"
|
||||
@@ -189,20 +185,20 @@ func (h *SysMenuHandler) Save(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *SysMenuHandler) UserMenus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
b := session.GetBytes(ctx, auth.StoreName)
|
||||
var u dto.AuthorizeUser
|
||||
if err := json.Unmarshal(b, &u); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
menus, err := systemservice.RecursiveSysMenus(ctx, u.RoleID)
|
||||
if err != nil {
|
||||
tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
// ctx := r.Context()
|
||||
// b := session.GetBytes(ctx, auth.StoreName)
|
||||
// var u dto.AuthorizeUser
|
||||
// if err := json.Unmarshal(b, &u); err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// menus, err := systemservice.RecursiveSysMenus(ctx, u.RoleID)
|
||||
// if err != nil {
|
||||
// tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()})
|
||||
// return
|
||||
// }
|
||||
|
||||
tpl.JSON(w, menus)
|
||||
tpl.JSON(w, nil)
|
||||
}
|
||||
|
||||
func (h *SysMenuHandler) XmSelectTree(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
db "management/internal/db/sqlc"
|
||||
"management/internal/pkg/convertor"
|
||||
"management/internal/router/manage/util"
|
||||
systemservice "management/internal/service/system"
|
||||
"management/internal/tpl"
|
||||
@@ -25,14 +26,13 @@ func (h *SysRoleHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *SysRoleHandler) PostList(w http.ResponseWriter, r *http.Request) {
|
||||
var q dto.SearchDto
|
||||
q.SearchStatus = util.ConvertInt(r.PostFormValue("SearchStatus"), 9999)
|
||||
q.SearchParentID = util.ConvertInt(r.PostFormValue("SearchParentID"), 0)
|
||||
q.SearchName = r.PostFormValue("SearchName")
|
||||
q.SearchKey = r.PostFormValue("SearchKey")
|
||||
q.Page = util.ConvertInt(r.PostFormValue("page"), 1)
|
||||
q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10)
|
||||
ctx := r.Context()
|
||||
res, count, err := systemservice.ListSysRoleCondition(ctx, q)
|
||||
q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999)
|
||||
q.SearchParentID = convertor.ConvertInt(r.PostFormValue("parentId"), 0)
|
||||
q.SearchName = r.PostFormValue("name")
|
||||
q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0)
|
||||
q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1)
|
||||
q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10)
|
||||
res, count, err := systemservice.ListSysRoleCondition(r.Context(), q)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -141,7 +141,7 @@ func (h *SysRoleHandler) Save(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *SysRoleHandler) XmSelect(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
res, err := systemservice.XmSelectSysRole(ctx)
|
||||
res, err := systemservice.XmSelectSysRole(ctx, 0)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
@@ -19,35 +18,25 @@ func ListCategoriesCondition(ctx context.Context, q dto.SearchDto) ([]*db.Catego
|
||||
countArg := &db.CountCategoriesConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int16(q.SearchStatus),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0 && q.SearchParentID != 1,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
Name: q.SearchName,
|
||||
}
|
||||
|
||||
dataArg := &db.ListCategoriesConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int16(q.SearchStatus),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0 && q.SearchParentID != 1,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
Name: q.SearchName,
|
||||
Skip: (int32(q.Page) - 1) * int32(q.Rows),
|
||||
Size: int32(q.Rows),
|
||||
}
|
||||
|
||||
if len(q.SearchKey) > 0 {
|
||||
switch strings.ToLower(q.SearchName) {
|
||||
case "id":
|
||||
id, err := strconv.Atoi(q.SearchKey)
|
||||
if err == nil {
|
||||
countArg.IsID = true
|
||||
countArg.ID = int32(id)
|
||||
|
||||
dataArg.IsID = true
|
||||
dataArg.ID = int32(id)
|
||||
}
|
||||
case "name":
|
||||
countArg.Name = q.SearchKey
|
||||
dataArg.Name = q.SearchKey
|
||||
}
|
||||
}
|
||||
count, err := db.Engine.CountCategoriesCondition(ctx, countArg)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -70,6 +59,15 @@ func DTreeCategory(ctx context.Context, id int32) ([]*dto.DTreeDto, error) {
|
||||
return toDtree(id, all), nil
|
||||
}
|
||||
|
||||
func XmSelectCategory(ctx context.Context, id int32) ([]*dto.XmSelectTreeDto, error) {
|
||||
all, err := db.Engine.AllCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toXmSelectTree(id, all), nil
|
||||
}
|
||||
|
||||
func GetParentCategorySelectLetter(ctx context.Context, letter string) ([]*global.DataDict, error) {
|
||||
all := AllCategories(ctx)
|
||||
if len(all) == 0 {
|
||||
@@ -178,6 +176,25 @@ func toDtree(parentId int32, data []*db.Category) []*dto.DTreeDto {
|
||||
item.Last = !hasChildren(v.ID, data)
|
||||
item.ParentId = strconv.FormatInt(int64(v.ParentID), 10)
|
||||
item.Children = toDtree(v.ID, data)
|
||||
if v.ParentID == 0 {
|
||||
item.Spread = true
|
||||
}
|
||||
res = append(res, &item)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func toXmSelectTree(parentId int32, data []*db.Category) []*dto.XmSelectTreeDto {
|
||||
var res []*dto.XmSelectTreeDto
|
||||
for _, v := range data {
|
||||
if v.ParentID == parentId {
|
||||
item := dto.XmSelectTreeDto{
|
||||
Name: v.Name,
|
||||
Value: strconv.FormatInt(int64(v.ID), 10),
|
||||
Children: toXmSelectTree(v.ID, data),
|
||||
}
|
||||
res = append(res, &item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
@@ -27,36 +26,25 @@ func GetSysRole(ctx context.Context, id int32) (*db.SysRole, error) {
|
||||
|
||||
func ListSysRoleCondition(ctx context.Context, q dto.SearchDto) ([]*db.SysRole, int64, error) {
|
||||
countArg := &db.CountSysRoleConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
DisplayName: q.SearchName,
|
||||
}
|
||||
|
||||
dataArg := &db.ListSysRoleConditionParams{
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
Skip: (int32(q.Page) - 1) * int32(q.Rows),
|
||||
Size: int32(q.Rows),
|
||||
}
|
||||
|
||||
if len(q.SearchKey) > 0 {
|
||||
switch strings.ToLower(q.SearchName) {
|
||||
case "id":
|
||||
id, err := strconv.Atoi(q.SearchKey)
|
||||
if err == nil {
|
||||
countArg.IsID = true
|
||||
countArg.ID = int32(id)
|
||||
|
||||
dataArg.IsID = true
|
||||
dataArg.ID = int32(id)
|
||||
}
|
||||
case "name":
|
||||
countArg.DisplayName = q.SearchKey
|
||||
dataArg.DisplayName = q.SearchKey
|
||||
}
|
||||
IsStatus: q.SearchStatus != 9999,
|
||||
Status: int32(q.SearchStatus),
|
||||
IsID: q.SearchID != 0,
|
||||
ID: int32(q.SearchID),
|
||||
IsParentID: q.SearchParentID != 0,
|
||||
ParentID: int32(q.SearchParentID),
|
||||
DisplayName: q.SearchName,
|
||||
Skip: (int32(q.Page) - 1) * int32(q.Rows),
|
||||
Size: int32(q.Rows),
|
||||
}
|
||||
count, err := db.Engine.CountSysRoleCondition(ctx, countArg)
|
||||
if err != nil {
|
||||
@@ -71,18 +59,29 @@ func ListSysRoleCondition(ctx context.Context, q dto.SearchDto) ([]*db.SysRole,
|
||||
return roles, count, nil
|
||||
}
|
||||
|
||||
func XmSelectSysRole(ctx context.Context) ([]*dto.XmSelectDto, error) {
|
||||
func XmSelectSysRole(ctx context.Context, id int32) ([]*dto.XmSelectTreeDto, error) {
|
||||
all, err := db.Engine.AllSysRole(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []*dto.XmSelectDto
|
||||
for _, item := range all {
|
||||
res = append(res, &dto.XmSelectDto{Name: item.DisplayName, Value: int(item.ID)})
|
||||
return toXmSelectTree(id, all), nil
|
||||
}
|
||||
|
||||
func toXmSelectTree(parentId int32, data []*db.SysRole) []*dto.XmSelectTreeDto {
|
||||
var res []*dto.XmSelectTreeDto
|
||||
for _, v := range data {
|
||||
if v.ParentID == parentId {
|
||||
item := dto.XmSelectTreeDto{
|
||||
Name: v.Name,
|
||||
Value: strconv.FormatInt(int64(v.ID), 10),
|
||||
Children: toXmSelectTree(v.ID, data),
|
||||
}
|
||||
res = append(res, &item)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return res
|
||||
}
|
||||
|
||||
func SetMenu(ctx context.Context, roleID int32, menus []*db.SysMenu) error {
|
||||
|
||||
@@ -23,11 +23,11 @@ type HtmlData struct {
|
||||
Data any
|
||||
}
|
||||
|
||||
func HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) {
|
||||
rndr.html(w, req, tpl, data)
|
||||
func HTML(w http.ResponseWriter, r *http.Request, tpl string, data map[string]any) {
|
||||
rndr.HTML(w, r, tpl, data)
|
||||
}
|
||||
|
||||
func (r *render) html(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) {
|
||||
func (r *render) HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) {
|
||||
name := strings.ReplaceAll(tpl, "/", "_")
|
||||
t, ok := r.templates[name]
|
||||
if !ok {
|
||||
|
||||
@@ -29,22 +29,34 @@ type ResponseList struct {
|
||||
}
|
||||
|
||||
func JSON(w http.ResponseWriter, data any) {
|
||||
rndr.json(w, data)
|
||||
rndr.JSON(w, data)
|
||||
}
|
||||
|
||||
func JSONF(w http.ResponseWriter, success bool, message string) {
|
||||
rndr.json(w, Response{Success: success, Message: message})
|
||||
rndr.JSONF(w, success, message)
|
||||
}
|
||||
|
||||
func JSONOK(w http.ResponseWriter, message string) {
|
||||
rndr.json(w, Response{Success: true, Message: message})
|
||||
rndr.JSONOK(w, message)
|
||||
}
|
||||
|
||||
func JSONERR(w http.ResponseWriter, message string) {
|
||||
rndr.json(w, Response{Success: false, Message: message})
|
||||
rndr.JSONERR(w, message)
|
||||
}
|
||||
|
||||
func (r *render) json(w http.ResponseWriter, data any) {
|
||||
func (r *render) JSONF(w http.ResponseWriter, success bool, message string) {
|
||||
r.JSON(w, Response{Success: success, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONOK(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: true, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONERR(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: false, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSON(w http.ResponseWriter, data any) {
|
||||
v, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
@@ -3,26 +3,29 @@ package tpl
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
var rndr Renderer
|
||||
|
||||
func Render() Renderer {
|
||||
return rndr
|
||||
}
|
||||
|
||||
type Renderer interface {
|
||||
html(w http.ResponseWriter, req *http.Request, name string, data map[string]any)
|
||||
json(w http.ResponseWriter, data any)
|
||||
HTML(w http.ResponseWriter, req *http.Request, name string, data map[string]any)
|
||||
JSON(w http.ResponseWriter, data any)
|
||||
JSONF(w http.ResponseWriter, success bool, message string)
|
||||
JSONOK(w http.ResponseWriter, message string)
|
||||
JSONERR(w http.ResponseWriter, message string)
|
||||
}
|
||||
|
||||
type render struct {
|
||||
session session.ISession
|
||||
config *TemplateConfig
|
||||
templates map[string]*template.Template
|
||||
}
|
||||
|
||||
func Init() error {
|
||||
func New(session session.ISession) (Renderer, error) {
|
||||
render := &render{
|
||||
session: session,
|
||||
config: &TemplateConfig{
|
||||
Root: ".",
|
||||
Extension: ".tmpl",
|
||||
@@ -33,15 +36,9 @@ func Init() error {
|
||||
|
||||
templates, err := render.createTemplateCache()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
render.templates = templates
|
||||
rndr = render
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitJson() error {
|
||||
rndr = &render{}
|
||||
return nil
|
||||
return render, nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"management/internal/db/model/dto"
|
||||
"management/internal/global/auth"
|
||||
"management/internal/pkg/session"
|
||||
systemservice "management/internal/service/system"
|
||||
templates "management/web/templates/manage"
|
||||
|
||||
@@ -27,11 +26,11 @@ func (r *render) setDefaultData(req *http.Request, data map[string]any) map[stri
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
isAuth := session.Exists(ctx, auth.StoreName)
|
||||
isAuth := r.session.Exists(ctx, auth.StoreName)
|
||||
data["IsAuthenticated"] = isAuth
|
||||
if isAuth {
|
||||
var authUser dto.AuthorizeUser
|
||||
u := session.GetBytes(ctx, auth.StoreName)
|
||||
u := r.session.GetBytes(ctx, auth.StoreName)
|
||||
_ = json.Unmarshal(u, &authUser)
|
||||
|
||||
data["AuthorizeMenus"] = r.getCurrentPathBtns(ctx, authUser.RoleID, req.URL.Path)
|
||||
|
||||
Reference in New Issue
Block a user