diff --git a/cmd/erp.go b/cmd/erp.go index 123869e..e1c8c61 100644 --- a/cmd/erp.go +++ b/cmd/erp.go @@ -65,7 +65,7 @@ func runErp(ctx context.Context) error { rander, err := tpl.New(session, biz.SystemV1().MenuBiz()) checkError(err) - handler := handler.NewHandler(conf, rander, session, biz) + handler := handler.NewHandler(conf, rander, redis, session, biz) address := fmt.Sprintf("%s:%d", conf.App.Host, conf.App.Port) log.Printf("Starting erp manage server on %s", address) diff --git a/internal/erpserver/biz/v1/system/category.go b/internal/erpserver/biz/v1/system/category.go new file mode 100644 index 0000000..6b3f844 --- /dev/null +++ b/internal/erpserver/biz/v1/system/category.go @@ -0,0 +1,174 @@ +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" +) + +type CategoryBiz interface { + Create(ctx context.Context, arg *db.CreateCategoryParams) (*db.Category, error) + Update(ctx context.Context, arg *db.UpdateCategoryParams) (*db.Category, error) + All(ctx context.Context) ([]*db.Category, error) + List(ctx context.Context, q dto.SearchDto) ([]*db.Category, int64, error) + Get(ctx context.Context, id int32) (*db.Category, error) + Refresh(ctx context.Context) ([]*db.Category, error) + RebuildParentPath(ctx context.Context) error + + Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error) + XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, error) +} + +type categoryBiz struct { + store db.Store + redis redis.IRedis +} + +var _ CategoryBiz = (*categoryBiz)(nil) + +func NewCategory(store db.Store, redis redis.IRedis) *categoryBiz { + return &categoryBiz{ + store: store, + redis: redis, + } +} + +func (b *categoryBiz) All(ctx context.Context) ([]*db.Category, error) { + key := keys.GetManageKey(ctx, keys.AllCategories) + bs, err := redis.GetBytes(ctx, key) + if err == nil { + var res []*db.Category + if err := json.Unmarshal(bs, &res); err == nil { + return res, nil + } + } + + return b.Refresh(ctx) +} + +func (b *categoryBiz) List(ctx context.Context, q dto.SearchDto) ([]*db.Category, int64, error) { + countArg := &db.CountCategoriesConditionParams{ + IsStatus: q.SearchStatus != 9999, + Status: int16(q.SearchStatus), + IsID: q.SearchID != 0, + ID: int32(q.SearchID), + IsParentID: q.SearchParentID != 0, + ParentID: int32(q.SearchParentID), + Name: q.SearchName, + } + + dataArg := &db.ListCategoriesConditionParams{ + IsStatus: q.SearchStatus != 9999, + Status: int16(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.CountCategoriesCondition(ctx, countArg) + if err != nil { + return nil, 0, err + } + + departs, err := b.store.ListCategoriesCondition(ctx, dataArg) + if err != nil { + return nil, 0, err + } + + return departs, count, nil +} + +func (b *categoryBiz) Get(ctx context.Context, id int32) (*db.Category, error) { + return b.store.GetCategory(ctx, id) +} + +func (b *categoryBiz) Create(ctx context.Context, arg *db.CreateCategoryParams) (*db.Category, error) { + return b.store.CreateCategory(ctx, arg) +} + +func (b *categoryBiz) Update(ctx context.Context, arg *db.UpdateCategoryParams) (*db.Category, error) { + return b.store.UpdateCategory(ctx, arg) +} + +func (b *categoryBiz) Refresh(ctx context.Context) ([]*db.Category, error) { + all, err := b.store.AllCategories(ctx) + if err != nil { + return nil, err + } + + bs, err := json.Marshal(all) + if err != nil { + return nil, err + } + + redis.Del(ctx, keys.GetManageKey(ctx, keys.AllCategorySimple)) + key := keys.GetManageKey(ctx, keys.AllCategories) + err = redis.Set(ctx, key, bs, time.Hour*6) + return all, err +} + +func (b *categoryBiz) RebuildParentPath(ctx context.Context) error { + return b.store.CategoryRebuildPath(ctx) +} + +func (b *categoryBiz) Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error) { + all, err := b.All(ctx) + if err != nil { + return nil, err + } + + return b.toTree(id, all), nil +} + +func (b *categoryBiz) XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, error) { + all, err := b.All(ctx) + if err != nil { + return nil, err + } + + return b.toXmSelectTree(id, all), nil +} + +func (b *categoryBiz) toTree(parentId int32, data []*db.Category) []*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 = b.toTree(v.ID, data) + if v.ParentID == 0 { + item.Spread = true + } + res = append(res, &item) + } + } + + return res +} + +func (b *categoryBiz) toXmSelectTree(parentId int32, data []*db.Category) []*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: b.toXmSelectTree(v.ID, data), + } + res = append(res, &item) + } + } + + return res +} diff --git a/internal/erpserver/biz/v1/system/config.go b/internal/erpserver/biz/v1/system/config.go index 00faf21..05411c0 100644 --- a/internal/erpserver/biz/v1/system/config.go +++ b/internal/erpserver/biz/v1/system/config.go @@ -13,6 +13,11 @@ import ( ) type ConfigBiz interface { + Create(ctx context.Context, arg *db.CreateSysConfigParams) error + Update(ctx context.Context, arg *db.UpdateSysConfigByKeyParams) error + Get(ctx context.Context, id int32) (*db.SysConfig, error) + List(ctx context.Context, q dto.SearchDto) ([]*db.SysConfig, int64, error) + ConfigExpansion } @@ -34,6 +39,36 @@ func NewConfig(store db.Store, redis redis.IRedis) *configBiz { } } +func (b *configBiz) Create(ctx context.Context, arg *db.CreateSysConfigParams) error { + return b.store.CreateSysConfig(ctx, arg) +} + +func (b *configBiz) Update(ctx context.Context, arg *db.UpdateSysConfigByKeyParams) error { + return b.store.UpdateSysConfigByKey(ctx, arg) +} + +func (b *configBiz) Get(ctx context.Context, id int32) (*db.SysConfig, error) { + return b.store.GetSysConfig(ctx, id) +} + +func (b *configBiz) List(ctx context.Context, q dto.SearchDto) ([]*db.SysConfig, int64, error) { + count, err := b.store.CountSysConfigCondition(ctx, q.SearchKey) + if err != nil { + return nil, 0, err + } + + configs, err := b.store.ListSysConfigCondition(ctx, &db.ListSysConfigConditionParams{ + Key: q.SearchName, + Skip: (int32(q.Page) - 1) * int32(q.Rows), + Size: int32(q.Rows), + }) + if err != nil { + return nil, 0, err + } + + return configs, count, nil +} + func (b *configBiz) Pear(ctx context.Context) (*dto.PearConfig, error) { // 判断redis是否存储 key := keys.GetManageKey(ctx, keys.PearAdmin) diff --git a/internal/erpserver/biz/v1/system/menu.go b/internal/erpserver/biz/v1/system/menu.go index 6038d17..17a2fee 100644 --- a/internal/erpserver/biz/v1/system/menu.go +++ b/internal/erpserver/biz/v1/system/menu.go @@ -3,6 +3,7 @@ package system import ( "context" "encoding/json" + "slices" "strconv" "strings" "time" @@ -32,9 +33,11 @@ type MenuExpansion interface { MapOwnerMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) SetOwnerMapMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) RefreshMenus(ctx context.Context) error + SetMenuViewData(ctx context.Context, roleID int32) ([]*dto.SetMenuDto, error) Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error) XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, error) + SetMenu(ctx context.Context, roleID int32, menus []*db.SysMenu) error } type menuBiz struct { @@ -274,6 +277,42 @@ func (b *menuBiz) XmSelect(ctx context.Context, id int32) ([]*view.XmSelectTree, return b.toXmSelectTree(id, all), nil } +func (b *menuBiz) SetMenu(ctx context.Context, roleID int32, menus []*db.SysMenu) error { + return b.store.ExecTx(ctx, func(q *db.Queries) error { + err := q.DeleteRoleMneuByRoleID(ctx, roleID) + if err != nil { + return err + } + + for _, m := range menus { + err := q.CreateRoleMenu(ctx, &db.CreateRoleMenuParams{ + RoleID: roleID, + MenuID: m.ID, + }) + if err != nil { + return err + } + } + + return nil + }) +} + +func (b *menuBiz) SetMenuViewData(ctx context.Context, roleID int32) ([]*dto.SetMenuDto, error) { + // 获取该用户已经有的权限 + hs, err := b.store.ListSysMenuIDByRoleID(ctx, roleID) + if err != nil { + return nil, err + } + + all, err := b.AllMenusCache(ctx) + if err != nil { + return nil, err + } + + return toSetMenuTree(all, hs, 0), nil +} + func (b *menuBiz) ownerMenusByRoleID(ctx context.Context, roleID int32) ([]*db.SysMenu, error) { // 判断当前用户是否有vip角色 role, err := b.store.GetSysRole(ctx, roleID) @@ -458,3 +497,49 @@ func uniqueSysMenus(sm []*db.SysMenu) []*db.SysMenu { } return res } + +func toSetMenuTree(data []*db.SysMenu, ids []int32, parentID int32) []*dto.SetMenuDto { + var res []*dto.SetMenuDto + for _, v := range data { + if v.ParentID == parentID { + isSelect := hasValueInArray(ids, v.ID) + if v.IsList { + item := dto.SetMenuDto{ + ID: v.ID, + Name: v.DisplayName, + Link: v.Type, + IsList: v.IsList, + IsSelect: isSelect, + } + item.Items = []*dto.SetMenuDto{ + { + ID: v.ID, + Name: "列表", + Link: "btn", + IsList: false, + IsSelect: isSelect, + Items: toSetMenuTree(data, ids, v.ID), + }, + } + item.Items = append(item.Items, toSetMenuTree(data, ids, v.ID)...) + res = append(res, &item) + } else { + item := dto.SetMenuDto{ + ID: v.ID, + Name: v.DisplayName, + Link: v.Type, + IsList: v.IsList, + IsSelect: isSelect, + Items: toSetMenuTree(data, ids, v.ID), + } + res = append(res, &item) + } + } + } + + return res +} + +func hasValueInArray(data []int32, id int32) bool { + return slices.Contains(data, id) +} diff --git a/internal/erpserver/biz/v1/system/system.go b/internal/erpserver/biz/v1/system/system.go index 2f4a8a6..caca6cf 100644 --- a/internal/erpserver/biz/v1/system/system.go +++ b/internal/erpserver/biz/v1/system/system.go @@ -14,6 +14,7 @@ type SystemBiz interface { ConfigBiz() ConfigBiz AuditBiz() AuditBiz LoginLogBiz() LoginLogBiz + CategoryBiz() CategoryBiz } type systemBiz struct { @@ -59,3 +60,7 @@ func (b *systemBiz) AuditBiz() AuditBiz { func (b *systemBiz) LoginLogBiz() LoginLogBiz { return NewLoginLog(b.store) } + +func (b *systemBiz) CategoryBiz() CategoryBiz { + return NewCategory(b.store, b.redis) +} diff --git a/internal/erpserver/handler/handler.go b/internal/erpserver/handler/handler.go index 91b8323..f6ad9b7 100644 --- a/internal/erpserver/handler/handler.go +++ b/internal/erpserver/handler/handler.go @@ -5,6 +5,7 @@ import ( "management/internal/erpserver/biz" "management/internal/erpserver/handler/common" "management/internal/erpserver/handler/system" + "management/internal/pkg/redis" "management/internal/pkg/session" "management/internal/pkg/tpl" ) @@ -21,6 +22,7 @@ type IHandler interface { type handler struct { conf *config.Config render tpl.Renderer + redis redis.IRedis session session.ISession biz biz.IBiz } @@ -29,10 +31,11 @@ type handler struct { var _ IHandler = (*handler)(nil) // NewHandler 创建一个 IHandler 类型的实例. -func NewHandler(conf *config.Config, render tpl.Renderer, session session.ISession, biz biz.IBiz) *handler { +func NewHandler(conf *config.Config, render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz) *handler { return &handler{ conf: conf, render: render, + redis: redis, session: session, biz: biz, } @@ -45,5 +48,5 @@ func (h *handler) CommonHandler() common.CommonHandler { // SystemHandler 返回一个实现了 SystemHandler 接口的实例. func (h *handler) SystemHandler() system.SystemHandler { - return system.NewSystemHandler(h.render, h.session, h.biz) + return system.NewSystemHandler(h.render, h.redis, h.session, h.biz) } diff --git a/internal/erpserver/handler/system/category.go b/internal/erpserver/handler/system/category.go new file mode 100644 index 0000000..f738093 --- /dev/null +++ b/internal/erpserver/handler/system/category.go @@ -0,0 +1,239 @@ +package system + +import ( + "fmt" + "net/http" + "strings" + + "management/internal/db/model/dto" + db "management/internal/db/sqlc" + "management/internal/erpserver/biz" + "management/internal/pkg/convertor" + "management/internal/pkg/tpl" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type CategoryHandler 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 categoryHandler struct { + render tpl.Renderer + biz biz.IBiz +} + +var _ CategoryHandler = (*categoryHandler)(nil) + +func NewCategoryHandler(render tpl.Renderer, biz biz.IBiz) *categoryHandler { + return &categoryHandler{ + render: render, + biz: biz, + } +} + +func (h *categoryHandler) List(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + h.render.HTML(w, r, "category/list.tmpl", nil) + case 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().CategoryBiz().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) + default: + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + } +} + +func (h *categoryHandler) Add(w http.ResponseWriter, r *http.Request) { + h.render.HTML(w, r, "category/edit.tmpl", map[string]any{ + "Item": &db.Category{Sort: 6666}, + }) +} + +func (h *categoryHandler) AddChildren(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + parentID := convertor.QueryInt[int32](vars, "parentID", 0) + vm := &db.Category{ParentID: parentID, Sort: 6666} + h.render.HTML(w, r, "category/edit.tmpl", map[string]any{ + "Item": vm, + }) +} + +func (h *categoryHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.QueryInt[int32](vars, "id", 0) + vm := &db.Category{Sort: 6666} + if id > 0 { + vm, _ = h.biz.SystemV1().CategoryBiz().Get(r.Context(), id) + } + h.render.HTML(w, r, "category/edit.tmpl", map[string]any{ + "Item": vm, + }) +} + +func (h *categoryHandler) Save(w http.ResponseWriter, r *http.Request) { + id := convertor.ConvertInt(r.PostFormValue("ID"), 0) + parentID := convertor.ConvertInt[int32](r.PostFormValue("ParentID"), 0) + name := r.PostFormValue("Name") + icon := r.PostFormValue("File") + if len(icon) > 0 && !strings.HasPrefix(icon, "/") { + icon = "/" + icon + } + description := r.PostFormValue("Description") + letter := r.PostFormValue("Letter") + if len(letter) == 0 { + letter = uuid.New().String() + } + sort := convertor.ConvertInt[int32](r.PostFormValue("Sort"), 6666) + status := convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) + + ctx := r.Context() + var parent *db.Category + if parentID > 0 { + var err error + parent, err = h.biz.SystemV1().CategoryBiz().Get(ctx, parentID) + if err != nil { + h.render.JSONERR(w, "父级节点错误") + return + } + } + + path := fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID) + path = strings.ReplaceAll(path, ",,", ",") + if id == 0 { + arg := &db.CreateCategoryParams{ + Name: name, + Icon: icon, + Description: description, + Letter: letter, + ParentID: parentID, + ParentPath: path, + Status: status, + Sort: sort, + } + _, err := h.biz.SystemV1().CategoryBiz().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 { + arg := &db.UpdateCategoryParams{ + ID: int32(id), + Name: pgtype.Text{ + String: name, + Valid: true, + }, + Icon: pgtype.Text{ + String: icon, + Valid: len(icon) > 0, + }, + Description: pgtype.Text{ + String: description, + Valid: len(description) > 0, + }, + Letter: pgtype.Text{ + String: letter, + Valid: len(letter) > 0, + }, + ParentID: pgtype.Int4{ + Int32: parentID, + Valid: true, + }, + ParentPath: pgtype.Text{ + String: path, + Valid: true, + }, + Sort: pgtype.Int4{ + Int32: sort, + Valid: true, + }, + Status: pgtype.Int2{ + Int16: status, + Valid: true, + }, + } + _, err := h.biz.SystemV1().CategoryBiz().Update(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "更新成功") + } +} + +func (h *categoryHandler) Tree(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := r.URL.Query() + if vars.Get("type") == "xmselect" { + res, err := h.biz.SystemV1().CategoryBiz().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().CategoryBiz().Tree(ctx, 0) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSON(w, res) + return + } +} + +func (h *categoryHandler) Refresh(w http.ResponseWriter, r *http.Request) { + _, err := h.biz.SystemV1().CategoryBiz().Refresh(r.Context()) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "刷新成功") +} + +func (h *categoryHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { + err := h.biz.SystemV1().CategoryBiz().RebuildParentPath(r.Context()) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "重建成功") +} diff --git a/internal/erpserver/handler/system/config.go b/internal/erpserver/handler/system/config.go index 07b1098..3575cf1 100644 --- a/internal/erpserver/handler/system/config.go +++ b/internal/erpserver/handler/system/config.go @@ -1,17 +1,26 @@ package system import ( + "encoding/json" "net/http" + "strings" + "management/internal/db/model/dto" + db "management/internal/db/sqlc" "management/internal/erpserver/biz" + "management/internal/global/pearadmin" + "management/internal/pkg/convertor" + "management/internal/pkg/redis" "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) + 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) + Refresh(w http.ResponseWriter, r *http.Request) + ResetPear(w http.ResponseWriter, r *http.Request) ConfigExpansion } @@ -23,19 +32,157 @@ type ConfigExpansion interface { // configHandler 是 ConfigHandler 接口的实现. type configHandler struct { render tpl.Renderer + redis redis.IRedis biz biz.IBiz } // 确保 userHandler 实现了 ConfigHandler 接口. var _ ConfigHandler = (*configHandler)(nil) -func NewConfigHandler(render tpl.Renderer, biz biz.IBiz) *configHandler { +func NewConfigHandler(render tpl.Renderer, redis redis.IRedis, biz biz.IBiz) *configHandler { return &configHandler{ render: render, + redis: redis, biz: biz, } } +func (h *configHandler) Add(w http.ResponseWriter, r *http.Request) { + h.render.HTML(w, r, "config/edit.tmpl", map[string]any{ + "Item": &db.SysConfig{}, + "Result": "", + }) +} + +type EditSysConfig struct { + *db.SysConfig + Result string +} + +func (h *configHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.QueryInt[int32](vars, "id", 0) + vm := &EditSysConfig{} + if id > 0 { + if conf, err := h.biz.SystemV1().ConfigBiz().Get(r.Context(), id); err == nil { + vm.SysConfig = conf + vm.Result = string(conf.Value) + } + } + h.render.HTML(w, r, "config/edit.tmpl", map[string]any{ + "Item": vm.SysConfig, + "Result": vm.Result, + }) +} + +func (h *configHandler) Save(w http.ResponseWriter, r *http.Request) { + id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) + key := r.PostFormValue("Key") + value := r.PostFormValue("Value") + if len(key) == 0 { + h.render.JSONERR(w, "Key不能为空") + return + } + if len(value) == 0 { + h.render.JSONERR(w, "Value不能为空") + return + } + + ctx := r.Context() + if id == 0 { + arg := &db.CreateSysConfigParams{ + Key: key, + Value: []byte(value), + } + err := h.biz.SystemV1().ConfigBiz().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().ConfigBiz().Get(ctx, id) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + arg := &db.UpdateSysConfigByKeyParams{ + Key: res.Key, + Value: []byte(value), + } + err = h.biz.SystemV1().ConfigBiz().Update(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "更新成功") + } +} + +func (h *configHandler) List(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + h.render.HTML(w, r, "config/list.tmpl", nil) + case http.MethodPost: + var q dto.SearchDto + q.SearchName = r.PostFormValue("name") + q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) + q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) + ctx := r.Context() + res, count, err := h.biz.SystemV1().ConfigBiz().List(ctx, q) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + data := tpl.ResponseList{ + Code: 0, + Message: "ok", + Count: count, + Data: res, + } + h.render.JSON(w, data) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h *configHandler) Refresh(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + err := h.redis.Del(r.Context(), strings.ToLower(key)) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "刷新成功") +} + +func (h *configHandler) ResetPear(w http.ResponseWriter, r *http.Request) { + b, err := json.Marshal(pearadmin.PearJson) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + err = h.biz.SystemV1().ConfigBiz().Update(r.Context(), &db.UpdateSysConfigByKeyParams{ + Key: pearadmin.PearKey, + Value: b, + }) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "重置成功") +} + func (h *configHandler) Pear(w http.ResponseWriter, r *http.Request) { pear, err := h.biz.SystemV1().ConfigBiz().Pear(r.Context()) if err != nil { diff --git a/internal/erpserver/handler/system/role.go b/internal/erpserver/handler/system/role.go index b88c96f..dea4b33 100644 --- a/internal/erpserver/handler/system/role.go +++ b/internal/erpserver/handler/system/role.go @@ -3,6 +3,7 @@ package system import ( "fmt" "net/http" + "strings" "time" "management/internal/db/model/dto" @@ -10,6 +11,7 @@ import ( "management/internal/erpserver/biz" "management/internal/pkg/convertor" "management/internal/pkg/tpl" + "management/internal/router/manage/util" ) type RoleHandler interface { @@ -22,6 +24,7 @@ type RoleHandler interface { Refresh(w http.ResponseWriter, r *http.Request) RebuildParentPath(w http.ResponseWriter, r *http.Request) RefreshRoleMenus(w http.ResponseWriter, r *http.Request) + SetMenu(w http.ResponseWriter, r *http.Request) } type roleHandler struct { @@ -247,3 +250,82 @@ func (h *roleHandler) RefreshRoleMenus(w http.ResponseWriter, r *http.Request) { h.render.JSONOK(w, "刷新成功") } + +func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + vars := r.URL.Query() + id := convertor.QueryInt[int32](vars, "id", 0) + vm := struct { + Role *db.SysRole + Menus []*dto.SetMenuDto + }{} + if id > 0 { + ctx := r.Context() + var err error + vm.Role, err = h.biz.SystemV1().RoleBiz().Get(ctx, id) + if err == nil { + vm.Menus, _ = h.biz.SystemV1().MenuBiz().SetMenuViewData(ctx, vm.Role.ID) + } + } + + h.render.HTML(w, r, "role/set_menu.tmpl", map[string]any{ + "Item": vm, + }) + case http.MethodPost: + id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) + menus := r.PostFormValue("roleMenu") + + if id == 0 { + h.render.JSONERR(w, "角色异常, 请刷新重试") + return + } + + if len(menus) == 0 { + h.render.JSONERR(w, "请选择菜单") + return + } + + ctx := r.Context() + _, err := h.biz.SystemV1().RoleBiz().Get(ctx, id) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + menuArr := strings.Split(menus, ",") + if len(menuArr) == 0 { + h.render.JSONERR(w, "请选择菜单") + return + } + + var menuList []*db.SysMenu + for _, v := range menuArr { + menuID := util.ConvertInt(v, 0) + if menuID > 0 { + menu, err := h.biz.SystemV1().MenuBiz().Get(ctx, int32(menuID)) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + menuList = append(menuList, menu) + } + } + + if len(menuList) == 0 { + h.render.JSONERR(w, "请选择正确的菜单") + return + } + + err = h.biz.SystemV1().MenuBiz().SetMenu(ctx, id, menuList) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSONOK(w, "设置成功") + default: + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + } +} diff --git a/internal/erpserver/handler/system/system.go b/internal/erpserver/handler/system/system.go index 7716e8e..bc45d33 100644 --- a/internal/erpserver/handler/system/system.go +++ b/internal/erpserver/handler/system/system.go @@ -4,6 +4,7 @@ import ( "net/http" "management/internal/erpserver/biz" + "management/internal/pkg/redis" "management/internal/pkg/session" "management/internal/pkg/tpl" ) @@ -17,19 +18,22 @@ type SystemHandler interface { ConfigHandler() ConfigHandler AuditHandler() AuditHandler LoginLogHandler() LoginLogHandler + CategoryHandler() CategoryHandler } type systemHandler struct { render tpl.Renderer + redis redis.IRedis session session.ISession biz biz.IBiz } var _ SystemHandler = (*systemHandler)(nil) -func NewSystemHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *systemHandler { +func NewSystemHandler(render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz) *systemHandler { return &systemHandler{ render: render, + redis: redis, session: session, biz: biz, } @@ -56,7 +60,7 @@ func (h *systemHandler) DepartmentHandler() DepartmentHandler { } func (h *systemHandler) ConfigHandler() ConfigHandler { - return NewConfigHandler(h.render, h.biz) + return NewConfigHandler(h.render, h.redis, h.biz) } func (h *systemHandler) AuditHandler() AuditHandler { @@ -66,3 +70,7 @@ func (h *systemHandler) AuditHandler() AuditHandler { func (h *systemHandler) LoginLogHandler() LoginLogHandler { return NewLoginLogHandler(h.render, h.biz) } + +func (h *systemHandler) CategoryHandler() CategoryHandler { + return NewCategoryHandler(h.render, h.biz) +} diff --git a/internal/erpserver/http.go b/internal/erpserver/http.go index e163e4e..5336309 100644 --- a/internal/erpserver/http.go +++ b/internal/erpserver/http.go @@ -44,16 +44,16 @@ func NewRouter(handler handler.IHandler, mw mw.IMiddleware) *chi.Mux { 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("/config", func(r chi.Router) { + r.Use(mw.Audit) + r.Get("/list", handler.SystemHandler().ConfigHandler().List) + r.Post("/list", handler.SystemHandler().ConfigHandler().List) + r.Get("/add", handler.SystemHandler().ConfigHandler().Add) + r.Get("/edit", handler.SystemHandler().ConfigHandler().Edit) + r.Post("/save", handler.SystemHandler().ConfigHandler().Save) + r.Post("/reset_pear", handler.SystemHandler().ConfigHandler().ResetPear) + r.Post("/refresh", handler.SystemHandler().ConfigHandler().Refresh) + }) r.Route("/department", func(r chi.Router) { r.Use(mw.Audit) @@ -80,8 +80,8 @@ func NewRouter(handler handler.IHandler, mw mw.IMiddleware) *chi.Mux { r.Post("/refresh", handler.SystemHandler().RoleHandler().Refresh) r.Post("/rebuild_parent_path", handler.SystemHandler().RoleHandler().RebuildParentPath) r.Post("/refresh_role_menus", handler.SystemHandler().RoleHandler().RefreshRoleMenus) - // r.Get("/set_menu", roleHandler.SetMenu) - // r.Post("/set_menu", roleHandler.PostSetMenu) + r.Get("/set_menu", handler.SystemHandler().RoleHandler().SetMenu) + r.Post("/set_menu", handler.SystemHandler().RoleHandler().SetMenu) }) r.Route("/user", func(r chi.Router) { @@ -117,21 +117,18 @@ func NewRouter(handler handler.IHandler, mw mw.IMiddleware) *chi.Mux { r.Post("/refresh_cache", handler.SystemHandler().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) - // }) + r.Route("/category", func(r chi.Router) { + r.Use(mw.Audit) + r.Get("/list", handler.SystemHandler().CategoryHandler().List) + r.Post("/list", handler.SystemHandler().CategoryHandler().List) + r.Get("/add", handler.SystemHandler().CategoryHandler().Add) + r.Get("/add_children", handler.SystemHandler().CategoryHandler().AddChildren) + r.Get("/edit", handler.SystemHandler().CategoryHandler().Edit) + r.Post("/save", handler.SystemHandler().CategoryHandler().Save) + r.Post("/tree", handler.SystemHandler().CategoryHandler().Tree) + r.Post("/refresh", handler.SystemHandler().CategoryHandler().Refresh) + r.Post("/rebuild_parent_path", handler.SystemHandler().CategoryHandler().RebuildParentPath) + }) }) }) diff --git a/management b/management index 2135aca..3267512 100755 Binary files a/management and b/management differ diff --git a/web/templates/manage/system/category/edit.tmpl b/web/templates/manage/system/category/edit.tmpl index 3904c1b..af75cca 100644 --- a/web/templates/manage/system/category/edit.tmpl +++ b/web/templates/manage/system/category/edit.tmpl @@ -26,7 +26,9 @@
上级
-
+
+ +
@@ -185,13 +187,13 @@ function getCategory() { $.ajax({ - url: "/system/category/xmselect", + url: "/system/category/tree?type=xmselect", type: 'post', dataType: 'json', headers: { 'X-CSRF-Token': $('#csrf_token').val() }, success: function (res) { xmSelect.render({ - el: '#ParentID', + el: '#categoryTree', // 工具栏 toolbar: { show: true diff --git a/web/templates/manage/system/category/list.tmpl b/web/templates/manage/system/category/list.tmpl index 35db5ea..63abb55 100644 --- a/web/templates/manage/system/category/list.tmpl +++ b/web/templates/manage/system/category/list.tmpl @@ -278,14 +278,14 @@ function getCategory() { $.ajax({ - url: "/system/category/dtree", + url: "/system/category/tree", type: 'post', dataType: 'json', headers: { 'X-CSRF-Token': $('#csrf_token').val() }, success: function (res) { tree.render({ elem: '#departTree', - data: res.data, + data: res, onlyIconControl: true, // 是否仅允许节点左侧图标控制展开收缩 showLine: true, click: function (obj) { diff --git a/web/templates/manage/system/config/edit.tmpl b/web/templates/manage/system/config/edit.tmpl index cd472af..864ef71 100644 --- a/web/templates/manage/system/config/edit.tmpl +++ b/web/templates/manage/system/config/edit.tmpl @@ -60,7 +60,7 @@
{{ submitBtn .AuthorizeMenus "save"}} -
@@ -81,9 +81,9 @@ {{define "js"}} -
-
-
-
-
- + + +
+
+
+ {{define "js"}} diff --git a/web/templates/manage/system/role/list.tmpl b/web/templates/manage/system/role/list.tmpl index 96c3ee1..0db1d41 100644 --- a/web/templates/manage/system/role/list.tmpl +++ b/web/templates/manage/system/role/list.tmpl @@ -232,7 +232,7 @@ type: 2, title: '为 ' + obj.data['display_name'] + ' 分配权限', shade: 0.1, - area: ['95%', '95%'], + area: ['99%', '98%'], content: "/system/role/set_menu?id=" + obj.data['id'] }); }