Ver código fonte

初始化基础模块

ZZH-wl 3 anos atrás
pai
commit
dfa3f6eb78
31 arquivos alterados com 3632 adições e 17 exclusões
  1. 25 0
      opms_admin/app/common/dao/casbin_rule.go
  2. 394 0
      opms_admin/app/common/dao/internal/casbin_rule.go
  3. 22 0
      opms_admin/app/common/global/cache_key.go
  4. 14 0
      opms_admin/app/common/model/casbin_rule.go
  5. 16 0
      opms_admin/app/common/model/internal/casbin_rule.go
  6. 20 0
      opms_admin/app/common/model/req.go
  7. 245 0
      opms_admin/app/common/service/cache.go
  8. 237 0
      opms_admin/app/common/service/casbin.go
  9. 45 6
      opms_admin/app/dao/internal/sys_user_group.go
  10. 44 0
      opms_admin/app/model/context.go
  11. 19 2
      opms_admin/app/model/internal/sys_user_group.go
  12. 29 1
      opms_admin/app/model/sys_config.go
  13. 30 1
      opms_admin/app/model/sys_dept.go
  14. 58 1
      opms_admin/app/model/sys_dict_data.go
  15. 34 1
      opms_admin/app/model/sys_dict_type.go
  16. 23 1
      opms_admin/app/model/sys_login.go
  17. 26 1
      opms_admin/app/model/sys_oper_log.go
  18. 27 1
      opms_admin/app/model/sys_post.go
  19. 22 1
      opms_admin/app/model/sys_role.go
  20. 107 1
      opms_admin/app/model/sys_user.go
  21. 34 0
      opms_admin/app/service/context.go
  22. 147 0
      opms_admin/app/service/sys_config.go
  23. 110 0
      opms_admin/app/service/sys_dept.go
  24. 159 0
      opms_admin/app/service/sys_dict_data.go
  25. 173 0
      opms_admin/app/service/sys_dict_type.go
  26. 100 0
      opms_admin/app/service/sys_login_log.go
  27. 172 0
      opms_admin/app/service/sys_oper_log.go
  28. 74 0
      opms_admin/app/service/sys_post.go
  29. 335 0
      opms_admin/app/service/sys_role.go
  30. 852 0
      opms_admin/app/service/sys_user.go
  31. 39 0
      opms_admin/app/service/sys_user_group.go

+ 25 - 0
opms_admin/app/common/dao/casbin_rule.go

@@ -0,0 +1,25 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package dao
+
+import (
+	"dashoo.cn/micro/app/common/dao/internal"
+)
+
+// casbinRuleDao is the manager for logic model data accessing
+// and custom defined data operations functions management. You can define
+// methods on it to extend its functionality as you wish.
+type casbinRuleDao struct {
+	internal.CasbinRuleDao
+}
+
+var (
+	// CasbinRule is globally public accessible object for table casbin_rule operations.
+	CasbinRule = casbinRuleDao{
+		internal.CasbinRule,
+	}
+)
+
+// Fill with you ideas below.

+ 394 - 0
opms_admin/app/common/dao/internal/casbin_rule.go

@@ -0,0 +1,394 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+import (
+	"context"
+	"dashoo.cn/micro/app/common/model"
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/frame/gmvc"
+	"time"
+)
+
+// CasbinRuleDao is the manager for logic model data accessing
+// and custom defined data operations functions management.
+type CasbinRuleDao struct {
+	gmvc.M
+	DB      gdb.DB
+	Table   string
+	Columns casbinRuleColumns
+}
+
+// CasbinRuleColumns defines and stores column names for table casbin_rule.
+type casbinRuleColumns struct {
+	Ptype string //
+	V0    string //
+	V1    string //
+	V2    string //
+	V3    string //
+	V4    string //
+	V5    string //
+}
+
+var (
+	// CasbinRule is globally public accessible object for table casbin_rule operations.
+	CasbinRule = CasbinRuleDao{
+		M:     g.DB("default").Model("casbin_rule").Safe(),
+		DB:    g.DB("default"),
+		Table: "casbin_rule",
+		Columns: casbinRuleColumns{
+			Ptype: "ptype",
+			V0:    "v0",
+			V1:    "v1",
+			V2:    "v2",
+			V3:    "v3",
+			V4:    "v4",
+			V5:    "v5",
+		},
+	}
+)
+
+// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
+// of current DB object and with given context in it.
+// Note that this returned DB object can be used only once, so do not assign it to
+// a global or package variable for long using.
+func (d *CasbinRuleDao) Ctx(ctx context.Context) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Ctx(ctx)}
+}
+
+// As sets an alias name for current table.
+func (d *CasbinRuleDao) As(as string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.As(as)}
+}
+
+// TX sets the transaction for current operation.
+func (d *CasbinRuleDao) TX(tx *gdb.TX) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.TX(tx)}
+}
+
+// Master marks the following operation on master node.
+func (d *CasbinRuleDao) Master() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Master()}
+}
+
+// Slave marks the following operation on slave node.
+// Note that it makes sense only if there's any slave node configured.
+func (d *CasbinRuleDao) Slave() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Slave()}
+}
+
+// Args sets custom arguments for model operation.
+func (d *CasbinRuleDao) Args(args ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Args(args...)}
+}
+
+// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *CasbinRuleDao) LeftJoin(table ...string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *CasbinRuleDao) RightJoin(table ...string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *CasbinRuleDao) InnerJoin(table ...string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *CasbinRuleDao) Fields(fieldNamesOrMapStruct ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Fields(fieldNamesOrMapStruct...)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *CasbinRuleDao) FieldsEx(fieldNamesOrMapStruct ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.FieldsEx(fieldNamesOrMapStruct...)}
+}
+
+// Option sets the extra operation option for the model.
+func (d *CasbinRuleDao) Option(option int) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (d *CasbinRuleDao) OmitEmpty() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (d *CasbinRuleDao) Filter() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter <where> can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (d *CasbinRuleDao) Where(where interface{}, args ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Where(where, args...)}
+}
+
+// WherePri does the same logic as M.Where except that if the parameter <where>
+// is a single condition like int/string/float/slice, it treats the condition as the primary
+// key value. That is, if primary key is "id" and given <where> parameter as "123", the
+// WherePri function treats the condition as "id=123", but M.Where treats the condition
+// as string "123".
+func (d *CasbinRuleDao) WherePri(where interface{}, args ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.WherePri(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (d *CasbinRuleDao) And(where interface{}, args ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (d *CasbinRuleDao) Or(where interface{}, args ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (d *CasbinRuleDao) Group(groupBy string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (d *CasbinRuleDao) Order(orderBy ...string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter <limit> can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (d *CasbinRuleDao) Limit(limit ...int) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (d *CasbinRuleDao) Offset(offset int) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter <page> is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (d *CasbinRuleDao) Page(page, limit int) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (d *CasbinRuleDao) Batch(batch int) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (d *CasbinRuleDao) Cache(duration time.Duration, name ...string) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (d *CasbinRuleDao) Data(data ...interface{}) *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*model.CasbinRule.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *CasbinRuleDao) All(where ...interface{}) ([]*model.CasbinRule, error) {
+	all, err := d.M.All(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*model.CasbinRule
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *model.CasbinRule.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *CasbinRuleDao) One(where ...interface{}) (*model.CasbinRule, error) {
+	one, err := d.M.One(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *model.CasbinRule
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by M.WherePri and M.One.
+// Also see M.WherePri and M.One.
+func (d *CasbinRuleDao) FindOne(where ...interface{}) (*model.CasbinRule, error) {
+	one, err := d.M.FindOne(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *model.CasbinRule
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindAll retrieves and returns Result by by M.WherePri and M.All.
+// Also see M.WherePri and M.All.
+func (d *CasbinRuleDao) FindAll(where ...interface{}) ([]*model.CasbinRule, error) {
+	all, err := d.M.FindAll(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*model.CasbinRule
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// Struct retrieves one record from table and converts it into given struct.
+// The parameter <pointer> should be type of *struct/**struct. If type **struct is given,
+// it can create the struct internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not nil.
+//
+// Eg:
+// user := new(User)
+// err  := dao.User.Where("id", 1).Struct(user)
+//
+// user := (*User)(nil)
+// err  := dao.User.Where("id", 1).Struct(&user)
+func (d *CasbinRuleDao) Struct(pointer interface{}, where ...interface{}) error {
+	return d.M.Struct(pointer, where...)
+}
+
+// Structs retrieves records from table and converts them into given struct slice.
+// The parameter <pointer> should be type of *[]struct/*[]*struct. It can create and fill the struct
+// slice internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not empty.
+//
+// Eg:
+// users := ([]User)(nil)
+// err   := dao.User.Structs(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Structs(&users)
+func (d *CasbinRuleDao) Structs(pointer interface{}, where ...interface{}) error {
+	return d.M.Structs(pointer, where...)
+}
+
+// Scan automatically calls Struct or Structs function according to the type of parameter <pointer>.
+// It calls function Struct if <pointer> is type of *struct/**struct.
+// It calls function Structs if <pointer> is type of *[]struct/*[]*struct.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved and given pointer is not empty or nil.
+//
+// Eg:
+// user  := new(User)
+// err   := dao.User.Where("id", 1).Scan(user)
+//
+// user  := (*User)(nil)
+// err   := dao.User.Where("id", 1).Scan(&user)
+//
+// users := ([]User)(nil)
+// err   := dao.User.Scan(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Scan(&users)
+func (d *CasbinRuleDao) Scan(pointer interface{}, where ...interface{}) error {
+	return d.M.Scan(pointer, where...)
+}
+
+// Chunk iterates the table with given size and callback function.
+func (d *CasbinRuleDao) Chunk(limit int, callback func(entities []*model.CasbinRule, err error) bool) {
+	d.M.Chunk(limit, func(result gdb.Result, err error) bool {
+		var entities []*model.CasbinRule
+		err = result.Structs(&entities)
+		if err == sql.ErrNoRows {
+			return false
+		}
+		return callback(entities, err)
+	})
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (d *CasbinRuleDao) LockUpdate() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (d *CasbinRuleDao) LockShared() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (d *CasbinRuleDao) Unscoped() *CasbinRuleDao {
+	return &CasbinRuleDao{M: d.M.Unscoped()}
+}

+ 22 - 0
opms_admin/app/common/global/cache_key.go

@@ -0,0 +1,22 @@
+package global
+
+const (
+	// CachePrefix 缓存前缀
+	CachePrefix = "cacheOv2_"
+
+	// SysAuthMenu 缓存菜单KEY
+	SysAuthMenu = CachePrefix + "sysAuthMenu"
+	// SysDict 字典缓存菜单KEY
+	SysDict = CachePrefix + "sysDict"
+	// SysRole 角色缓存key
+	SysRole = CachePrefix + "sysRole"
+	// SysWebSet 站点配置缓存key
+	SysWebSet = CachePrefix + "sysWebSet"
+
+	// SysAuthTag 权限缓存TAG标签
+	SysAuthTag = CachePrefix + "sysAuthTag"
+	// SysDictTag 字典缓存标签
+	SysDictTag = CachePrefix + "sysDictTag"
+	// SysConfigTag 系统参数配置
+	SysConfigTag = CachePrefix + "sysConfigTag"
+)

+ 14 - 0
opms_admin/app/common/model/casbin_rule.go

@@ -0,0 +1,14 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. Fill this file as you wish.
+// ==========================================================================
+
+package model
+
+import (
+	"dashoo.cn/micro/app/common/model/internal"
+)
+
+// CasbinRule is the golang structure for table casbin_rule.
+type CasbinRule internal.CasbinRule
+
+// Fill with you ideas below.

+ 16 - 0
opms_admin/app/common/model/internal/casbin_rule.go

@@ -0,0 +1,16 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+// CasbinRule is the golang structure for table casbin_rule.
+type CasbinRule struct {
+	Ptype string `orm:"ptype" json:"ptype"` //
+	V0    string `orm:"v0"    json:"v0"`    //
+	V1    string `orm:"v1"    json:"v1"`    //
+	V2    string `orm:"v2"    json:"v2"`    //
+	V3    string `orm:"v3"    json:"v3"`    //
+	V4    string `orm:"v4"    json:"v4"`    //
+	V5    string `orm:"v5"    json:"v5"`    //
+}

+ 20 - 0
opms_admin/app/common/model/req.go

@@ -0,0 +1,20 @@
+package model
+
+import "context"
+
+const PageSize = 10
+
+type PageReq struct {
+	BeginTime string          `p:"beginTime"` //开始时间
+	EndTime   string          `p:"endTime"`   //结束时间
+	PageNum   int             `p:"pageNum"`   //当前页码
+	PageSize  int             `p:"pageSize"`  //每页数
+	Ctx       context.Context `swaggerignore:"true"`
+	OrderBy   string          //排序方式
+}
+
+type UpFile struct {
+	Name string `json:"name"`
+	Url  string `json:"url"`
+	Uid  uint64 `json:"uid"`
+}

+ 245 - 0
opms_admin/app/common/service/cache.go

@@ -0,0 +1,245 @@
+package service
+
+import (
+	"context"
+	"dashoo.cn/micro/app/common/global"
+	"fmt"
+	"github.com/gogf/gcache-adapter/adapter"
+	"github.com/gogf/gf/crypto/gmd5"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gcache"
+	"github.com/gogf/gf/util/gconv"
+	"reflect"
+	"sync"
+	"time"
+)
+
+type cache struct {
+}
+
+type cacheTagService struct {
+	tagKey    interface{}
+	cache     *gcache.Cache
+	tagSetMux *sync.Mutex
+}
+
+var (
+	Cache     = new(cache)
+	userRedis = g.Cfg().GetBool("redis.open")
+	gChe      = gcache.New()
+)
+
+func (s *cache) New() *cacheTagService {
+	gChe.Ctx(context.Background())
+	if userRedis {
+		adapter := adapter.NewRedis(g.Redis())
+		gChe.SetAdapter(adapter)
+	}
+	return &cacheTagService{
+		cache:     gChe,
+		tagSetMux: new(sync.Mutex),
+	}
+}
+
+// 设置tag缓存的keys
+func (c *cacheTagService) cacheTagKey(key interface{}, tag interface{}) {
+	c.setTagKey(tag)
+	if c.tagKey != nil {
+		tagValue := []interface{}{key}
+		value, _ := c.cache.Get(c.tagKey)
+		if value != nil {
+			var keyValue []interface{}
+			//若是字符串
+			if kStr, ok := value.(string); ok {
+				js, err := gjson.DecodeToJson(kStr)
+				if err != nil {
+					g.Log().Error(err)
+					return
+				}
+				keyValue = gconv.SliceAny(js.Value())
+			} else {
+				keyValue = gconv.SliceAny(value)
+			}
+			for _, v := range keyValue {
+				if !reflect.DeepEqual(key, v) {
+					tagValue = append(tagValue, v)
+				}
+			}
+		}
+		c.cache.Set(c.tagKey, tagValue, 0)
+	}
+}
+
+// 获取带标签的键名
+func (c *cacheTagService) setTagKey(tag interface{}) {
+	if tag != nil {
+		c.tagKey = interface{}(fmt.Sprintf("%s_tag_%s", global.CachePrefix, gmd5.MustEncryptString(gconv.String(tag))))
+	}
+}
+
+// Set sets cache with <tagKey>-<value> pair, which is expired after <duration>.
+// It does not expire if <duration> <= 0.
+func (c *cacheTagService) Set(key interface{}, value interface{}, duration time.Duration, tag ...interface{}) {
+	c.tagSetMux.Lock()
+	if len(tag) > 0 {
+		c.cacheTagKey(key, tag[0])
+	}
+	err := c.cache.Set(key, value, duration)
+	if err != nil {
+		g.Log().Error(err)
+	}
+	c.tagSetMux.Unlock()
+}
+
+// SetIfNotExist sets cache with <tagKey>-<value> pair if <tagKey> does not exist in the cache,
+// which is expired after <duration>. It does not expire if <duration> <= 0.
+func (c *cacheTagService) SetIfNotExist(key interface{}, value interface{}, duration time.Duration, tag interface{}) bool {
+	c.tagSetMux.Lock()
+	defer c.tagSetMux.Unlock()
+	c.cacheTagKey(key, tag)
+	v, _ := c.cache.SetIfNotExist(key, value, duration)
+	return v
+}
+
+// Sets batch sets cache with tagKey-value pairs by <data>, which is expired after <duration>.
+//
+// It does not expire if <duration> <= 0.
+func (c *cacheTagService) Sets(data map[interface{}]interface{}, duration time.Duration, tag interface{}) {
+	c.tagSetMux.Lock()
+	if tag != nil {
+		for k, _ := range data {
+			c.cacheTagKey(k, tag)
+		}
+		c.cache.Sets(data, duration)
+	} else {
+		c.cache.Sets(data, duration)
+	}
+	c.tagSetMux.Unlock()
+}
+
+// Get returns the value of <tagKey>.
+// It returns nil if it does not exist or its value is nil.
+func (c *cacheTagService) Get(key interface{}) interface{} {
+	v, err := c.cache.Get(key)
+	if err != nil {
+		g.Log().Error(err)
+	}
+	return v
+}
+
+// GetOrSet returns the value of <tagKey>,
+// or sets <tagKey>-<value> pair and returns <value> if <tagKey> does not exist in the cache.
+// The tagKey-value pair expires after <duration>.
+//
+// It does not expire if <duration> <= 0.
+func (c *cacheTagService) GetOrSet(key interface{}, value interface{}, duration time.Duration, tag interface{}) interface{} {
+	c.tagSetMux.Lock()
+	defer c.tagSetMux.Unlock()
+	c.cacheTagKey(key, tag)
+	v, _ := c.cache.GetOrSet(key, value, duration)
+	return v
+}
+
+// GetOrSetFunc returns the value of <tagKey>, or sets <tagKey> with result of function <f>
+// and returns its result if <tagKey> does not exist in the cache. The tagKey-value pair expires
+// after <duration>. It does not expire if <duration> <= 0.
+func (c *cacheTagService) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration, tag interface{}) interface{} {
+	c.tagSetMux.Lock()
+	defer c.tagSetMux.Unlock()
+	c.cacheTagKey(key, tag)
+	v, _ := c.cache.GetOrSetFunc(key, f, duration)
+	return v
+}
+
+// GetOrSetFuncLock returns the value of <tagKey>, or sets <tagKey> with result of function <f>
+// and returns its result if <tagKey> does not exist in the cache. The tagKey-value pair expires
+// after <duration>. It does not expire if <duration> <= 0.
+//
+// Note that the function <f> is executed within writing mutex lock.
+func (c *cacheTagService) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration, tag interface{}) interface{} {
+	c.tagSetMux.Lock()
+	defer c.tagSetMux.Unlock()
+	c.cacheTagKey(key, tag)
+	v, _ := c.cache.GetOrSetFuncLock(key, f, duration)
+	return v
+}
+
+// Contains returns true if <tagKey> exists in the cache, or else returns false.
+func (c *cacheTagService) Contains(key interface{}) bool {
+	v, _ := c.cache.Contains(key)
+	return v
+}
+
+// Remove deletes the <tagKey> in the cache, and returns its value.
+func (c *cacheTagService) Remove(key interface{}) interface{} {
+	v, _ := c.cache.Remove(key)
+	return v
+}
+
+// Removes deletes <keys> in the cache.
+func (c *cacheTagService) Removes(keys []interface{}) {
+	c.cache.Remove(keys...)
+}
+
+// Remove deletes the <tag> in the cache, and returns its value.
+func (c *cacheTagService) RemoveByTag(tag interface{}) {
+	c.tagSetMux.Lock()
+	c.setTagKey(tag)
+	//删除tagKey 对应的 key和值
+	keys := c.Get(c.tagKey)
+	if keys != nil {
+		//如果是字符串
+		if kStr, ok := keys.(string); ok {
+			js, err := gjson.DecodeToJson(kStr)
+			if err != nil {
+				g.Log().Error(err)
+				return
+			}
+			ks := gconv.SliceAny(js.Value())
+			c.Removes(ks)
+		} else {
+			ks := gconv.SliceAny(keys)
+			c.Removes(ks)
+		}
+	}
+	c.Remove(c.tagKey)
+	c.tagSetMux.Unlock()
+}
+
+// Removes deletes <tags> in the cache.
+func (c *cacheTagService) RemoveByTags(tag []interface{}) {
+	for _, v := range tag {
+		c.RemoveByTag(v)
+	}
+}
+
+// Data returns a copy of all tagKey-value pairs in the cache as map type.
+func (c *cacheTagService) Data() map[interface{}]interface{} {
+	v, _ := c.cache.Data()
+	return v
+}
+
+// Keys returns all keys in the cache as slice.
+func (c *cacheTagService) Keys() []interface{} {
+	v, _ := c.cache.Keys()
+	return v
+}
+
+// KeyStrings returns all keys in the cache as string slice.
+func (c *cacheTagService) KeyStrings() []string {
+	v, _ := c.cache.KeyStrings()
+	return v
+}
+
+// Values returns all values in the cache as slice.
+func (c *cacheTagService) Values() []interface{} {
+	v, _ := c.cache.Values()
+	return v
+}
+
+// Size returns the size of the cache.
+func (c *cacheTagService) Size() int {
+	v, _ := c.cache.Size()
+	return v
+}

+ 237 - 0
opms_admin/app/common/service/casbin.go

@@ -0,0 +1,237 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/common/dao"
+	dbModel "dashoo.cn/micro/app/common/model"
+	"github.com/casbin/casbin/v2"
+	"github.com/casbin/casbin/v2/model"
+	"github.com/casbin/casbin/v2/persist"
+	"github.com/gogf/gf/frame/g"
+	"sync"
+)
+
+type cabin struct{}
+
+type adapterCasbin struct {
+	Enforcer    *casbin.SyncedEnforcer
+	EnforcerErr error
+}
+
+var (
+	Casbin = new(cabin)
+	once   sync.Once
+	m      sync.RWMutex
+	ac     *adapterCasbin
+)
+
+// GetEnforcer 获取adapter单例对象
+func (s *cabin) GetEnforcer() (enforcer *casbin.SyncedEnforcer, err error) {
+	once.Do(func() {
+		ac = s.newAdapter()
+	})
+	enforcer = ac.Enforcer
+	err = ac.EnforcerErr
+	return
+}
+
+// 初始化adapter操作
+func (s *cabin) newAdapter() (a *adapterCasbin) {
+	a = new(adapterCasbin)
+	a.initPolicy()
+	return
+}
+
+func (a *adapterCasbin) initPolicy() {
+	// Because the DB is empty at first,
+	// so we need to load the policy from the file adapter (.CSV) first.
+	e, err := casbin.NewSyncedEnforcer(g.Cfg().GetString("casbin.modelFile"),
+		g.Cfg().GetString("casbin.policyFile"))
+
+	if err != nil {
+		a.EnforcerErr = err
+		return
+	}
+
+	// This is a trick to save the current policy to the DB.
+	// We can't call e.SavePolicy() because the adapter in the enforcer is still the file adapter.
+	// The current policy means the policy in the Casbin enforcer (aka in memory).
+	//err = a.SavePolicy(e.GetModel())
+	//if err != nil {
+	//	return err
+	//}
+	//set adapter
+	e.SetAdapter(a)
+	// Clear the current policy.
+	e.ClearPolicy()
+	a.Enforcer = e
+	// Load the policy from DB.
+	err = a.LoadPolicy(e.GetModel())
+	if err != nil {
+		a.EnforcerErr = err
+		return
+	}
+}
+
+// SavePolicy saves policy to database.
+func (a *adapterCasbin) SavePolicy(model model.Model) (err error) {
+	err = a.dropTable()
+	if err != nil {
+		return
+	}
+	err = a.createTable()
+	if err != nil {
+		return
+	}
+	for ptype, ast := range model["p"] {
+		for _, rule := range ast.Policy {
+			line := savePolicyLine(ptype, rule)
+			_, err := dao.CasbinRule.Data(line).Insert()
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	for ptype, ast := range model["g"] {
+		for _, rule := range ast.Policy {
+			line := savePolicyLine(ptype, rule)
+			_, err := dao.CasbinRule.Data(line).Insert()
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return
+}
+
+func (a *adapterCasbin) dropTable() (err error) {
+	return
+}
+
+func (a *adapterCasbin) createTable() (err error) {
+	return
+}
+
+// LoadPolicy loads policy from database.
+func (a *adapterCasbin) LoadPolicy(model model.Model) error {
+	var lines []*dbModel.CasbinRule
+	if err := dao.CasbinRule.Scan(&lines); err != nil {
+		return err
+	}
+	for _, line := range lines {
+		loadPolicyLine(line, model)
+	}
+	return nil
+}
+
+// AddPolicy adds a policy rule to the storage.
+func (a *adapterCasbin) AddPolicy(sec string, ptype string, rule []string) error {
+	line := savePolicyLine(ptype, rule)
+	_, err := dao.CasbinRule.Data(line).Insert()
+	return err
+}
+
+// RemovePolicy removes a policy rule from the storage.
+func (a *adapterCasbin) RemovePolicy(sec string, ptype string, rule []string) error {
+	line := savePolicyLine(ptype, rule)
+	err := rawDelete(a, line)
+	return err
+}
+
+// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
+func (a *adapterCasbin) RemoveFilteredPolicy(sec string, ptype string,
+	fieldIndex int, fieldValues ...string) error {
+	line := &dbModel.CasbinRule{}
+	line.Ptype = ptype
+	if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) {
+		line.V0 = fieldValues[0-fieldIndex]
+	}
+	if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) {
+		line.V1 = fieldValues[1-fieldIndex]
+	}
+	if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) {
+		line.V2 = fieldValues[2-fieldIndex]
+	}
+	if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) {
+		line.V3 = fieldValues[3-fieldIndex]
+	}
+	if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) {
+		line.V4 = fieldValues[4-fieldIndex]
+	}
+	if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) {
+		line.V5 = fieldValues[5-fieldIndex]
+	}
+	err := rawDelete(a, line)
+	return err
+}
+
+func loadPolicyLine(line *dbModel.CasbinRule, model model.Model) {
+	lineText := line.Ptype
+	if line.V0 != "" {
+		lineText += ", " + line.V0
+	}
+	if line.V1 != "" {
+		lineText += ", " + line.V1
+	}
+	if line.V2 != "" {
+		lineText += ", " + line.V2
+	}
+	if line.V3 != "" {
+		lineText += ", " + line.V3
+	}
+	if line.V4 != "" {
+		lineText += ", " + line.V4
+	}
+	if line.V5 != "" {
+		lineText += ", " + line.V5
+	}
+	persist.LoadPolicyLine(lineText, model)
+}
+
+func savePolicyLine(ptype string, rule []string) *dbModel.CasbinRule {
+	line := &dbModel.CasbinRule{}
+	line.Ptype = ptype
+	if len(rule) > 0 {
+		line.V0 = rule[0]
+	}
+	if len(rule) > 1 {
+		line.V1 = rule[1]
+	}
+	if len(rule) > 2 {
+		line.V2 = rule[2]
+	}
+	if len(rule) > 3 {
+		line.V3 = rule[3]
+	}
+	if len(rule) > 4 {
+		line.V4 = rule[4]
+	}
+	if len(rule) > 5 {
+		line.V5 = rule[5]
+	}
+	return line
+}
+
+func rawDelete(a *adapterCasbin, line *dbModel.CasbinRule) error {
+	db := dao.CasbinRule.Where("ptype = ?", line.Ptype)
+	if line.V0 != "" {
+		db = db.Where("v0 = ?", line.V0)
+	}
+	if line.V1 != "" {
+		db = db.Where("v1 = ?", line.V1)
+	}
+	if line.V2 != "" {
+		db = db.Where("v2 = ?", line.V2)
+	}
+	if line.V3 != "" {
+		db = db.Where("v3 = ?", line.V3)
+	}
+	if line.V4 != "" {
+		db = db.Where("v4 = ?", line.V4)
+	}
+	if line.V5 != "" {
+		db = db.Where("v5 = ?", line.V5)
+	}
+	_, err := db.Delete()
+	return err
+}

+ 45 - 6
opms_admin/app/dao/internal/sys_user_group.go

@@ -26,8 +26,21 @@ type SysUserGroupDao struct {
 
 // SysUserGroupColumns defines and stores column names for table sys_user_group.
 type sysUserGroupColumns struct {
-	UserId  string // 用户ID
-	GroupId string // 用户组ID
+	GroupId     string // 用户组ID
+	UserId      string // 用户ID
+	GroupName   string // 组名称
+	Leader      string // 负责人
+	Phone       string // 联系电话
+	Email       string // 邮箱
+	Status      string // 状态(10正常20停用)
+	Remark      string // 备注
+	CreatedBy   string // 创建者
+	CreatedName string // 创建人
+	CreatedTime string // 创建时间
+	UpdatedBy   string // 更新者
+	UpdatedName string // 更新人
+	UpdatedTime string // 更新时间
+	DeletedTime string // 删除时间
 }
 
 var (
@@ -37,8 +50,21 @@ var (
 		DB:    g.DB("default"),
 		Table: "sys_user_group",
 		Columns: sysUserGroupColumns{
-			UserId:  "user_id",
-			GroupId: "group_id",
+			GroupId:     "group_id",
+			UserId:      "user_id",
+			GroupName:   "group_name",
+			Leader:      "leader",
+			Phone:       "phone",
+			Email:       "email",
+			Status:      "status",
+			Remark:      "remark",
+			CreatedBy:   "created_by",
+			CreatedName: "created_name",
+			CreatedTime: "created_time",
+			UpdatedBy:   "updated_by",
+			UpdatedName: "updated_name",
+			UpdatedTime: "updated_time",
+			DeletedTime: "deleted_time",
 		},
 	}
 )
@@ -50,8 +76,21 @@ func NewSysUserGroupDao(tenant string) SysUserGroupDao {
 		DB:    g.DB(tenant),
 		Table: "sys_user_group",
 		Columns: sysUserGroupColumns{
-			UserId:  "user_id",
-			GroupId: "group_id",
+			GroupId:     "group_id",
+			UserId:      "user_id",
+			GroupName:   "group_name",
+			Leader:      "leader",
+			Phone:       "phone",
+			Email:       "email",
+			Status:      "status",
+			Remark:      "remark",
+			CreatedBy:   "created_by",
+			CreatedName: "created_name",
+			CreatedTime: "created_time",
+			UpdatedBy:   "updated_by",
+			UpdatedName: "updated_name",
+			UpdatedTime: "updated_time",
+			DeletedTime: "deleted_time",
 		},
 	}
 	return dao

+ 44 - 0
opms_admin/app/model/context.go

@@ -0,0 +1,44 @@
+package model
+
+import (
+	"dashoo.cn/micro/app/dao"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+)
+
+const (
+	// CtxKey 上下文变量存储键名,前后端系统共享
+	CtxKey = "GFastContext"
+)
+
+// Context 请求上下文结构
+type Context struct {
+	Session *ghttp.Session // 当前Session管理对象
+	User    *CtxUser       // 上下文用户信息
+	Data    g.Map          // 自定KV变量,业务模块根据需要设置,不固定
+}
+
+// CtxUser 请求上下文中的用户信息
+type CtxUser struct {
+	Id           uint64 `json:"id"`           // 用户id
+	UserName     string `json:"userName"`     // 用户名
+	DeptId       uint64 `json:"deptId"`       // 部门id
+	UserNickname string `json:"userNickname"` // 用户昵称
+	UserStatus   uint   `json:"userStatus"`   // 用户状态;0:禁用,1:正常,2:未验证
+	IsAdmin      int    `json:"isAdmin"`      // 是否后台管理员 1 是  0   否
+	Avatar       string `json:"avatar"`       //头像
+}
+
+// GetUserId 获取登录用户id
+func (ctxUser *CtxUser) GetUserId() (id uint64) {
+	return ctxUser.Id
+}
+
+// GetDept 获取登录用户所属部门
+func (ctxUser *CtxUser) GetDept() (err error, dept *SysDept) {
+	err = g.DB().Model(dao.SysDept.Table).Fields(dao.SysDept.Columns.Id, dao.SysDept.Columns.DeptName).WherePri(ctxUser.DeptId).Scan(&dept)
+	if dept == nil {
+		dept = &SysDept{}
+	}
+	return
+}

+ 19 - 2
opms_admin/app/model/internal/sys_user_group.go

@@ -4,8 +4,25 @@
 
 package internal
 
+import (
+	"github.com/gogf/gf/os/gtime"
+)
+
 // SysUserGroup is the golang structure for table sys_user_group.
 type SysUserGroup struct {
-	UserId  int `orm:"user_id,primary"  json:"userId"`  // 用户ID
-	GroupId int `orm:"group_id,primary" json:"groupId"` // 用户组ID
+	GroupId     int         `orm:"group_id,primary" json:"groupId"`     // 用户组ID
+	UserId      int         `orm:"user_id,primary"  json:"userId"`      // 用户ID
+	GroupName   string      `orm:"group_name"       json:"groupName"`   // 组名称
+	Leader      string      `orm:"leader"           json:"leader"`      // 负责人
+	Phone       string      `orm:"phone"            json:"phone"`       // 联系电话
+	Email       string      `orm:"email"            json:"email"`       // 邮箱
+	Status      string      `orm:"status"           json:"status"`      // 状态(10正常20停用)
+	Remark      string      `orm:"remark"           json:"remark"`      // 备注
+	CreatedBy   int         `orm:"created_by"       json:"createdBy"`   // 创建者
+	CreatedName string      `orm:"created_name"     json:"createdName"` // 创建人
+	CreatedTime *gtime.Time `orm:"created_time"     json:"createdTime"` // 创建时间
+	UpdatedBy   int         `orm:"updated_by"       json:"updatedBy"`   // 更新者
+	UpdatedName string      `orm:"updated_name"     json:"updatedName"` // 更新人
+	UpdatedTime *gtime.Time `orm:"updated_time"     json:"updatedTime"` // 更新时间
+	DeletedTime *gtime.Time `orm:"deleted_time"     json:"deletedTime"` // 删除时间
 }

+ 29 - 1
opms_admin/app/model/sys_config.go

@@ -5,10 +5,38 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysConfig is the golang structure for table sys_config.
 type SysConfig internal.SysConfig
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// 分页请求参数
+type SysConfigSearchReq struct {
+	ConfigName string `p:"configName"` //参数名称
+	ConfigKey  string `p:"configKey"`  //参数键名
+	ConfigType string `p:"configType"` //状态
+	BeginTime  string `p:"beginTime"`  //开始时间
+	EndTime    string `p:"endTime"`    //结束时间
+	comModel.PageReq
+}
+
+// 新增页面请求参数
+type SysConfigAddReq struct {
+	ConfigName  string `p:"configName"  v:"required#参数名称不能为空"`
+	ConfigKey   string `p:"configKey"  v:"required#参数键名不能为空"`
+	ConfigValue string `p:"configValue"  v:"required#参数键值不能为空"`
+	ConfigType  int    `p:"configType"    v:"required|in:0,1#系统内置不能为空|系统内置类型只能为0或1"`
+	Remark      string `p:"remark"`
+	CreateBy    uint64
+}
+
+// 修改页面请求参数
+type SysConfigEditReq struct {
+	ConfigId int64 `p:"configId" v:"required|min:1#主键ID不能为空|主键ID参数错误"`
+	UpdateBy uint64
+	SysConfigAddReq
+}

+ 30 - 1
opms_admin/app/model/sys_dept.go

@@ -11,4 +11,33 @@ import (
 // SysDept is the golang structure for table sys_dept.
 type SysDept internal.SysDept
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+type SysDeptSearchParams struct {
+	ExcludeId int    `p:"excludeId"`
+	DeptName  string `p:"deptName"`
+	Status    string `p:"status"`
+}
+
+type SysDeptTreeRes struct {
+	*SysDept
+	Children []*SysDeptTreeRes `json:"children"`
+}
+
+type SysDeptAddParams struct {
+	ParentID  int    `json:"parentId" orm:"parent_id"  p:"parentId"  v:"required#父级不能为空"`
+	DeptName  string `json:"deptName" orm:"dept_name" p:"deptName"  v:"required#部门名称不能为空"`
+	OrderNum  int    `json:"orderNum" orm:"order_num"  p:"orderNum"  v:"required#排序不能为空"`
+	Leader    string `json:"leader" orm:"leader" p:"leader"  v:"required#负责人不能为空"`
+	Phone     string `json:"phone" orm:"Phone" p:"phone"  v:"required#电话不能为空"`
+	Email     string `json:"email" orm:"email" p:"email"  v:"required#邮箱不能为空"`
+	Status    string `json:"status" orm:"status" p:"status"  v:"required#状态必须"`
+	Ancestors string `json:"ancestors" p:"ancestors" orm:"ancestors"`
+	CreatedBy uint64 `json:"CreatedBy"  orm:"created_by"`
+}
+
+type EditParams struct {
+	DeptID int64 `json:"deptId" orm:"dept_id" p:"id" v:"integer|min:1#ID只能为整数|ID只能为正数"`
+	SysDeptAddParams
+	UpdatedBy uint64 `json:"updatedBy" orm:"updated_by"`
+}

+ 58 - 1
opms_admin/app/model/sys_dict_data.go

@@ -5,10 +5,67 @@
 package model
 
 import (
+	"context"
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysDictData is the golang structure for table sys_dict_data.
 type SysDictData internal.SysDictData
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// SelectDictPageReq 分页请求参数
+type SelectDictPageReq struct {
+	DictType  string `p:"dictType"`  //字典类型
+	DictLabel string `p:"dictLabel"` //字典标签
+	Status    string `p:"status"`    //状态
+	comModel.PageReq
+}
+
+// GetDictReq 获取字典信息请求参数
+type GetDictReq struct {
+	DictType     string `p:"dictType" v:"required#字典类型不能为空"`
+	DefaultValue string `p:"defaultValue"`
+	Ctx          context.Context
+}
+
+// DictRes 完整的一个字典信息
+type DictRes struct {
+	Info   *DictTypeRes   `json:"info"`
+	Values []*DictDataRes `json:"values"`
+}
+
+type DictTypeRes struct {
+	DictName string `json:"name"`
+	Remark   string `json:"remark"`
+}
+
+// DictDataRes 字典数据
+type DictDataRes struct {
+	DictValue string `json:"key"`
+	DictLabel string `json:"value"`
+	IsDefault int    `json:"isDefault"`
+	Remark    string `json:"remark"`
+}
+
+// DictDataAddReq 新增操作请求参数
+type DictDataAddReq struct {
+	DictLabel string `p:"dictLabel"  v:"required#字典标签不能为空"`
+	DictValue string `p:"dictValue"  v:"required#字典键值不能为空"`
+	DictType  string `p:"dictType"  v:"required#字典类型不能为空"`
+	DictSort  int    `p:"dictSort"  v:"integer#排序只能为整数"`
+	CssClass  string `p:"cssClass"`
+	ListClass string `p:"listClass"`
+	IsDefault int    `p:"isDefault" v:"required|in:0,1#系统默认不能为空|默认值只能为0或1"`
+	Status    int    `p:"status"    v:"required|in:0,1#状态不能为空|状态只能为0或1"`
+	Remark    string `p:"remark"`
+	CreateBy  uint64
+}
+
+// EditDictDataReq 修改字典数据操作请求参数
+type EditDictDataReq struct {
+	DictCode int `p:"dictCode" v:"required|min:1#主键ID不能为空|主键ID不能小于1"`
+	UpdateBy uint64
+	DictDataAddReq
+}

+ 34 - 1
opms_admin/app/model/sys_dict_type.go

@@ -5,10 +5,43 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
+	"github.com/gogf/gf/os/gtime"
 )
 
 // SysDictType is the golang structure for table sys_dict_type.
 type SysDictType internal.SysDictType
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+type ListSysDictTypeReq struct {
+	DictName string `p:"dictName"` //字典名称
+	DictType string `p:"dictType"` //字典类型
+	Status   string `p:"status"`   //字典状态
+	comModel.PageReq
+}
+
+// SysDictTypeAddReq 新增操作请求参数
+type SysDictTypeAddReq struct {
+	DictName string `p:"dictName"  v:"required#字典名称不能为空"`
+	DictType string `p:"dictType"  v:"required#字典类型不能为空"`
+	Status   uint   `p:"status"  v:"required|in:0,1#状态不能为空|状态只能为0或1"`
+	Remark   string `p:"remark"`
+	CreateBy uint64
+}
+
+type SysDictTypeEditReq struct {
+	SysDictTypeAddReq
+	DictId   int64 `p:dictId v:required|min:1#主键ID不能为空|主键ID必须为大于0的值`
+	UpdateBy uint64
+}
+
+type SysDictTypeInfoRes struct {
+	DictId    uint64      `orm:"dict_id,primary"  json:"dictId"`    // 字典主键
+	DictName  string      `orm:"dict_name"        json:"dictName"`  // 字典名称
+	DictType  string      `orm:"dict_type,unique" json:"dictType"`  // 字典类型
+	Status    uint        `orm:"status"           json:"status"`    // 状态(0正常 1停用)
+	Remark    string      `orm:"remark"           json:"remark"`    // 备注
+	CreatedAt *gtime.Time `orm:"created_at"       json:"createdAt"` // 创建日期
+}

+ 23 - 1
opms_admin/app/model/sys_login.go

@@ -5,10 +5,32 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysLogin is the golang structure for table sys_login.
 type SysLogin internal.SysLogin
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// LoginLogParams 登录日志写入参数
+type LoginLogParams struct {
+	Status    int
+	Username  string
+	Ip        string
+	UserAgent string
+	Msg       string
+	Module    string
+}
+
+// SysLoginLogSearchReq 查询列表请求参数
+type SysLoginLogSearchReq struct {
+	LoginName     string `p:"userName"`      //登陆名
+	Status        string `p:"status"`        //状态
+	Ipaddr        string `p:"ipaddr"`        //登录地址
+	SortName      string `p:"orderByColumn"` //排序字段
+	SortOrder     string `p:"isAsc"`         //排序方式
+	LoginLocation string `p:"loginLocation"` //登录地点
+	comModel.PageReq
+}

+ 26 - 1
opms_admin/app/model/sys_oper_log.go

@@ -5,10 +5,35 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
+	"github.com/gogf/gf/frame/g"
+	"net/url"
 )
 
 // SysOperLog is the golang structure for table sys_oper_log.
 type SysOperLog internal.SysOperLog
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// SysOperLogAdd 添加操作日志参数
+type SysOperLogAdd struct {
+	User         *CtxUser
+	Menu         *SysMenu
+	Url          *url.URL
+	Params       g.Map
+	Method       string
+	ClientIp     string
+	OperatorType int
+}
+
+// SysOperLogSearchReq 查询列表请求参数
+type SysOperLogSearchReq struct {
+	Title         string `p:"title"`         //系统模块
+	OperName      string `p:"operName"`      //操作人员
+	Status        string `p:"status"`        //操作状态
+	SortName      string `p:"orderByColumn"` //排序字段
+	SortOrder     string `p:"isAsc"`         //排序方式
+	RequestMethod string `p:"requestMethod"` //操作类型
+	comModel.PageReq
+}

+ 27 - 1
opms_admin/app/model/sys_post.go

@@ -5,10 +5,36 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysPost is the golang structure for table sys_post.
 type SysPost internal.SysPost
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// SysPostSearchParams 搜索参数
+type SysPostSearchParams struct {
+	PostCode string `p:"postCode"` //岗位编码
+	PostName string `p:"postName"` //岗位名称
+	Status   string `p:"status"`   //状态
+	comModel.PageReq
+}
+
+// SysPostAddParams 添加岗位参数
+type SysPostAddParams struct {
+	PostCode  string `p:"postCode" v:"required#岗位编码不能为空"`
+	PostName  string `p:"postName" v:"required#岗位名称不能为空"`
+	PostSort  int    `p:"postSort" v:"required#岗位排序不能为空"`
+	Status    string `p:"status" v:"required#状态不能为空"`
+	Remark    string `p:"remark"`
+	CreatedBy uint64
+}
+
+// SysPostEditParams 修改岗位参数
+type SysPostEditParams struct {
+	PostId int64 `p:"postId" v:"required#id必须"`
+	SysPostAddParams
+	UpdatedBy uint64
+}

+ 22 - 1
opms_admin/app/model/sys_role.go

@@ -5,10 +5,31 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysRole is the golang structure for table sys_role.
 type SysRole internal.SysRole
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// 分页请求参数
+type SelectPageReq struct {
+	RoleName string `p:"roleName"` //参数名称
+	Status   string `p:"status"`   //状态
+	comModel.PageReq
+}
+
+// 修改状态参数
+type StatusSetReq struct {
+	RoleId uint `p:"roleId" v:"required#角色ID不能为空"`
+	Status uint `p:"status" v:"required#状态不能为空"`
+}
+
+// 角色数据授权参数
+type DataScopeReq struct {
+	RoleId    uint   `p:"roleId" v:"required#角色ID不能为空"`
+	DataScope uint   `p:"dataScope" v:"required#权限范围不能为空"`
+	DeptIds   []uint `p:"deptIds"`
+}

+ 107 - 1
opms_admin/app/model/sys_user.go

@@ -5,10 +5,116 @@
 package model
 
 import (
+	comModel "dashoo.cn/micro/app/common/model"
 	"dashoo.cn/micro/app/model/internal"
 )
 
 // SysUser is the golang structure for table sys_user.
 type SysUser internal.SysUser
 
-// Fill with you ideas below.
+// Fill with you ideas below.
+
+// LoginParamsReq 登陆参数
+type LoginParamsReq struct {
+	Username   string `p:"username" v:"required#用户名不能为空"`
+	Password   string `p:"password" v:"required#密码不能为空"`
+	VerifyCode string `p:"verifyCode" v:"required#验证码不能为空"`
+	VerifyKey  string `p:"verifyKey"`
+}
+
+// LoginUserRes 登录返回
+type LoginUserRes struct {
+	Id           uint64 `orm:"id,primary"       json:"id"`           //
+	UserName     string `orm:"user_name,unique" json:"userName"`     // 用户名
+	UserNickname string `orm:"user_nickname"    json:"userNickname"` // 用户昵称
+	UserPassword string `orm:"user_password"    json:"userPassword"` // 登录密码;cmf_password加密
+	UserSalt     string `orm:"user_salt"        json:"userSalt"`     // 加密盐
+	UserStatus   uint   `orm:"user_status"      json:"userStatus"`   // 用户状态;0:禁用,1:正常,2:未验证
+	IsAdmin      int    `orm:"is_admin"         json:"isAdmin"`      // 是否后台管理员 1 是  0   否
+	Avatar       string `orm:"avatar" json:"avatar"`                 //头像
+	DeptId       uint64 `orm:"dept_id"       json:"deptId"`          //部门id
+}
+
+// SysUserSearchReq 用户搜索请求参数
+type SysUserSearchReq struct {
+	DeptId      string  `p:"deptId"` //部门id
+	DeptIds     []int64 //所属部门id数据
+	Phonenumber string  `p:"phonenumber"`
+	Status      string  `p:"status"`
+	KeyWords    string  `p:"userName"`
+	comModel.PageReq
+}
+
+// SetUserReq 添加修改用户公用请求字段
+type SetUserReq struct {
+	DeptId      uint64  `p:"deptId" v:"required#用户部门不能为空"` //所属部门
+	Email       string  `p:"email" v:"email#邮箱格式错误"`       //邮箱
+	NickName    string  `p:"nickName" v:"required#用户昵称不能为空"`
+	Phonenumber string  `p:"phonenumber" v:"required|phone#手机号不能为空|手机号格式错误"`
+	PostIds     []int64 `p:"postIds"`
+	Remark      string  `p:"remark"`
+	RoleIds     []int64 `p:"roleIds"`
+	Sex         int     `p:"sex"`
+	Status      uint    `p:"status"`
+	IsAdmin     int     `p:"isAdmin"` // 是否后台管理员 1 是  0   否
+}
+
+// AddUserReq 添加用户参数
+type AddUserReq struct {
+	SetUserReq
+	UserName string `p:"userName" v:"required#用户账号不能为空"`
+	Password string `p:"password" v:"required|password#密码不能为空|密码以字母开头,只能包含字母、数字和下划线,长度在6~18之间"`
+	UserSalt string
+}
+
+type EditUserReq struct {
+	SetUserReq
+	UserId int `p:"userId" v:"required#用户id不能为空"`
+}
+
+type SysUserRoleDeptRes struct {
+	*SysUser
+	Dept     *SysDept `json:"dept"`
+	RoleInfo []*struct {
+		RoleId uint   `json:"roleId"`
+		Name   string `json:"name"`
+	} `json:"roleInfo"`
+	Post []*struct {
+		PostId   int64  `json:"postId"`
+		PostName string `json:"postName"`
+	} `json:"post"`
+}
+
+// SysUserResetPwdReq 重置用户密码状态参数
+type SysUserResetPwdReq struct {
+	Id       uint64 `p:"userId" v:"required#用户id不能为空"`
+	Password string `p:"password" v:"required|password#密码不能为空|密码以字母开头,只能包含字母、数字和下划线,长度在6~18之间"`
+}
+
+// SysUserStatusReq 设置用户状态参数
+type SysUserStatusReq struct {
+	Id         uint64 `p:"userId" v:"required#用户id不能为空"`
+	UserStatus uint   `p:"status" v:"required#用户状态不能为空"`
+}
+
+// ProfileUpReq 个人中心修改用户信息参数
+type ProfileUpReq struct {
+	UserId       uint64
+	UserNickname string `p:"userNickname" v:"required#用户昵称不能为空" orm:"user_nickname"` // 用户昵称
+	Mobile       string `p:"mobile" v:"required|phone#手机号不能为空|手机号格式错误" orm:"mobile,unique"`
+	UserEmail    string `p:"userEmail" v:"email#邮箱格式错误" orm:"user_email"`
+	Sex          int    `p:"sex" orm:"sex"`
+}
+
+// ProfileUpdatePwdReq 个人中心修改用户密码
+type ProfileUpdatePwdReq struct {
+	UserId      uint64
+	OldPassword string `p:"oldPassword" v:"required#旧密码不能为空"`
+	NewPassword string `p:"newPassword" v:"required#新密码不能为空"`
+}
+
+// SysUserRes 用于查询用户信息对象
+type SysUserRes struct {
+	Id           uint64 `json:"id" orm:"id"`
+	UserNickname string `json:"userNickname" orm:"user_nickname"`
+}

+ 34 - 0
opms_admin/app/service/context.go

@@ -0,0 +1,34 @@
+package service
+
+import (
+	"context"
+	"dashoo.cn/micro/app/model"
+	"github.com/gogf/gf/net/ghttp"
+)
+
+// Context 上下文管理服务
+var Context = new(contextService)
+
+type contextService struct{}
+
+// Init 初始化上下文对象指针到上下文对象中,以便后续的请求流程中可以修改。
+func (s *contextService) Init(r *ghttp.Request, customCtx *model.Context) {
+	r.SetCtxVar(model.CtxKey, customCtx)
+}
+
+// Get 获得上下文变量,如果没有设置,那么返回nil
+func (s *contextService) Get(ctx context.Context) *model.Context {
+	value := ctx.Value(model.CtxKey)
+	if value == nil {
+		return nil
+	}
+	if localCtx, ok := value.(*model.Context); ok {
+		return localCtx
+	}
+	return nil
+}
+
+// SetUser 将上下文信息设置到上下文请求中,注意是完整覆盖
+func (s *contextService) SetUser(ctx context.Context, ctxUser *model.CtxUser) {
+	s.Get(ctx).User = ctxUser
+}

+ 147 - 0
opms_admin/app/service/sys_config.go

@@ -0,0 +1,147 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/common/global"
+	comModel "dashoo.cn/micro/app/common/model"
+	comService "dashoo.cn/micro/app/common/service"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type sysConfig struct {
+}
+
+var SysConfig = new(sysConfig)
+
+func (s *sysConfig) SelectListByPage(req *model.SysConfigSearchReq) (total, page int, list []*model.SysConfig, err error) {
+	m := dao.SysConfig.Ctx(req.Ctx)
+	if req != nil {
+		if req.ConfigName != "" {
+			m = m.Where("config_name like ?", "%"+req.ConfigName+"%")
+		}
+		if req.ConfigType != "" {
+			m = m.Where("config_type = ", gconv.Int(req.ConfigType))
+		}
+		if req.ConfigKey != "" {
+			m = m.Where("config_key like ?", "%"+req.ConfigKey+"%")
+		}
+		if req.BeginTime != "" {
+			m = m.Where("create_time >= ? ", req.BeginTime)
+		}
+
+		if req.EndTime != "" {
+			m = m.Where("create_time<=?", req.EndTime)
+		}
+	}
+	total, err = m.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	page = req.PageNum
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	err = m.Page(page, req.PageSize).Order("config_id asc").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+		return
+	}
+	return
+}
+
+// CheckConfigKeyUniqueAll 验证参数键名是否存在
+func (s *sysConfig) CheckConfigKeyUniqueAll(configKey string) error {
+	entity, err := dao.SysConfig.Fields(dao.SysConfig.Columns.Id).FindOne(dao.SysConfig.Columns.ConfigKey, configKey)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("校验数据失败")
+	}
+	if entity != nil {
+		return gerror.New("参数键名已经存在")
+	}
+	return nil
+}
+
+// AddSave 添加操作
+func (s *sysConfig) AddSave(req *model.SysConfigAddReq) (err error) {
+	_, err = dao.SysConfig.Insert(req)
+	return
+}
+
+func (s *sysConfig) GetById(id int) (data *model.SysConfig, err error) {
+	err = dao.SysConfig.WherePri(id).Scan(&data)
+	return
+}
+
+// CheckConfigKeyUnique 检查键是否已经存在
+func (s *sysConfig) CheckConfigKeyUnique(configKey string, configId int64) error {
+	entity, err := dao.SysConfig.Fields(dao.SysConfig.Columns.Id).
+		FindOne(dao.SysConfig.Columns.ConfigKey+"=? and "+dao.SysConfig.Columns.Id+"!=?",
+			configKey, configId)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("校验数据失败")
+	}
+	if entity != nil {
+		return gerror.New("参数键名已经存在")
+	}
+	return nil
+}
+
+// EditSave 修改系统参数
+func (s *sysConfig) EditSave(req *model.SysConfigEditReq) (err error) {
+	_, err = dao.SysConfig.FieldsEx(dao.SysConfig.Columns.Id, dao.SysConfig.Columns.CreatedBy).
+		WherePri(req.ConfigId).Data(req).Update()
+	return
+}
+
+// DeleteByIds 删除
+func (s *sysConfig) DeleteByIds(ids []int) error {
+	_, err := dao.SysConfig.Delete(dao.SysConfig.Columns.Id+" in (?)", ids)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除失败")
+	}
+	return nil
+}
+
+// GetConfigByKey 通过key获取参数(从缓存获取)
+func (s *sysConfig) GetConfigByKey(key string) (config *model.SysConfig, err error) {
+	if key == "" {
+		err = gerror.New("参数key不能为空")
+		return
+	}
+	cache := comService.Cache.New()
+	cf := cache.Get(global.SysConfigTag + key)
+	if cf != nil {
+		err = gconv.Struct(cf, &config)
+		return
+	}
+	config, err = s.GetByKey(key)
+	if err != nil {
+		return
+	}
+	if config != nil {
+		cache.Set(global.SysConfigTag+key, config, 0, global.SysConfigTag)
+	}
+	return
+}
+
+// GetByKey 通过key获取参数(从数据库获取)
+func (s *sysConfig) GetByKey(key string) (config *model.SysConfig, err error) {
+	err = dao.SysConfig.Where("config_key", key).Scan(&config)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取配置失败")
+	}
+	return
+}

+ 110 - 0
opms_admin/app/service/sys_dept.go

@@ -0,0 +1,110 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+)
+
+type dept struct {
+}
+
+var Dept = new(dept)
+
+func (s *dept) GetList(searchParams *model.SysDeptSearchParams) ([]*model.SysDept, error) {
+	deptModel := dao.SysDept.M
+	if searchParams.DeptName != "" {
+		deptModel = deptModel.Where("dept_name like ?", "%"+searchParams.DeptName+"%")
+	}
+	if searchParams.Status != "" {
+		deptModel = deptModel.Where("status", searchParams.Status)
+	}
+	depts := ([]*model.SysDept)(nil)
+	if err := deptModel.Scan(&depts); err != nil {
+		return nil, err
+	}
+	list := make([]*model.SysDept, 0, len(depts))
+	if searchParams.ExcludeId != 0 {
+		for _, v := range depts {
+			if searchParams.ExcludeId != v.Id {
+				list = append(list, v)
+			}
+		}
+		return list, nil
+	} else {
+		return depts, nil
+	}
+}
+
+func (s *dept) GetRoleDepts(roleId int) ([]int, error) {
+	var entitys []*model.SysRoleDept
+	err := dao.SysRoleDept.Where("role_id", roleId).Scan(&entitys)
+	if err != nil {
+		return nil, err
+	}
+	result := make([]int, 0)
+	for _, v := range entitys {
+		result = append(result, v.DeptId)
+	}
+	return result, nil
+}
+
+func (s *dept) FindSonByParentId(depts []*model.SysDept, deptId int) []*model.SysDept {
+	children := make([]*model.SysDept, 0, len(depts))
+	for _, v := range depts {
+		if v.ParentId == deptId {
+			children = append(children, v)
+			fChildren := s.FindSonByParentId(depts, v.Id)
+			children = append(children, fChildren...)
+		}
+	}
+	return children
+}
+
+func (s *dept) GetDeptListTree(pid int, list []*model.SysDept) []*model.SysDeptTreeRes {
+	tree := make([]*model.SysDeptTreeRes, 0, len(list))
+	for _, v := range list {
+		if v.ParentId == pid {
+			t := &model.SysDeptTreeRes{
+				SysDept: v,
+			}
+			child := s.GetDeptListTree(v.Id, list)
+			if len(child) > 0 {
+				t.Children = child
+			}
+			tree = append(tree, t)
+		}
+	}
+	return tree
+}
+
+func (s *dept) AddDept(params *model.SysDeptAddParams) (err error) {
+	_, err = dao.SysDept.Insert(params)
+	return
+}
+
+func (s *dept) GetDeptById(id uint64) (dept *model.SysDept, err error) {
+	err = dao.SysDept.Where("dept_id", id).Scan(&dept)
+	return
+}
+
+func (s *dept) EditDept(params *model.EditParams) error {
+	_, err := dao.SysDept.FieldsEx(dao.SysDept.Columns.Id, dao.SysDept.Columns.CreatedBy).WherePri(params.DeptID).
+		Update(params)
+	return err
+}
+
+func (s *dept) DelDept(id int) error {
+	var list []*model.SysDept
+	err := dao.SysDept.Scan(&list)
+	if err != nil {
+		return err
+	}
+	children := s.FindSonByParentId(list, id)
+	ids := make([]int, 0, len(list))
+	for _, v := range children {
+		ids = append(ids, v.Id)
+	}
+	ids = append(ids, id)
+	_, err = dao.SysDept.Where(dao.SysDept.Columns.Id+" in (?)", ids).Delete()
+	return err
+}

+ 159 - 0
opms_admin/app/service/sys_dict_data.go

@@ -0,0 +1,159 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/common/global"
+	comModel "dashoo.cn/micro/app/common/model"
+	comService "dashoo.cn/micro/app/common/service"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"database/sql"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type sysDictData struct{}
+
+var SysDictData = new(sysDictData)
+
+func (s sysDictData) DictDataList(req *model.SelectDictPageReq) (total, page int, list []*model.SysDictData, err error) {
+	d := dao.SysDictData.Ctx(req.Ctx)
+	if req != nil {
+		if req.DictLabel != "" {
+			d = d.Where(dao.SysDictData.Columns.DictLabel+" like ?", "%"+req.DictLabel+"%")
+		}
+		if req.Status != "" {
+			d = d.Where(dao.SysDictData.Columns.Status+" = ", gconv.Int(req.Status))
+		}
+		if req.DictType != "" {
+			d = d.Where(dao.SysDictData.Columns.DictType+" = ?", req.DictType)
+		}
+		total, err = d.Count()
+		if err != nil {
+			g.Log().Error(err)
+			err = gerror.New("获取总行数失败")
+			return
+		}
+		if req.PageNum == 0 {
+			req.PageNum = 1
+		}
+	}
+	page = req.PageNum
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	list, err = d.Page(page, req.PageSize).Order(dao.SysDictData.Columns.DictSort + " asc," +
+		dao.SysDictData.Columns.DictCode + " asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+		return
+	}
+	return
+}
+
+// GetDictWithDataByType 通过字典键类型获取选项
+func (s sysDictData) GetDictWithDataByType(req *model.GetDictReq) (dict *model.DictRes, err error) {
+	cache := comService.Cache.New()
+	cacheKey := global.SysDict + "_" + req.DictType
+	//从缓存获取
+	iDict := cache.Get(cacheKey)
+	if iDict != nil {
+		err = gconv.Struct(iDict, &dict)
+		if err != nil {
+			return
+		}
+	} else {
+		//从数据库获取
+		dict = &model.DictRes{}
+		//获取类型数据
+		err = dao.SysDictType.Ctx(req.Ctx).Where(dao.SysDictType.Columns.DictType, req.DictType).
+			And(dao.SysDictType.Columns.Status, 1).Fields(model.DictTypeRes{}).Scan(&dict.Info)
+		if err != nil {
+			g.Log().Error(err)
+			err = gerror.New("获取字典类型失败")
+		}
+		err = dao.SysDictData.Ctx(req.Ctx).Fields(model.DictDataRes{}).
+			Where(dao.SysDictData.Columns.DictType, req.DictType).
+			Order(dao.SysDictData.Columns.DictSort + " asc," +
+				dao.SysDictData.Columns.DictCode + " asc").
+			Scan(&dict.Values)
+		if err != nil {
+			g.Log().Error(err)
+			err = gerror.New("获取字典数据失败")
+		}
+		//缓存菜单
+		if dict.Info != nil && dict.Values != nil {
+			cache.Set(cacheKey, dict, 0, global.SysDictTag)
+		}
+	}
+	//设置给定的默认值
+	for _, v := range dict.Values {
+		if req.DefaultValue != "" {
+			if gstr.Equal(req.DefaultValue, v.DictValue) {
+				v.IsDefault = 1
+			} else {
+				v.IsDefault = 0
+			}
+		}
+	}
+	return
+}
+
+// CheckDictTypeUniqueAll 检查字典类型是否唯一
+func (s *sysDictData) CheckDictTypeUniqueAll(dictType string) bool {
+	dict, err := dao.SysDictData.FindOne(dao.SysDictData.Columns.DictType+"=?", dictType)
+	if err != nil {
+		g.Log().Error(err)
+		return false
+	}
+	if dict != nil {
+		return false
+	}
+	return true
+}
+
+// AddSave 添加保存字典数据
+func (s *sysDictData) AddSave(req *model.DictDataAddReq) (id int64, err error) {
+	var res sql.Result
+	res, err = dao.SysDictData.Data(req).Insert()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("添加字典数据失败")
+		return
+	}
+	id, err = res.LastInsertId()
+	return
+}
+
+// GetDictDataById 通过字典数据id获取字典数据
+func (s sysDictData) GetDictDataById(id int) (data *model.SysDictData, err error) {
+	data, err = dao.SysDictData.FindOne(dao.SysDictData.Columns.DictCode, id)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取字典数据失败")
+		return
+	}
+	if data == nil {
+		err = gerror.New("获取字典数据失败")
+	}
+	return
+}
+
+// EditSaveData 修改字典数据
+func (s sysDictData) EditSaveData(req *model.EditDictDataReq) (err error) {
+	_, err = dao.SysDictData.FieldsEx(dao.SysDictData.Columns.DictCode, dao.SysDictData.Columns.CreatedBy).
+		WherePri(req.DictCode).Update(req)
+	return
+}
+
+// DeleteDictDataByIds 删除字典数据
+func (s sysDictData) DeleteDictDataByIds(ids []int) error {
+	_, err := dao.SysDictData.Where(dao.SysDictData.Columns.DictCode+" in(?)", ids).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除失败")
+	}
+	return nil
+}

+ 173 - 0
opms_admin/app/service/sys_dict_type.go

@@ -0,0 +1,173 @@
+package service
+
+import (
+	"context"
+	"dashoo.cn/micro/app/common/global"
+	comModel "dashoo.cn/micro/app/common/model"
+	comService "dashoo.cn/micro/app/common/service"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+
+	"github.com/gogf/gf/container/garray"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type sysDictType struct {
+}
+
+var SysDictType = new(sysDictType)
+
+func (s *sysDictType) SelectList(req *model.ListSysDictTypeReq) (total, page int,
+	list []*model.SysDictTypeInfoRes, err error) {
+	d := dao.SysDictType.Ctx(req.Ctx)
+	if req.DictName != "" {
+		d = d.Where(dao.SysDictType.Columns.DictName+" like ?", "%"+req.DictName+"%")
+	}
+	if req.DictType != "" {
+		d = d.Where(dao.SysDictType.Columns.DictType+" like ?", "%"+req.DictType+"%")
+	}
+	if req.Status != "" {
+		d = d.Where(dao.SysDictType.Columns.Status+" = ", gconv.Int(req.Status))
+	}
+	total, err = d.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	page = req.PageNum
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	err = d.Fields(model.SysDictTypeInfoRes{}).Page(page, req.PageSize).
+		Order(dao.SysDictType.Columns.Id + " asc").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+	}
+	return
+}
+
+// ExistsDictType 检查类型是否已经存在
+func (s *sysDictType) ExistsDictType(dictType string, dictId ...int64) bool {
+	d := dao.SysDictType.Fields(dao.SysDictType.Columns.Id).
+		Where(dao.SysDictType.Columns.DictType, dictType)
+	if len(dictId) > 0 {
+		d = d.And(dao.SysDictType.Columns.Id+" !=? ", dictId)
+	}
+	dict, err := d.FindOne()
+	if err != nil {
+		g.Log().Error(err)
+		return false
+	}
+	if dict != nil {
+		return true
+	}
+	return false
+}
+
+func (s *sysDictType) Add(req *model.SysDictTypeAddReq) error {
+	_, err := dao.SysDictType.Insert(req)
+	if err != nil {
+		g.Log().Debug(err)
+		err = gerror.New("保存到数据库失败")
+	}
+	return err
+}
+
+func (s *sysDictType) Edit(ctx context.Context, req *model.SysDictTypeEditReq) error {
+	err := g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
+		dt, err := dao.SysDictType.Fields(dao.SysDictType.Columns.DictType).FindOne(req.DictId)
+		if err != nil {
+			return err
+		}
+		//修改字典类型
+		_, err = dao.SysDictType.TX(tx).FieldsEx(dao.SysDictType.Columns.CreatedBy,
+			dao.SysDictType.Columns.Id).WherePri(req.DictId).Update(req)
+		if err != nil {
+			return err
+		}
+		//修改字段数据的类型
+		_, err = dao.SysDictData.TX(tx).Data(g.Map{dao.SysDictData.Columns.DictType: req.DictType}).
+			Where(dao.SysDictData.Columns.DictType, dt.DictType).Update()
+		return err
+	})
+	if err != nil {
+		g.Log().Debug(err)
+		err = gerror.New("保存到数据库失败")
+	}
+	return err
+}
+
+func (s *sysDictType) Delete(ctx context.Context, dictIds []int) (err error) {
+	discs := ([]*model.SysDictType)(nil)
+	discs, err = dao.SysDictType.Fields(dao.SysDictType.Columns.DictType).
+		Where(dao.SysDictType.Columns.Id+" in (?) ", dictIds).All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("没有要删除的数据")
+		return
+	}
+	types := garray.NewStrArray()
+	for _, dt := range discs {
+		types.Append(dt.DictType)
+	}
+	if types.Len() > 0 {
+		err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
+			_, err = dao.SysDictType.TX(tx).Delete(dao.SysDictType.Columns.Id+" in (?) ", dictIds)
+			if err != nil {
+				g.Log().Error(err)
+				err = gerror.New("删除类型失败")
+				return err
+			}
+			_, err = dao.SysDictData.TX(tx).Delete(dao.SysDictData.Columns.DictType+" in (?) ", types.Slice())
+			if err != nil {
+				g.Log().Error(err)
+				err = gerror.New("删除字典数据失败")
+				return err
+			}
+			return nil
+		})
+	}
+	return
+}
+
+// GetDictById 获取字典类型
+func (s *sysDictType) GetDictById(id int) (dict *model.SysDictType, err error) {
+	dict, err = dao.SysDictType.FindOne(dao.SysDictType.Columns.Id, id)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取字典类型失败")
+		return
+	}
+	if dict == nil {
+		err = gerror.New("不存在的字典类型")
+	}
+	return
+}
+
+// GetAllDictType 获取所有正常状态下的字典类型
+func (s *sysDictType) GetAllDictType() (list []*model.SysDictType, err error) {
+	cache := comService.Cache.New()
+	//从缓存获取
+	data := cache.Get(global.SysDict + "_dict_type_all")
+	if data != nil {
+		err = gconv.Structs(data, &list)
+		return
+	}
+	err = dao.SysDictType.Where("status", 1).Order("dict_id ASC").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取字典类型数据出错")
+		return
+	}
+	//缓存
+	cache.Set(global.SysDict+"_dict_type_all", list, 0, global.SysDictTag)
+	return
+}

+ 100 - 0
opms_admin/app/service/sys_login_log.go

@@ -0,0 +1,100 @@
+package service
+
+import (
+	comModel "dashoo.cn/micro/app/common/model"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/grpool"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type sysLoginLog struct {
+	Pool *grpool.Pool
+}
+
+var (
+	SysLoginLog = &sysLoginLog{
+		Pool: grpool.New(100),
+	}
+)
+
+func (s *sysLoginLog) Invoke(data *model.LoginLogParams) {
+	s.Pool.Add(func() {
+		//写入日志数据
+		SysUser.LoginLog(data)
+	})
+}
+
+func (s *sysLoginLog) LoginLogListByPage(req *model.SysLoginLogSearchReq) (total, page int, list []*model.SysLogin, err error) {
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	model := dao.SysLogin.M
+	page = req.PageNum
+	order := "info_id DESC"
+	if req.LoginName != "" {
+		model = model.Where("login_name like ?", "%"+req.LoginName+"%")
+	}
+	if req.Status != "" {
+		model = model.Where("status", gconv.Int(req.Status))
+	}
+	if req.Ipaddr != "" {
+		model = model.Where("ipaddr like ?", "%"+req.Ipaddr+"%")
+	}
+	if req.LoginLocation != "" {
+		model = model.Where("login_location like ?", "%"+req.LoginLocation+"%")
+	}
+	if req.BeginTime != "" {
+		model = model.Where("login_time >=", req.BeginTime)
+	}
+	if req.EndTime != "" {
+		model = model.Where("login_time <=", req.EndTime)
+	}
+	if req.SortName != "" {
+		if req.SortOrder != "" {
+			order = req.SortName + " " + req.SortOrder
+		} else {
+			order = req.SortName + " DESC"
+		}
+	}
+
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	err = model.Page(page, req.PageSize).Order(order).Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+	}
+	return
+}
+
+func (s *sysLoginLog) DeleteLoginLogByIds(ids []int) (err error) {
+	if len(ids) == 0 {
+		err = gerror.New("参数错误")
+		return
+	}
+	_, err = dao.SysLogin.Delete("info_id in (?)", ids)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("删除失败")
+	}
+	return
+}
+
+func (s *sysLoginLog) ClearLoginLog() (err error) {
+	_, err = g.DB().Exec("truncate " + dao.SysLogin.Table)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("清除失败")
+	}
+	return
+}

+ 172 - 0
opms_admin/app/service/sys_oper_log.go

@@ -0,0 +1,172 @@
+package service
+
+import (
+	comModel "dashoo.cn/micro/app/common/model"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"dashoo.cn/micro_libary/utils"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/grpool"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/util/gconv"
+)
+
+type sysOperLog struct {
+	Pool *grpool.Pool
+}
+
+var SysOperLog = &sysOperLog{
+	Pool: grpool.New(100),
+}
+
+func (s *sysOperLog) Invoke(data *model.SysOperLogAdd) {
+	s.Pool.Add(func() {
+		//写入日志数据
+		s.OperationLogAdd(data)
+	})
+}
+
+// OperationLogAdd 添加操作日志
+func (s sysOperLog) OperationLogAdd(data *model.SysOperLogAdd) {
+	menuTitle := ""
+	if data.Menu != nil {
+		menuTitle = data.Menu.MenuName
+	}
+	err, dept := data.User.GetDept()
+	if err != nil {
+		g.Log().Error(err)
+		return
+	}
+	insertData := g.Map{
+		dao.SysOperLog.Columns.Title:         menuTitle,
+		dao.SysOperLog.Columns.Method:        data.Url.Path,
+		dao.SysOperLog.Columns.RequestMethod: data.Method,
+		dao.SysOperLog.Columns.OperatorType:  data.OperatorType,
+		dao.SysOperLog.Columns.OperName:      data.User.UserName,
+		dao.SysOperLog.Columns.DeptName:      dept.DeptName,
+		dao.SysOperLog.Columns.OperIp:        data.ClientIp,
+		dao.SysOperLog.Columns.OperLocation:  utils.GetCityByIp(data.ClientIp),
+		dao.SysOperLog.Columns.OperTime:      gtime.Now(),
+	}
+	rawQuery := data.Url.RawQuery
+	if rawQuery != "" {
+		rawQuery = "?" + rawQuery
+	}
+	insertData[dao.SysOperLog.Columns.OperUrl] = data.Url.Path + rawQuery
+	if data.Params != nil {
+		if v, ok := data.Params["apiReturnRes"]; ok {
+			res := gconv.Map(v)
+			if gconv.Int(res["code"]) == 0 {
+				insertData[dao.SysOperLog.Columns.Status] = 1
+			} else {
+				insertData[dao.SysOperLog.Columns.Status] = 0
+			}
+			if _, ok = res["data"]; ok {
+				delete(res, "data")
+			}
+			b, _ := gjson.Encode(res)
+			if len(b) > 0 {
+				insertData[dao.SysOperLog.Columns.JsonResult] = string(b)
+			}
+			delete(data.Params, "apiReturnRes")
+		}
+		b, _ := gjson.Encode(data.Params)
+		if len(b) > 0 {
+			insertData[dao.SysOperLog.Columns.OperParam] = string(b)
+		}
+	}
+	_, err = dao.SysOperLog.Insert(insertData)
+	if err != nil {
+		g.Log().Error(err)
+	}
+}
+
+func (s *sysOperLog) OperationLogListByPage(req *model.SysOperLogSearchReq) (total, page int, list []*model.SysOperLog, err error) {
+	model := dao.SysOperLog.M
+	order := "oper_id DESC"
+	if req != nil {
+		if req.OperName != "" {
+			model = model.Where("oper_name like ?", "%"+req.OperName+"%")
+		}
+		if req.Title != "" {
+			model = model.Where("title like ?", "%"+req.Title+"%")
+		}
+		if req.RequestMethod != "" {
+			model = model.Where("request_method = ?", req.RequestMethod)
+		}
+		if req.Status != "" {
+			model = model.Where("status", gconv.Int(req.Status))
+		}
+		if req.BeginTime != "" {
+			model = model.Where("oper_time >=", req.BeginTime)
+		}
+		if req.EndTime != "" {
+			model = model.Where("oper_time <=", req.EndTime)
+		}
+		if req.SortName != "" {
+			if req.SortOrder != "" {
+				order = req.SortName + " " + req.SortOrder
+			} else {
+				order = req.SortName + " DESC"
+			}
+		}
+	}
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	page = req.PageNum
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	err = model.Page(page, req.PageSize).Order(order).Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+	}
+	return
+}
+
+func (s *sysOperLog) DeleteOperationLogByIds(ids []int) (err error) {
+	if len(ids) == 0 {
+		err = gerror.New("参数错误")
+		return
+	}
+	_, err = dao.SysOperLog.Delete("oper_id in (?)", ids)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("删除失败")
+	}
+	return
+}
+
+func (s *sysOperLog) GetOperationLogById(id int64) (log *model.SysOperLog, err error) {
+	if id == 0 {
+		err = gerror.New("参数错误")
+		return
+	}
+	err = dao.SysOperLog.Where("oper_id", id).Scan(&log)
+	if err != nil {
+		g.Log().Error(err)
+	}
+	if err != nil || log == nil {
+		err = gerror.New("获取操作日志失败")
+	}
+	return
+}
+
+func (s *sysOperLog) ClearOperationLog() (err error) {
+	_, err = g.DB().Exec("truncate " + dao.SysOperLog.Table)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("清除失败")
+	}
+	return
+}

+ 74 - 0
opms_admin/app/service/sys_post.go

@@ -0,0 +1,74 @@
+package service
+
+import (
+	comModel "dashoo.cn/micro/app/common/model"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+)
+
+type sysPost struct{}
+
+var SysPost = new(sysPost)
+
+func (s *sysPost) List(req *model.SysPostSearchParams) (total, page int, list []*model.SysPost, err error) {
+	model := dao.SysPost.M
+	if req != nil {
+		if req.PostCode != "" {
+			model.Where("post_code like ?", "%"+req.PostCode+"%")
+		}
+
+		if req.PostName != "" {
+			model.Where("post_name like ?", "%"+req.PostName+"%")
+		}
+
+		if req.Status != "" {
+			model.Where("status", req.Status)
+		}
+	}
+
+	total, err = model.Count()
+
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+	}
+
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+
+	page = req.PageNum
+
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	err = model.Page(page, req.PageSize).Order("post_sort asc,post_id asc").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+	}
+	return
+}
+
+func (s *sysPost) Add(params *model.SysPostAddParams) error {
+	_, err := dao.SysPost.Insert(params)
+	return err
+}
+
+func (s *sysPost) GetOneById(id int64) (post *model.SysPost, err error) {
+	err = dao.SysPost.WherePri(id).Scan(&post)
+	return
+}
+
+func (s *sysPost) Edit(params *model.SysPostEditParams) (err error) {
+	_, err = dao.SysPost.FieldsEx(dao.SysPost.Columns.Id, dao.SysPost.Columns.CreatedBy).
+		WherePri(params.PostId).Update(params)
+	return err
+}
+
+func (s *sysPost) Delete(ids []int) error {
+	_, err := dao.SysPost.Where(dao.SysPost.Columns.Id+" in(?)", ids).Delete()
+	return err
+}

+ 335 - 0
opms_admin/app/service/sys_role.go

@@ -0,0 +1,335 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/common/global"
+	comModel "dashoo.cn/micro/app/common/model"
+	"dashoo.cn/micro/app/common/service"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"dashoo.cn/micro_libary/utils"
+	"database/sql"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+type sysRole struct{}
+
+var SysRole = new(sysRole)
+
+func (s *sysRole) DeleteByIds(ids []int) (err error) {
+	tx, err := g.DB("default").Begin() //开启事务
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("事务处理失败")
+		return
+	}
+
+	_, err = tx.Model(model.SysRole{}).Where("id in(?)", ids).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		err = gerror.New("删除失败")
+		return
+	}
+	//删除角色的权限和管理的部门数据权限
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		tx.Rollback()
+		return e
+	}
+	for _, v := range ids {
+		_, err = enforcer.RemoveFilteredPolicy(0, fmt.Sprintf("%d", v))
+		if err != nil {
+			tx.Rollback()
+			return err
+		}
+		_, err = tx.Model(model.SysRoleDept{}).Delete("role_id", v)
+		if err != nil {
+			tx.Rollback()
+			return err
+		}
+	}
+	tx.Commit()
+	//清除TAG缓存
+	service.Cache.New().RemoveByTag(global.SysAuthTag)
+	return nil
+}
+
+func (s *sysRole) One(id int) (*model.SysRole, error) {
+	return dao.SysRole.Where("id = ?", id).FindOne()
+}
+
+// GetRoleList 获取角色列表
+func (s *sysRole) GetRoleList() (list []*model.SysRole, err error) {
+	cache := service.Cache.New()
+	//从缓存获取
+	iList := cache.Get(global.SysRole)
+	if iList != nil {
+		err = gconv.Struct(iList, &list)
+		return
+	}
+	//从数据库获取
+	//list, err = dao.SysRole.Order(dao.SysRole.Columns.ListOrder + " asc," + dao.SysRole.Columns.Id + " asc").All()
+	list, err = dao.SysRole.Order(dao.SysRole.Columns.Id + " asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取角色数据失败")
+	}
+	//缓存数据
+	cache.Set(global.SysRole, list, 0, global.SysAuthTag)
+	iList = cache.Get(global.SysRole)
+	return
+}
+
+func (s *sysRole) GetRoleListSearch(req *model.SelectPageReq) (total, page int, list []*model.SysRole, err error) {
+	model := dao.SysRole.M
+	if req.RoleName != "" {
+		model = model.Where("name like ?", "%"+req.RoleName+"%")
+	}
+	if req.Status != "" {
+		model = model.Where("status", gconv.Int(req.Status))
+	}
+	if req.BeginTime != "" {
+
+		model = model.Where("create_time >= ? ", utils.StrToTimestamp(req.BeginTime))
+	}
+
+	if req.EndTime != "" {
+		model = model.Where("create_time<=?", utils.StrToTimestamp(req.EndTime))
+	}
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	page = req.PageNum
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	err = model.Page(page, req.PageSize).Order("id asc").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取数据失败")
+		return
+	}
+	return
+}
+
+// 获取角色关联的菜单规则
+func (s *sysRole) GetFilteredNamedPolicy(id int) ([]int, error) {
+	enforcer, err := service.Casbin.GetEnforcer()
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	gp := enforcer.GetFilteredNamedPolicy("p", 0, fmt.Sprintf("%d", id))
+	gpSlice := make([]int, len(gp))
+	for k, v := range gp {
+		gpSlice[k] = gconv.Int(v[1])
+	}
+
+	return gpSlice, nil
+}
+
+func (s *sysRole) AddRolePost(m map[string]interface{}) error {
+	tx, err := g.DB("default").Begin() //开启事务
+
+	if err != nil {
+		return err
+	}
+	//插入角色
+	insertId, err := s.AddRole(tx, m)
+	if err != nil {
+		tx.Rollback() //回滚
+		return err
+	}
+	//添加角色权限
+	err = s.AddRoleRule(m["menuIds"], insertId)
+
+	if err != nil {
+		tx.Rollback() //回滚
+		g.Log().Error(err.Error())
+		return err
+	}
+
+	tx.Commit()
+	//清除TAG缓存
+	service.Cache.New().RemoveByTag(global.SysAuthTag)
+	return nil
+}
+
+// AddRoleRule 添加角色权限
+func (s *sysRole) AddRoleRule(iRule interface{}, roleId int64) (err error) {
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		err = e
+		return
+	}
+	rule := gconv.Strings(iRule)
+	for _, v := range rule {
+		_, err = enforcer.AddPolicy(fmt.Sprintf("%d", roleId), fmt.Sprintf("%s", v), "All")
+		if err != nil {
+			break
+		}
+	}
+	return
+}
+
+// 插入角色
+func (s *sysRole) AddRole(tx *gdb.TX, data map[string]interface{}) (InsId int64, err error) {
+	if e := s.checkRoleData(data); e != nil {
+		err = e.(gvalid.Error).Current()
+		return
+	}
+	//保存角色信息
+	roleMap := gdb.Map{
+		"status":     data["status"],
+		"name":       data["roleName"],
+		"list_order": data["roleSort"],
+		"remark":     data["remark"],
+	}
+	var res sql.Result
+	res, err = tx.Model(model.SysRole{}).Data(roleMap).Save()
+	if err != nil {
+		return
+	}
+	InsId, _ = res.LastInsertId()
+	return
+}
+
+func (s *sysRole) checkRoleData(params map[string]interface{}) error {
+	rules := []string{
+		"roleName@required|length:1,20#请填写角色名称|名称应在:min到:max个字符之间",
+	}
+
+	e := gvalid.CheckMap(nil, params, rules)
+	if e != nil {
+		return e
+	}
+	return nil
+}
+
+// 修改角色信息
+func (s *sysRole) EditRole(tx *gdb.TX, data map[string]interface{}) error {
+	if _, k := data["roleId"]; !k {
+		return errors.New("缺少更新条件Id")
+	}
+
+	if e := s.checkRoleData(data); e != nil {
+		return e.(gvalid.Error).Current()
+	}
+
+	//保存角色信息
+	roleMap := gdb.Map{
+		"id":         data["roleId"],
+		"status":     data["status"],
+		"name":       data["roleName"],
+		"list_order": data["roleSort"],
+		"remark":     data["remark"],
+	}
+	_, err := tx.Model(model.SysRole{}).Data(roleMap).Save()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// 修改角色的授权规则
+func (s *sysRole) EditRoleRule(iRule interface{}, roleId int64) (err error) {
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		return e
+	}
+
+	//删除旧权限
+	_, err = enforcer.RemoveFilteredPolicy(0, fmt.Sprintf("%d", roleId))
+	if err != nil {
+		return
+	}
+	// 添加新权限
+	rule := gconv.Strings(iRule)
+	for _, v := range rule {
+		_, err = enforcer.AddPolicy(fmt.Sprintf("%d", roleId), fmt.Sprintf("%s", v), "All")
+		if err != nil {
+			break
+		}
+	}
+
+	return
+
+}
+
+func (s *sysRole) EditRolePost(m map[string]interface{}, id int) error {
+	tx, err := g.DB("default").Begin() //开启事务
+	if err != nil {
+		return err
+	}
+	err = s.EditRole(tx, m)
+	if err != nil {
+		tx.Rollback()
+		return err
+	}
+	err = s.EditRoleRule(m["menuIds"], int64(id))
+	if err != nil {
+		tx.Rollback() //回滚
+		return err
+	}
+	tx.Commit()
+	//清除TAG缓存
+	service.Cache.New().RemoveByTag(global.SysAuthTag)
+	return nil
+}
+
+func (s *sysRole) StatusSetRole(req *model.StatusSetReq) error {
+	_, err := dao.SysRole.Where(dao.SysRole.Columns.Id, req.RoleId).Data(dao.SysRole.Columns.Status, req.Status).
+		Update()
+	if err == nil {
+		//清除TAG缓存
+		service.Cache.New().RemoveByTag(global.SysAuthTag)
+	}
+	return err
+}
+
+// 设置角色数据权限
+func (s *sysRole) RoleDataScope(req *model.DataScopeReq) error {
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("设置失败")
+	}
+	_, err = tx.Model(model.SysRole{}).Where("id", req.RoleId).Data(g.Map{"data_scope": req.DataScope}).Update()
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		return gerror.New("设置失败")
+	}
+	if req.DataScope == 2 {
+		_, err := tx.Model(model.SysRoleDept{}).Where("role_id", req.RoleId).Delete()
+		if err != nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			return gerror.New("设置失败")
+		}
+		data := g.List{}
+		for _, deptId := range req.DeptIds {
+			data = append(data, g.Map{"role_id": req.RoleId, "dept_id": deptId})
+		}
+		_, err = tx.Model(model.SysRoleDept{}).Data(data).Insert()
+		if err != nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			return gerror.New("设置失败")
+		}
+	}
+	tx.Commit()
+	return nil
+}

+ 852 - 0
opms_admin/app/service/sys_user.go

@@ -0,0 +1,852 @@
+package service
+
+import (
+	"context"
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/container/gset"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/grand"
+	"github.com/mssola/user_agent"
+	"reflect"
+)
+
+type sysUser struct {
+	NotCheckAuthAdminIds *gset.Set //无需验证权限的用户id
+}
+
+type UserMenu struct {
+	*model.SysAuthRuleInfoRes
+	Index     string `json:"index"`
+	Name      string `json:"name"`
+	MenuName  string `json:"menuName"`
+	Component string `json:"component"`
+	Path      string `json:"path"`
+	Meta      struct {
+		Icon  string `json:"icon"`
+		Title string `json:"title"`
+	} `json:"meta"`
+	Hidden     bool `json:"hidden"`
+	AlwaysShow bool `json:"alwaysShow"`
+}
+
+type UserMenus struct {
+	UserMenu
+	Children []UserMenus `json:"children"`
+}
+
+var (
+	notCheckAuthAdminIds = g.Cfg().GetInterfaces("system.notCheckAuthAdminIds")
+	SysUser              = &sysUser{
+		NotCheckAuthAdminIds: gset.NewFrom(notCheckAuthAdminIds),
+	}
+)
+
+// GetAdminUserByUsernamePassword 后台登陆验证
+func (s *sysUser) GetAdminUserByUsernamePassword(ctx context.Context, req *model.LoginParamsReq) (user *model.LoginUserRes, err error) {
+	user, err = s.GetUserByUsernamePassword(ctx, req)
+	if err != nil {
+		return
+	}
+	//判断是否后台用户
+	if user.IsAdmin != 1 {
+		return nil, gerror.New("抱歉!您不属于后台管理员!")
+	}
+	return
+}
+
+// GetUserByUsernamePassword 登陆验证
+func (s *sysUser) GetUserByUsernamePassword(ctx context.Context, req *model.LoginParamsReq) (user *model.LoginUserRes, err error) {
+	user, err = s.GetUserByUsername(ctx, req.Username)
+	if err != nil {
+		return
+	}
+	if user == nil {
+		return nil, gerror.New("账号密码错误")
+	}
+	//验证密码
+	if library.EncryptPassword(req.Password, user.UserSalt) != user.UserPassword {
+		return nil, gerror.New("账号密码错误")
+	}
+	//账号状态
+	if user.UserStatus == 0 {
+		return nil, gerror.New("账号已被冻结")
+	}
+	return
+}
+
+// GetUserByUsername 通过用户名获取用户信息
+func (s *sysUser) GetUserByUsername(ctx context.Context, userName string) (user *model.LoginUserRes, err error) {
+	return dao.SysUser.FindByUsername(ctx, userName)
+}
+
+// UpdateLoginInfo 更新用户登录信息 保存登录日志
+func (s *sysUser) UpdateLoginInfo(id uint64, username, ip, userAgent, msg, module string) {
+	status := 0 //登录状态 0失败 1成功
+	if id != 0 {
+		//说明登录成功更新登录信息
+		status = 1
+		dao.SysUser.UpLoginInfo(id, ip)
+	}
+	//保存登录日志(异步)
+	SysLoginLog.Invoke(&model.LoginLogParams{
+		Status:    status,
+		Username:  username,
+		Ip:        ip,
+		UserAgent: userAgent,
+		Msg:       msg,
+		Module:    module,
+	})
+}
+
+// LoginLog 记录登录日志
+func (s *sysUser) LoginLog(params *model.LoginLogParams) {
+	ua := user_agent.New(params.UserAgent)
+	browser, _ := ua.Browser()
+	loginData := &model.SysLogin{
+		LoginName:     params.Username,
+		Ipaddr:        params.Ip,
+		LoginLocation: library.GetCityByIp(params.Ip),
+		Browser:       browser,
+		Os:            ua.OS(),
+		Status:        params.Status,
+		Msg:           params.Msg,
+		LoginTime:     gtime.Now(),
+		Module:        params.Module,
+	}
+	dao.SysLoginLog.SaveLog(loginData)
+}
+
+// SaveOnline 保存用户登录在线状态信息
+func (s *sysUser) SaveOnline(params *model.SysUserOnline) {
+	dao.SysUserOnline.SaveOnline(params)
+}
+
+// GetAdminRole 获取用户角色
+func (s *sysUser) GetAdminRole(userId uint64, allRoleList []*model.SysRole) (roles []*model.SysRole, err error) {
+	var roleIds []uint
+	roleIds, err = s.GetAdminRoleIds(userId)
+	if err != nil {
+		return
+	}
+	roles = make([]*model.SysRole, 0, len(allRoleList))
+	for _, v := range allRoleList {
+		for _, id := range roleIds {
+			if id == v.Id {
+				roles = append(roles, v)
+			}
+		}
+		if len(roles) == len(roleIds) {
+			break
+		}
+	}
+	return
+}
+
+// GetAdminRoleIds 获取用户角色ids
+func (s *sysUser) GetAdminRoleIds(userId uint64) (roleIds []uint, err error) {
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		err = e
+		return
+	}
+	//查询关联角色规则
+	groupPolicy := enforcer.GetFilteredGroupingPolicy(0, gconv.String(userId))
+	if len(groupPolicy) > 0 {
+		roleIds = make([]uint, len(groupPolicy))
+		//得到角色id的切片
+		for k, v := range groupPolicy {
+			roleIds[k] = gconv.Uint(v[1])
+		}
+	}
+	return
+}
+
+func (s *sysUser) GetPermissions(roleIds []uint) ([]string, error) {
+	//获取角色对应的菜单id
+	enforcer, err := service.Casbin.GetEnforcer()
+	if err != nil {
+		return nil, err
+	}
+	menuIds := map[int64]int64{}
+	for _, roleId := range roleIds {
+		//查询当前权限
+		gp := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
+		for _, p := range gp {
+			mid := gconv.Int64(p[1])
+			menuIds[mid] = mid
+		}
+	}
+	//获取所有开启的按钮
+	allButtons, err := Rule.GetIsButtonStatusList()
+	userButtons := make([]string, 0, len(allButtons))
+	for _, button := range allButtons {
+		if _, ok := menuIds[gconv.Int64(button.Id)]; gstr.Equal(button.Condition, "nocheck") || ok {
+			userButtons = append(userButtons, button.Name)
+		}
+	}
+	return userButtons, nil
+}
+
+func (s *sysUser) GetAllMenus() (menus []UserMenus, err error) {
+	//获取所有开启的菜单
+	var allMenus []*model.SysAuthRuleInfoRes
+	allMenus, err = Rule.GetIsMenuStatusList()
+	if err != nil {
+		return
+	}
+	menus = make([]UserMenus, len(allMenus))
+	for k, v := range allMenus {
+		var menu UserMenu
+		menu = s.setMenuData(menu, v)
+		menus[k] = UserMenus{UserMenu: menu}
+	}
+	menus = s.GetMenusTree(menus, 0)
+	return
+}
+
+func (s *sysUser) GetAdminMenusByRoleIds(roleIds []uint) (menus []UserMenus, err error) {
+	//获取角色对应的菜单id
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		err = e
+		return
+	}
+	menuIds := map[int64]int64{}
+	for _, roleId := range roleIds {
+		//查询当前权限
+		gp := enforcer.GetFilteredPolicy(0, fmt.Sprintf("%d", roleId))
+		for _, p := range gp {
+			mid := gconv.Int64(p[1])
+			menuIds[mid] = mid
+		}
+	}
+	//获取所有开启的菜单
+	allMenus, err := Rule.GetIsMenuStatusList()
+	if err != nil {
+		return
+	}
+	menus = make([]UserMenus, 0, len(allMenus))
+	for _, v := range allMenus {
+		if _, ok := menuIds[gconv.Int64(v.Id)]; gstr.Equal(v.Condition, "nocheck") || ok {
+			var roleMenu UserMenu
+			roleMenu = s.setMenuData(roleMenu, v)
+			menus = append(menus, UserMenus{UserMenu: roleMenu})
+		}
+	}
+	menus = s.GetMenusTree(menus, 0)
+	return
+}
+
+func (s *sysUser) GetMenusTree(menus []UserMenus, pid uint) []UserMenus {
+	returnList := make([]UserMenus, 0, len(menus))
+	for _, menu := range menus {
+		if menu.Pid == pid {
+			menu.Children = s.GetMenusTree(menus, menu.Id)
+			returnList = append(returnList, menu)
+		}
+	}
+	return returnList
+}
+
+func (s *sysUser) setMenuData(menu UserMenu, entity *model.SysAuthRuleInfoRes) UserMenu {
+	menu = UserMenu{
+		SysAuthRuleInfoRes: entity,
+		Index:              entity.Name,
+		Name:               gstr.UcFirst(entity.Path),
+		MenuName:           entity.Title,
+		Meta: struct {
+			Icon  string `json:"icon"`
+			Title string `json:"title"`
+		}(struct {
+			Icon  string
+			Title string
+		}{Icon: entity.Icon, Title: entity.Title}),
+	}
+	if entity.MenuType != 0 {
+		menu.Component = entity.Component
+		menu.Path = entity.Path
+	} else {
+		menu.Component = "Layout"
+		menu.Path = "/" + entity.Path
+	}
+	if entity.AlwaysShow == 1 {
+		menu.Hidden = false
+	} else {
+		menu.Hidden = true
+	}
+	if entity.AlwaysShow == 1 && entity.MenuType == 0 {
+		menu.AlwaysShow = true
+	} else {
+		menu.AlwaysShow = false
+	}
+	return menu
+}
+
+func (s *sysUser) WriteDeptIdsOfSearchReq(req *model.SysUserSearchReq) error {
+	if req.DeptId == "" {
+		return nil
+	}
+	depts, e := Dept.GetList(&dao.SysDeptSearchParams{
+		Status: "1",
+	})
+	if e != nil {
+		return e
+	}
+	deptId := gconv.Int64(req.DeptId)
+	req.DeptIds = append(req.DeptIds, deptId)
+	children := Dept.FindSonByParentId(depts, deptId)
+	for _, d := range children {
+		req.DeptIds = append(req.DeptIds, d.DeptId)
+	}
+	return nil
+}
+
+// GetUsersRoleDept 获取多个用户角色 部门信息
+func (s *sysUser) GetUsersRoleDept(userList []*model.SysUser) ([]*model.SysUserRoleDeptRes, error) {
+	allRoles, err := SysRole.GetRoleList()
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	depts, err := Dept.GetList(&dao.SysDeptSearchParams{})
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	users := make([]*model.SysUserRoleDeptRes, len(userList))
+	for k, u := range userList {
+		var dept *model.SysDept
+		users[k] = &model.SysUserRoleDeptRes{
+			SysUser: u,
+		}
+		for _, d := range depts {
+			if u.DeptId == uint64(d.DeptId) {
+				dept = d
+			}
+		}
+		users[k].Dept = dept
+		roles, err := s.GetAdminRole(u.Id, allRoles)
+		if err != nil {
+			g.Log().Error(err)
+			return nil, err
+		}
+		for _, r := range roles {
+			users[k].RoleInfo = append(users[k].RoleInfo, &struct {
+				RoleId uint   `json:"roleId"`
+				Name   string `json:"name"`
+			}{RoleId: r.Id, Name: r.Name})
+		}
+	}
+	return users, nil
+}
+
+// GetUserRoleDeptPost 获取某个用户对应的部门、岗位、角色信息
+func (s *sysUser) GetUserRoleDeptPost(user *model.SysUser) (*model.SysUserRoleDeptRes, error) {
+	allRoles, err := SysRole.GetRoleList()
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	//部门
+	depts, err := Dept.GetList(&dao.SysDeptSearchParams{})
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	userData := &model.SysUserRoleDeptRes{
+		SysUser: user,
+	}
+	for _, d := range depts {
+		if user.DeptId == uint64(d.DeptId) {
+			userData.Dept = d
+		}
+	}
+	//角色
+	roles, err := s.GetAdminRole(user.Id, allRoles)
+	if err != nil {
+		g.Log().Error(err)
+		return nil, err
+	}
+	for _, r := range roles {
+		userData.RoleInfo = append(userData.RoleInfo, &struct {
+			RoleId uint   `json:"roleId"`
+			Name   string `json:"name"`
+		}{RoleId: r.Id, Name: r.Name})
+	}
+	//岗位
+	posts, err := s.GetPostsByUserId(user.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, v := range posts {
+		userData.Post = append(userData.Post, &struct {
+			PostId   int64  `json:"postId"`
+			PostName string `json:"postName"`
+		}{PostId: v.PostId, PostName: v.PostName})
+	}
+	return userData, nil
+}
+
+func (s *sysUser) GetUserList(req *model.SysUserSearchReq) (total, page int, userList []*model.SysUser, err error) {
+	if req.PageSize == 0 {
+		req.PageSize = comModel.PageSize
+	}
+	userModel := dao.SysUser.M
+	if req.KeyWords != "" {
+		keyWords := "%" + req.KeyWords + "%"
+		userModel = userModel.Where("user_name like ? or  user_nickname like ?", keyWords, keyWords)
+	}
+	if len(req.DeptIds) != 0 {
+		userModel = userModel.Where("dept_id in (?)", req.DeptIds)
+	}
+	if req.Status != "" {
+		userModel = userModel.Where("user_status", gconv.Int(req.Status))
+	}
+	if req.Phonenumber != "" {
+		userModel = userModel.Where("mobile like ?", "%"+req.Phonenumber+"%")
+	}
+	if req.BeginTime != "" {
+		userModel = userModel.Where("created_at >=?", req.BeginTime)
+	}
+	if req.EndTime != "" {
+		userModel = userModel.Where("created_at <=?", req.EndTime)
+	}
+	total, err = userModel.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总行数失败")
+		return
+	}
+	if req.PageNum == 0 {
+		req.PageNum = 1
+	}
+	page = req.PageNum
+	err = userModel.FieldsEx(dao.SysUser.Columns.UserPassword, dao.SysUser.Columns.UserSalt).
+		Page(page, req.PageSize).Order("id asc").Scan(&userList)
+	return
+}
+
+func (s *sysUser) AddUser(req *model.AddUserReq) (err error) {
+	req.UserSalt = grand.S(10)
+	req.Password = library.EncryptPassword(req.Password, req.UserSalt)
+	var tx *gdb.TX
+	tx, err = g.DB().Begin()
+	if err != nil {
+		err = gerror.New("事务开启失败")
+		return
+	}
+	Model := dao.SysUser.TX(tx)
+	if i, _ := Model.Where("user_name=?", req.UserName).Count(); i != 0 {
+		err = gerror.New("用户名已经存在")
+		tx.Rollback()
+		return
+	}
+	if i, _ := Model.Where("mobile=?", req.Phonenumber).Count(); i != 0 {
+		err = gerror.New("手机号已经存在")
+		tx.Rollback()
+		return
+	}
+	userData := new(model.SysUser)
+	userData.UserName = req.UserName
+	userData.DeptId = req.DeptId
+	userData.UserStatus = req.Status
+	userData.Mobile = req.Phonenumber
+	userData.Sex = req.Sex
+	userData.UserEmail = req.Email
+	userData.UserNickname = req.NickName
+	userData.UserSalt = req.UserSalt
+	userData.UserPassword = req.Password
+	userData.Remark = req.Remark
+	userData.IsAdmin = req.IsAdmin
+	res, err := Model.Insert(userData)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	InsertId, _ := res.LastInsertId()
+	err = s.AddUserRole(req.RoleIds, InsertId)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("设置用户权限失败")
+		tx.Rollback()
+		return
+	}
+	err = s.AddUserPost(req.PostIds, InsertId, tx)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("设置用户岗位信息失败")
+		tx.Rollback()
+		return
+	}
+	tx.Commit()
+	return
+}
+
+// AddUserRole 添加用户角色信息
+func (s *sysUser) AddUserRole(roleIds interface{}, userId int64) (err error) {
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		err = e
+		return
+	}
+	rule := gconv.Ints(roleIds)
+	for _, v := range rule {
+		_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%d", userId), fmt.Sprintf("%d", v))
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// AddUserPost 添加用户岗位信息
+func (s *sysUser) AddUserPost(postIds []int64, userId int64, tx *gdb.TX) (err error) {
+	//删除旧岗位信息
+	_, err = dao.SysUserPost.TX(tx).Where(dao.SysUserPost.Columns.UserId, userId).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		return
+	}
+	if len(postIds) == 0 {
+		return
+	}
+	//添加用户岗位信息
+	data := g.List{}
+	for _, v := range postIds {
+		data = append(data, g.Map{
+			dao.SysUserPost.Columns.UserId: userId,
+			dao.SysUserPost.Columns.PostId: v,
+		})
+	}
+	_, err = dao.SysUserPost.TX(tx).Data(data).Insert()
+	if err != nil {
+		g.Log().Error(err)
+		return
+	}
+	return
+}
+
+// GetUserInfoById 通过Id获取用户信息
+func (s *sysUser) GetUserInfoById(id uint64, withPwd ...bool) (user *model.SysUser, err error) {
+	if len(withPwd) > 0 && withPwd[0] {
+		//用户用户信息
+		err = dao.SysUser.Where(dao.SysUser.Columns.Id, id).Scan(&user)
+	} else {
+		//用户用户信息
+		err = dao.SysUser.Where(dao.SysUser.Columns.Id, id).
+			FieldsEx(dao.SysUser.Columns.UserPassword, dao.SysUser.Columns.UserSalt).Scan(&user)
+	}
+	if err != nil {
+		g.Log().Error(err)
+		return nil, errors.New("获取用户数据失败")
+	}
+	return
+}
+
+// GetEditUser 获取要修改的用户信息
+func (s *sysUser) GetEditUser(id uint64) (g.Map, error) {
+	userData, err := s.GetUserInfoById(id)
+	//获取角色信息
+	roleList, err := SysRole.GetRoleList()
+	if err != nil {
+		g.Log().Error(err)
+		return nil, errors.New("获取角色数据失败")
+	}
+	//获取已选择的角色信息
+	checkedRoleIds, err := SysUser.GetAdminRoleIds(id)
+	if err != nil {
+		g.Log().Error(err)
+		return nil, errors.New("获取用户角色数据失败")
+	}
+	if checkedRoleIds == nil {
+		checkedRoleIds = []uint{}
+	}
+	//获取岗位信息
+	posts, err := s.GetUsedPost()
+	if err != nil {
+		return nil, err
+	}
+	checkedPosts, err := s.GetUserPostIds(id)
+	if err != nil {
+		return nil, err
+	}
+
+	if checkedPosts == nil {
+		checkedPosts = []int64{}
+	}
+
+	res := g.Map{
+		"roleList":       roleList,
+		"userInfo":       userData,
+		"checkedRoleIds": checkedRoleIds,
+		"posts":          posts,
+		"checkedPosts":   checkedPosts,
+	}
+
+	return res, nil
+}
+
+// GetUsedPost 获取正常状态的岗位
+func (s *sysUser) GetUsedPost() (list []*model.SysPost, err error) {
+	err = dao.SysPost.Where(dao.SysPost.C.Status, 1).
+		Order(dao.SysPost.C.PostSort + " ASC, " + dao.SysPost.C.PostId + " ASC ").Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取岗位数据失败")
+	}
+	return
+}
+
+// GetUserPostIds 获取用户岗位
+func (s *sysUser) GetUserPostIds(userId uint64) (postIds []int64, err error) {
+	var list []*model.SysUserPost
+	err = dao.SysUserPost.Where(dao.SysUserPost.Columns.UserId, userId).Scan(&list)
+	if err != nil {
+		g.Log().Error(err)
+		return nil, gerror.New("获取用户岗位信息失败")
+	}
+	postIds = make([]int64, 0)
+	for _, entity := range list {
+		postIds = append(postIds, entity.PostId)
+	}
+	return
+}
+
+// GetPostsByUserId 根据用户id获取岗位信息详情
+func (s *sysUser) GetPostsByUserId(userId uint64) ([]*model.SysPost, error) {
+	postIds, err := s.GetUserPostIds(userId)
+	if err != nil {
+		return nil, err
+	}
+	var posts []*model.SysPost
+	err = dao.SysPost.Where(dao.SysPost.C.PostId+" in (?)", postIds).Scan(&posts)
+	return posts, err
+}
+
+// EditUser 修改用户
+func (s *sysUser) EditUser(req *model.EditUserReq) (err error) {
+	if i, _ := dao.SysUser.Where("id!=? and mobile=?", req.UserId, req.Phonenumber).Count(); i != 0 {
+		err = gerror.New("手机号已经存在")
+		return
+	}
+	var tx *gdb.TX
+	tx, err = g.DB().Begin()
+	//保存管理员信息
+	var userData *model.SysUser
+	err = dao.SysUser.Where("id", req.UserId).Scan(&userData)
+	if err != nil || userData == nil {
+		g.Log().Error(err)
+		err = gerror.New("获取用户信息失败")
+		return
+	}
+	userData.DeptId = req.DeptId
+	userData.UserStatus = req.Status
+	userData.Mobile = req.Phonenumber
+	userData.Sex = req.Sex
+	userData.UserEmail = req.Email
+	userData.UserNickname = req.NickName
+	userData.Remark = req.Remark
+	userData.IsAdmin = req.IsAdmin
+	_, err = dao.SysUser.TX(tx).FieldsEx(dao.SysUser.Columns.Id, dao.SysUser.Columns.CreatedAt,
+		dao.SysUser.Columns.DeletedAt, dao.SysUser.Columns.LastLoginTime).
+		WherePri(userData.Id).Update(userData)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("修改用户信息失败")
+		tx.Rollback()
+		return
+	}
+	//设置用户所属角色信息
+	err = s.EditUserRole(req.RoleIds, req.UserId)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("设置用户权限失败")
+		tx.Rollback()
+		return
+	}
+	//设置用户岗位数据
+	err = s.AddUserPost(req.PostIds, gconv.Int64(req.UserId), tx)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("设置用户岗位信息失败")
+		tx.Rollback()
+		return
+	}
+	tx.Commit()
+	return
+}
+
+// EditUserRole 修改用户角色信息
+func (s *sysUser) EditUserRole(roleIds interface{}, userId int) (err error) {
+	enforcer, e := service.Casbin.GetEnforcer()
+	if e != nil {
+		err = e
+		return
+	}
+	rule := gconv.Ints(roleIds)
+	//删除用户旧角色信息
+	enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%d", userId))
+	for _, v := range rule {
+		_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%d", userId), fmt.Sprintf("%d", v))
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// ResetUserPwd 重置用户密码
+func (s *sysUser) ResetUserPwd(req *model.SysUserResetPwdReq) error {
+	salt := grand.S(10)
+	password := library.EncryptPassword(req.Password, salt)
+	_, err := dao.SysUser.WherePri(req.Id).Update(g.Map{
+		dao.SysUser.Columns.UserSalt:     salt,
+		dao.SysUser.Columns.UserPassword: password,
+	})
+	return err
+}
+
+func (s *sysUser) ChangeUserStatus(req *model.SysUserStatusReq) error {
+	_, err := dao.SysUser.WherePri(req.Id).Update(g.Map{
+		dao.SysUser.Columns.UserStatus: req.UserStatus,
+	})
+	return err
+}
+
+// DeleteUser 删除用户信息
+func (s *sysUser) DeleteUser(ctx context.Context, ids []int) error {
+	return g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
+		_, err := dao.SysUser.Ctx(ctx).TX(tx).Where(dao.SysUser.Columns.Id+" in(?)", ids).Delete()
+		//删除对应权限
+		enforcer, err := service.Casbin.GetEnforcer()
+		if err == nil {
+			for _, v := range ids {
+				enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%d", v))
+			}
+		}
+		//删除用户对应的岗位
+		_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Delete(dao.SysUserPost.Columns.UserId+" in (?)", ids)
+		return err
+	})
+}
+
+// SetAvatar 修改用户头像
+func (s *sysUser) SetAvatar(userId uint64, avatarUrl string) error {
+	_, err := dao.SysUser.WherePri(userId).Unscoped().Update(g.Map{
+		dao.SysUser.Columns.Avatar: avatarUrl,
+	})
+	return err
+}
+
+// ProfileEdit 修改个人资料
+func (s *sysUser) ProfileEdit(req *model.ProfileUpReq) error {
+	_, err := dao.SysUser.WherePri(req.UserId).Unscoped().Update(req)
+	return err
+}
+
+// ProfileUpdatePwd 修改个人密码
+func (s *sysUser) ProfileUpdatePwd(req *model.ProfileUpdatePwdReq) error {
+	userInfo, err := s.GetUserInfoById(req.UserId, true)
+	if err != nil {
+		return err
+	}
+	oldPassword := library.EncryptPassword(req.OldPassword, userInfo.UserSalt)
+	if oldPassword != userInfo.UserPassword {
+		return errors.New("原始密码错误!")
+	}
+	salt := grand.S(10)
+	newPassword := library.EncryptPassword(req.NewPassword, salt)
+	_, err = dao.SysUser.WherePri(req.UserId).Unscoped().Update(g.Map{
+		dao.SysUser.Columns.UserSalt:     salt,
+		dao.SysUser.Columns.UserPassword: newPassword,
+	})
+	return err
+}
+
+// GetDataWhere 获取数据权限判断条件
+func (s *sysUser) GetDataWhere(userInfo *dao.CtxUser, entity interface{}) (where g.Map, err error) {
+	whereJustMe := g.Map{} //本人数据权限
+	t := reflect.TypeOf(entity)
+	for i := 0; i < t.Elem().NumField(); i++ {
+		if t.Elem().Field(i).Name == "CreatedBy" {
+			//若存在用户id的字段,则生成判断数据权限的条件
+			//1、获取当前用户所属角色
+			allRoles := ([]*model.SysRole)(nil)
+			allRoles, err = SysRole.GetRoleList()
+			if err != nil {
+				return nil, err
+			}
+			roles := ([]*model.SysRole)(nil)
+			roles, err = s.GetAdminRole(userInfo.Id, allRoles)
+			if err != nil {
+				return nil, err
+			}
+			//2获取角色对应数据权限
+			deptIdArr := gset.New()
+			for _, role := range roles {
+				switch role.DataScope {
+				case 1: //全部数据权限
+					return
+				case 2: //自定数据权限
+					var deptIds []int64
+					deptIds, err = Dept.GetRoleDepts(gconv.Int64(role.Id))
+					if err != nil {
+						return
+					}
+					deptIdArr.Add(gconv.Interfaces(deptIds)...)
+				case 3: //本部门数据权限
+					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
+				case 4: //本部门及以下数据权限
+					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
+					//获取正常状态部门数据
+					depts := ([]*model.SysDept)(nil)
+					depts, err = Dept.GetList(&dao.SysDeptSearchParams{Status: "1"})
+					if err != nil {
+						return
+					}
+					var dList g.List
+					for _, d := range depts {
+						m := g.Map{
+							"id":    d.DeptId,
+							"pid":   d.ParentId,
+							"label": d.DeptName,
+						}
+						dList = append(dList, m)
+					}
+					l := library.FindSonByParentId(dList, gconv.Int(userInfo.DeptId), "pid", "id")
+					for _, li := range l {
+						deptIdArr.Add(gconv.Int64(li["id"]))
+					}
+				case 5: //仅本人数据权限
+					whereJustMe = g.Map{"user.id": userInfo.Id}
+				}
+			}
+			if deptIdArr.Size() > 0 {
+				where = g.Map{"user.dept_id": deptIdArr.Slice()}
+			} else if len(whereJustMe) > 0 {
+				where = whereJustMe
+			}
+		}
+	}
+	return
+}
+
+// GetUsers 通过用户ids查询多个用户信息
+func (s *sysUser) GetUsers(ids []int) (users []*model.SysUserRes, err error) {
+	if len(ids) == 0 {
+		return
+	}
+	idsSet := gset.NewIntSetFrom(ids).Slice()
+	err = dao.SysUser.Where(dao.SysUser.Columns.Id+" in(?)", idsSet).Fields(model.SysUserRes{}).
+		Order(dao.SysUser.Columns.Id + " ASC").Scan(&users)
+	return
+}

+ 39 - 0
opms_admin/app/service/sys_user_group.go

@@ -0,0 +1,39 @@
+package service
+
+import (
+	"dashoo.cn/micro/app/dao"
+	"dashoo.cn/micro/app/model"
+)
+
+type sysUserGroup struct {
+}
+
+var SysUserGroup = new(sysUserGroup)
+
+func (s *sysUserGroup) GetList(searchParams *model.SysDeptSearchParams) ([]*model.SysUserGroup, error) {
+	sysUserGroupModel := dao.SysUserGroup.M
+	if searchParams.DeptName != "" {
+		sysUserGroupModel = sysUserGroupModel.Where("dept_name like ?", "%"+searchParams.DeptName+"%")
+	}
+	if searchParams.Status != "" {
+		sysUserGroupModel = sysUserGroupModel.Where("status", searchParams.Status)
+	}
+	sysUserGroupList := ([]*model.SysUserGroup)(nil)
+	if err := sysUserGroupModel.Scan(&sysUserGroupList); err != nil {
+		return nil, err
+	}
+	return sysUserGroupList, nil
+}
+
+func (s *sysUserGroup) GetRoleDepts(roleId int) ([]int, error) {
+	var entitys []*model.SysRoleDept
+	err := dao.SysRoleDept.Where("role_id", roleId).Scan(&entitys)
+	if err != nil {
+		return nil, err
+	}
+	result := make([]int, 0)
+	for _, v := range entitys {
+		result = append(result, v.DeptId)
+	}
+	return result, nil
+}