Просмотр исходного кода

缓存线程安全处理,层级递归删除

yxh 6 лет назад
Родитель
Сommit
ddf120e528

+ 5 - 21
app/controller/admin/auth.go

@@ -46,6 +46,7 @@ func (c *Auth) MenuSort(r *ghttp.Request) {
 	for k, v := range s {
 		auth_rule.Model.Where("id=?", k).Data("weigh", v).Update()
 	}
+	cache_service.New().RemoveByTag(cache_service.AdminAuthTag)
 	response.SusJson(true, r, "排序成功")
 }
 
@@ -126,7 +127,7 @@ func (c *Auth) DeleteMenu(r *ghttp.Request) {
 	if len(ids) == 0 {
 		response.FailJson(true, r, "删除失败,参数错误")
 	}
-	_, err := auth_rule.Model.Where("id in(?)", ids).Delete()
+	err := auth_service.DeleteMenuByIds(ids)
 	if err != nil {
 		g.Log().Error(err)
 		response.FailJson(true, r, "删除失败")
@@ -276,31 +277,14 @@ func (c *Auth) EditRole(r *ghttp.Request) {
 
 //删除角色
 func (c *Auth) DeleteRole(r *ghttp.Request) {
-	ids := r.GetRequestArray("ids")
+	ids := r.GetInts("ids")
 	if len(ids) == 0 {
 		response.FailJson(true, r, "删除失败,参数错误")
 	}
-	tx, err := g.DB("default").Begin() //开启事务
-	if err != nil {
-		g.Log().Error(err)
-		response.FailJson(true, r, "事务处理失败")
-	}
-	_, err = tx.Table(role.Table).Where("id in(?)", ids).Delete()
+	err := auth_service.DeleteRoleByIds(ids)
 	if err != nil {
-		g.Log().Error(err)
-		tx.Rollback()
-		response.FailJson(true, r, "删除失败")
-	}
-	//删除角色的权限
-	for _, v := range ids {
-		err = auth_service.DeleteRoleRule(gconv.Int64(v))
-		if err != nil {
-			g.Log().Error(err)
-			tx.Rollback()
-			response.FailJson(true, r, "删除失败")
-		}
+		response.FailJson(true, r, "删除失败,"+err.Error())
 	}
-	tx.Commit()
 	//清除TAG缓存
 	cache_service.New().RemoveByTag(cache_service.AdminAuthTag)
 	response.SusJson(true, r, "删除成功")

+ 1 - 1
app/controller/admin/public.go

@@ -11,6 +11,6 @@ type Public struct{}
 
 //获取验证码图片信息
 func (p *Public) Verify(r *ghttp.Request) {
-	idKeyC, base64stringC := utils.GetVerifyImg()
+	idKeyC, base64stringC := utils.GetVerifyImgString()
 	response.SusJson(true, r, "ok", g.MapStrStr{"idKeyC": idKeyC, "base64stringC": base64stringC})
 }

+ 78 - 1
app/service/admin/auth_service/auth_rule.go

@@ -48,6 +48,21 @@ func GetIsMenuStatusList() ([]*auth_rule.Entity, error) {
 	return gList, nil
 }
 
+//获取status==1的菜单列表
+func GetMenuIsStatusList() ([]*auth_rule.Entity, error) {
+	list, err := GetMenuList()
+	if err != nil {
+		return nil, err
+	}
+	var gList = make([]*auth_rule.Entity, 0, len(list))
+	for _, v := range list {
+		if v.Status == 1 {
+			gList = append(gList, v)
+		}
+	}
+	return gList, nil
+}
+
 //获取所有菜单
 func GetMenuList() (list []*auth_rule.Entity, err error) {
 	cache := cache_service.New()
@@ -219,7 +234,7 @@ func EditRoleRule(iRule interface{}, roleId int64) (err error) {
 }
 
 //删除角色权限操作
-func DeleteRoleRule(roleId int64) (err error) {
+func DeleteRoleRule(roleId int) (err error) {
 	enforcer, e := casbin_adapter_service.GetEnforcer()
 	if e != nil {
 		err = e
@@ -363,3 +378,65 @@ func checkUserData(params map[string]interface{}, t string) error {
 	}
 	return nil
 }
+
+func DeleteRoleByIds(ids []int) (err error) {
+	//查询所有子级id
+	roleAllEntity, err := GetRoleList()
+	if err != nil {
+		g.Log().Debug(err)
+		err = gerror.New("删除失败,不存在角色信息")
+		return
+	}
+	roleAll := gconv.SliceMap(roleAllEntity)
+	sonList := make(g.List, 0, len(roleAll))
+	for _, id := range ids {
+		sonList = append(sonList, utils.FindSonByParentId(roleAll, id, "parent_id", "id")...)
+	}
+	for _, role := range sonList {
+		ids = append(ids, gconv.Int(role["id"]))
+	}
+	tx, err := g.DB("default").Begin() //开启事务
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("事务处理失败")
+		return
+	}
+	_, err = tx.Table(role.Table).Where("id in(?)", ids).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		err = gerror.New("删除失败")
+		return
+	}
+	//删除角色的权限
+	for _, v := range ids {
+		err = DeleteRoleRule(v)
+		if err != nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			err = gerror.New("删除失败")
+			return
+		}
+	}
+	tx.Commit()
+	return
+}
+
+//删除菜单
+func DeleteMenuByIds(ids []int) (err error) {
+	//获取菜单数据
+	menus, err := GetMenuList()
+	if err != nil {
+		return
+	}
+	menuList := gconv.SliceMap(menus)
+	son := make(g.List, 0, len(menuList))
+	for _, id := range ids {
+		son = append(son, utils.FindSonByParentId(menuList, id, "pid", "id")...)
+	}
+	for _, v := range son {
+		ids = append(ids, gconv.Int(v["id"]))
+	}
+	_, err = auth_rule.Model.Where("id in (?)", ids).Delete()
+	return
+}

+ 1 - 1
app/service/admin/user_service/user.go

@@ -120,7 +120,7 @@ func GetAdminMenusByRoleIds(roleIds []int) (menus g.List, err error) {
 	}
 	roleMenus := make(g.List, 0, 100)
 	for _, v := range allMenus {
-		if _, ok := menuIds[gconv.Int64(v.Id)]; ok {
+		if _, ok := menuIds[gconv.Int64(v.Id)]; gstr.Equal(v.Condition, "nocheck") || ok {
 			roleMenu := gconv.Map(v)
 			roleMenu["index"] = v.Name
 			roleMenus = append(roleMenus, roleMenu)

+ 19 - 7
app/service/cache_service/cache.go

@@ -6,9 +6,12 @@ import (
 	"github.com/gogf/gf/os/gcache"
 	"github.com/gogf/gf/util/gconv"
 	"reflect"
+	"sync"
 	"time"
 )
 
+var tagSetMux sync.Mutex
+
 type CacheTagService struct {
 	tagKey interface{}
 }
@@ -25,16 +28,11 @@ func (c *CacheTagService) cacheTagKey(key interface{}, tag interface{}) {
 		value := gcache.Get(c.tagKey)
 		if value != nil {
 			keyValue := gconv.SliceAny(value)
-			hasKey := false
 			for _, v := range keyValue {
-				if reflect.DeepEqual(key, v) {
-					hasKey = true
-					break
+				if !reflect.DeepEqual(key, v) {
+					tagValue = append(tagValue, v)
 				}
 			}
-			if !hasKey {
-				tagValue = append(tagValue, gconv.SliceAny(value)...)
-			}
 		}
 		gcache.Set(c.tagKey, tagValue, 0)
 	}
@@ -50,13 +48,17 @@ func (c *CacheTagService) setTagKey(tag interface{}) {
 // 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{}) {
+	tagSetMux.Lock()
 	c.cacheTagKey(key, tag)
 	gcache.Set(key, value, duration)
+	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 {
+	tagSetMux.Lock()
+	defer tagSetMux.Unlock()
 	c.cacheTagKey(key, tag)
 	return gcache.SetIfNotExist(key, value, duration)
 }
@@ -65,6 +67,7 @@ func (c *CacheTagService) SetIfNotExist(key interface{}, value interface{}, dura
 //
 // It does not expire if <duration> <= 0.
 func (c *CacheTagService) Sets(data map[interface{}]interface{}, duration time.Duration, tag interface{}) {
+	tagSetMux.Lock()
 	if tag != nil {
 		for k, _ := range data {
 			c.cacheTagKey(k, tag)
@@ -73,6 +76,7 @@ func (c *CacheTagService) Sets(data map[interface{}]interface{}, duration time.D
 	} else {
 		gcache.Sets(data, duration)
 	}
+	tagSetMux.Unlock()
 }
 
 // Get returns the value of <tagKey>.
@@ -87,6 +91,8 @@ func (c *CacheTagService) Get(key interface{}) interface{} {
 //
 // It does not expire if <duration> <= 0.
 func (c *CacheTagService) GetOrSet(key interface{}, value interface{}, duration time.Duration, tag interface{}) interface{} {
+	tagSetMux.Lock()
+	defer tagSetMux.Unlock()
 	c.cacheTagKey(key, tag)
 	return gcache.GetOrSet(key, value, duration)
 }
@@ -95,6 +101,8 @@ func (c *CacheTagService) GetOrSet(key interface{}, value interface{}, duration
 // 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{}, duration time.Duration, tag interface{}) interface{} {
+	tagSetMux.Lock()
+	defer tagSetMux.Unlock()
 	c.cacheTagKey(key, tag)
 	return gcache.GetOrSetFunc(key, f, duration)
 }
@@ -105,6 +113,8 @@ func (c *CacheTagService) GetOrSetFunc(key interface{}, f func() interface{}, du
 //
 // Note that the function <f> is executed within writing mutex lock.
 func (c *CacheTagService) GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration, tag interface{}) interface{} {
+	tagSetMux.Lock()
+	defer tagSetMux.Unlock()
 	c.cacheTagKey(key, tag)
 	return gcache.GetOrSetFuncLock(key, f, duration)
 }
@@ -127,6 +137,7 @@ func (c *CacheTagService) Removes(keys []interface{}) {
 // Remove deletes the <tag> in the cache, and returns its value.
 func (c *CacheTagService) RemoveByTag(tag interface{}) {
 	c.setTagKey(tag)
+	tagSetMux.Lock()
 	//删除tagKey 对应的 key和值
 	keys := c.Get(c.tagKey)
 	if keys != nil {
@@ -134,6 +145,7 @@ func (c *CacheTagService) RemoveByTag(tag interface{}) {
 		c.Removes(ks)
 	}
 	c.Remove(c.tagKey)
+	tagSetMux.Unlock()
 }
 
 // Removes deletes <tags> in the cache.

+ 1 - 1
config/config.toml

@@ -48,5 +48,5 @@ policyFile="./config/casbin_conf/rbac_policy.csv"
 
 #后台相关配置
 [adminInfo]
-notCheckAuthAdminIds = [1,2]  #无需验证后台权限的用户id
+notCheckAuthAdminIds = [1,2,31]  #无需验证后台权限的用户id
 pageNum = 5  #后台列表分页长度

+ 3 - 2
go.mod

@@ -4,7 +4,8 @@ require (
 	github.com/casbin/casbin/v2 v2.1.2
 	github.com/goflyfox/gtoken v1.3.9
 	github.com/gogf/gf v1.11.4
-	github.com/mojocn/base64Captcha v1.2.2
+	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
+	github.com/mojocn/base64Captcha v1.3.0
 )
 
-go 1.13
+go 1.14

+ 40 - 32
library/utils/function.go

@@ -3,7 +3,6 @@ package utils
 import (
 	"database/sql"
 	"errors"
-	"fmt"
 	"gfast/app/model/admin/user"
 	"gfast/library/response"
 	"github.com/goflyfox/gtoken/gtoken"
@@ -14,7 +13,6 @@ import (
 	"github.com/gogf/gf/net/ghttp"
 	"github.com/gogf/gf/os/gtime"
 	"github.com/gogf/gf/util/gconv"
-	"github.com/gogf/gf/util/grand"
 	"github.com/gogf/gf/util/gvalid"
 	"github.com/mojocn/base64Captcha"
 )
@@ -27,30 +25,44 @@ var (
 	NotCheckAuthAdminIds []int //无需验证权限的用户id
 )
 
-//获取验证码
-func GetVerifyImg() (idKeyC string, base64stringC string) {
-	//字符,公式,验证码配置
-	var configC = base64Captcha.ConfigCharacter{
-		Height: 60,
-		Width:  240,
-		//const CaptchaModeNumber:数字,CaptchaModeAlphabet:字母,CaptchaModeArithmetic:算术,CaptchaModeNumberAlphabet:数字字母混合.
-		Mode:               base64Captcha.CaptchaModeNumberAlphabet,
-		ComplexOfNoiseText: base64Captcha.CaptchaComplexLower,
-		ComplexOfNoiseDot:  base64Captcha.CaptchaComplexLower,
-		IsShowHollowLine:   false,
-		IsShowNoiseDot:     false,
-		IsShowNoiseText:    false,
-		IsShowSlimeLine:    false,
-		IsShowSineLine:     true,
-		CaptchaLen:         4,
-	}
-	//创建字符公式验证码.
-	//GenerateCaptcha 第一个参数为空字符串,包会自动在服务器一个随机种子给你产生随机uiid.
-	var capC base64Captcha.CaptchaInterface
-	idKeyC, capC = base64Captcha.GenerateCaptcha(grand.Str(20), configC)
-	//以base64编码
-	base64stringC = base64Captcha.CaptchaWriteToBase64Encoding(capC)
-	return idKeyC, base64stringC
+//获取数字验证码
+func GetVerifyImgDigit() (idKeyC string, base64stringC string) {
+	driver := &base64Captcha.DriverDigit{80, 240, 5, 0.7, 5}
+	store := base64Captcha.DefaultMemStore
+	c := base64Captcha.NewCaptcha(driver, store)
+	idKeyC, base64stringC, err := c.Generate()
+	if err != nil {
+		g.Log().Error(err)
+	}
+	return
+}
+
+//获取字母数字混合验证码
+func GetVerifyImgString() (idKeyC string, base64stringC string) {
+	driver := &base64Captcha.DriverString{
+		Height:          80,
+		Width:           240,
+		NoiseCount:      50,
+		ShowLineOptions: 20,
+		Length:          4,
+		Source:          "abcdefghijklmnopqrstuvwxyz0123456789",
+	}
+	driver = driver.ConvertFonts()
+	store := base64Captcha.DefaultMemStore
+	c := base64Captcha.NewCaptcha(driver, store)
+	idKeyC, base64stringC, err := c.Generate()
+	if err != nil {
+		g.Log().Error(err)
+	}
+	return
+}
+
+//验证输入的验证码是否正确
+func VerifyString(id, answer string) bool {
+	driver := new(base64Captcha.DriverString)
+	store := base64Captcha.DefaultMemStore
+	c := base64Captcha.NewCaptcha(driver, store)
+	return c.Verify(id, answer, true)
 }
 
 //AdminLogin 后台用户登陆验证
@@ -70,9 +82,9 @@ func AdminLogin(r *ghttp.Request) (string, interface{}) {
 		response.JsonExit(r, response.ErrorCode, e.String())
 	}
 	//判断验证码是否正确
-	/*if !base64Captcha.VerifyCaptchaAndIsClear(data["idKeyC"], data["idValueC"], true) {
+	if !VerifyString(data["idKeyC"], data["idValueC"]) {
 		response.JsonExit(r, response.ErrorCode, "验证码输入错误")
-	}*/
+	}
 	password := EncryptCBC(data["password"], AdminCbcPublicKey)
 	var keys string
 	if AdminMultiLogin {
@@ -93,10 +105,6 @@ func AuthAfterFunc(r *ghttp.Request, respData gtoken.Resp) {
 	if r.Method == "OPTIONS" || respData.Success() {
 		r.Middleware.Next()
 	} else {
-		params := r.GetRequestMap()
-		no := gtime.TimestampMilliStr()
-		g.Log().Info(fmt.Sprintf("[AUTH_%s][url:%s][params:%s][data:%s]",
-			no, r.URL.Path, params, respData.Json()))
 		respData.Msg = "用户信息验证失败"
 		response := r.Response
 		options := response.DefaultCORSOptions()

+ 48 - 4
library/utils/slice_tree.go

@@ -5,7 +5,6 @@ import (
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/util/gconv"
 	"reflect"
-	"strings"
 )
 
 //有层级关系的数组,父级-》子级 排序
@@ -34,7 +33,7 @@ func ParentSonSort(list g.List, params ...interface{}) g.List {
 	levelName = gconv.String(GetSliceByKey(args, 4, "flg"))
 	title = gconv.String(GetSliceByKey(args, 5, "title"))
 	breaks = gconv.Int(GetSliceByKey(args, 6, -1))
-	prefixStr = gconv.String(GetSliceByKey(args, 7, "  "))
+	prefixStr = gconv.String(GetSliceByKey(args, 7, ""))
 	//定义一个新slice用于返回
 	var returnSlice g.List
 	for _, v := range list {
@@ -46,10 +45,10 @@ func ParentSonSort(list g.List, params ...interface{}) g.List {
 				if levelClone < 0 {
 					break
 				}
-				titlePrefix += strings.Repeat(prefixStr, 2)
+				titlePrefix += prefixStr
 				levelClone--
 			}
-			titlePrefix += "├"
+			titlePrefix = "├" + titlePrefix
 			if level == 0 {
 				v["title_prefix"] = ""
 			} else {
@@ -131,3 +130,48 @@ func GetSliceByKey(args []interface{}, key int, val interface{}) interface{} {
 	}
 	return value
 }
+
+//有层级关系的数组,通过父级id查找所有子级id数组
+func FindSonByParentId(list g.List, fid int, flg, flgV string) g.List {
+	newList := make(g.List, 0, len(list))
+	for _, v := range list {
+		if gconv.Int(v[flg]) == fid {
+			newList = append(newList, v)
+			fList := FindSonByParentId(list, gconv.Int(v[flgV]), flg, flgV)
+			newList = append(newList, fList...)
+		}
+	}
+	return newList
+}
+
+//有层级关系的数组,通过子级fid查找所有父级数组
+func findParentBySonPid(list g.List, id int, params ...interface{}) g.List {
+	args := make([]interface{}, 4)
+	for k, v := range params {
+		if k == 4 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		filter      = gconv.String(GetSliceByKey(args, 0, "filter")) //过滤键名
+		fPid        = gconv.String(GetSliceByKey(args, 1, "pid"))    //父级id字段键名
+		filterValue = GetSliceByKey(args, 2, nil)                    //过滤键值
+		fid         = gconv.String(GetSliceByKey(args, 3, "id"))     //id字段键名
+	)
+	rList := make(g.List, 0, len(list))
+	for _, v := range list {
+		if gconv.Int(v[fid]) == id {
+			if fv, ok := v[filter]; ok {
+				if reflect.DeepEqual(fv, filterValue) {
+					rList = append(rList, v)
+				}
+			} else {
+				rList = append(rList, v)
+			}
+			r := findParentBySonPid(list, gconv.Int(v[fPid]), filter, fPid, filterValue, fid)
+			rList = append(rList, r...)
+		}
+	}
+	return rList
+}

+ 19 - 3
router/middleware.go

@@ -3,12 +3,14 @@ package router
 import (
 	"fmt"
 	"gfast/app/model/admin/auth_rule"
+	"gfast/app/service/admin/auth_service"
 	"gfast/app/service/admin/user_service"
 	"gfast/app/service/casbin_adapter_service"
 	"gfast/library/response"
 	"gfast/library/utils"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/text/gstr"
 )
 
 //跨域处理中间件
@@ -29,14 +31,28 @@ func MiddlewareAuth(r *ghttp.Request) {
 		}
 	}
 	url := r.Request.URL
-	g.Log().Debug(url.Path)
 	//获取地址对应的菜单id
-	gValue, err := auth_rule.Model.Where("name=?", url.Path).Fields("id").Value()
+	menuList, err := auth_service.GetMenuIsStatusList()
 	if err != nil {
 		g.Log().Error(err)
 		response.FailJson(true, r, "请求数据失败")
 	}
-	menuId := gValue.Int()
+	var menu *auth_rule.Entity
+	for _, m := range menuList {
+		if gstr.Equal(m.Name, url.Path) {
+			menu = m
+			break
+		}
+	}
+	if menu == nil {
+		response.FailJson(true, r, "没有访问权限")
+	}
+	//若存在不需要验证的条件则跳过
+	if gstr.Equal(menu.Condition, "nocheck") {
+		r.Middleware.Next()
+		return
+	}
+	menuId := menu.Id
 	//菜单没存数据库不验证权限
 	if menuId != 0 {
 		//判断权限操作

+ 1 - 1
router/router.go

@@ -13,7 +13,7 @@ func init() {
 	sysLoginGroup := group.Group("/sysLogin")
 	sysLoginGroup.ALL("/public", new(admin.Public))
 	systemGroup := group.Group("/system")
-	//systemGroup.Middleware(MiddlewareAuth)//后台权限验证
+	systemGroup.Middleware(MiddlewareAuth) //后台权限验证
 	systemGroup.ALL("/index", new(admin.Index))
 	systemGroup.ALL("/auth", new(admin.Auth))
 }

+ 0 - 12
test/demo2_test.go

@@ -1,8 +1,6 @@
 package test
 
 import (
-	"fmt"
-	"gfast/app/service/cache_service"
 	"testing"
 )
 
@@ -11,15 +9,5 @@ func TestDemo2(t *testing.T) {
 }
 
 func test21(t *testing.T) {
-	cache_service.New().Set("wang", "老王", 0, "person")
-	cache_service.New().Set("zs", "张三", 0, "person")
-	cache_service.New().Set("ls", "李四", 0, "person")
-	cache_service.New().Set("dog", "狗狗", 0, "animal")
-	cache_service.New().Set("cat", "猫猫", 0, "animal")
-	fmt.Println(cache_service.New().Keys())
 
-	cache_service.New().RemoveByTag("animal") //删除动物标签
-	fmt.Println(cache_service.New().Keys())
-
-	fmt.Println(cache_service.New().Get("dog"))
 }