+ JWT, Captcha, Password 校验
This commit is contained in:
parent
3d9e85e216
commit
3f68510076
13 changed files with 554 additions and 140 deletions
|
|
@ -89,17 +89,17 @@ func NewConfig(envfile ...string) Config {
|
|||
|
||||
if len(envfile) > 0 {
|
||||
file, err := filepath.Abs(envfile[0])
|
||||
if err != nil {
|
||||
log.Printf("加载环境配置文件%s出错 %s\n", envfile[0], err.Error())
|
||||
} else {
|
||||
if err == nil {
|
||||
// log.Printf("加载环境配置文件%s出错 %s\n", envfile[0], err.Error())
|
||||
// } else {
|
||||
filename = file
|
||||
}
|
||||
}
|
||||
|
||||
err := godotenv.Overload(filename)
|
||||
if err != nil {
|
||||
log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error())
|
||||
}
|
||||
godotenv.Overload(filename)
|
||||
// if err != nil {
|
||||
// log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error())
|
||||
// }
|
||||
|
||||
cfg := Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
|
|
|
|||
47
helper/array.process.go
Normal file
47
helper/array.process.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package helper
|
||||
|
||||
import "github.com/yaoapp/gou"
|
||||
|
||||
// ProcessArrayPluck xiang.helper.ArrayPluck 将多个数据记录集合,合并为一个数据记录集合
|
||||
func ProcessArrayPluck(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
columns := process.ArgsStrings(0)
|
||||
pluck := process.ArgsMap(1)
|
||||
return ArrayPluck(columns, pluck)
|
||||
}
|
||||
|
||||
// ProcessArraySplit xiang.helper.ArraySplit 将多条数记录集合,分解为一个 columns:[]string 和 values: [][]interface{}
|
||||
func ProcessArraySplit(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
records := process.ArgsRecords(0)
|
||||
columns, values := ArraySplit(records)
|
||||
return map[string]interface{}{
|
||||
"columns": columns,
|
||||
"values": values,
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessArrayColumn xiang.helper.ArrayColumn 返回多条数据记录,指定字段数值。
|
||||
func ProcessArrayColumn(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
name := process.ArgsString(1)
|
||||
values := ArrayColumn(records, name)
|
||||
return values
|
||||
}
|
||||
|
||||
// ProcessArrayKeep xiang.helper.ArrayKeep 仅保留指定键名的数据
|
||||
func ProcessArrayKeep(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
columns := process.ArgsStrings(1)
|
||||
return ArrayKeep(records, columns)
|
||||
}
|
||||
|
||||
// ProcessArrayTree xiang.helper.ArrayTree 转换为属性结构
|
||||
func ProcessArrayTree(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
setting := process.ArgsMap(1)
|
||||
return ArrayTree(records, setting)
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
)
|
||||
|
||||
|
|
@ -106,3 +107,50 @@ func TestArrayTree(t *testing.T) {
|
|||
res := ArrayTree(records, map[string]interface{}{"parent": "parent_id"})
|
||||
assert.Equal(t, 9, len(res))
|
||||
}
|
||||
|
||||
func TestProcessArrayPluck(t *testing.T) {
|
||||
args := []interface{}{
|
||||
[]interface{}{"城市", "行业", "计费"},
|
||||
map[string]interface{}{
|
||||
"行业": map[string]interface{}{"key": "city", "value": "数量", "items": []map[string]interface{}{{"city": "北京", "数量": 32}, {"city": "上海", "数量": 20}}},
|
||||
"计费": map[string]interface{}{"key": "city", "value": "计费种类", "items": []map[string]interface{}{{"city": "北京", "计费种类": 6}, {"city": "西安", "计费种类": 3}}},
|
||||
},
|
||||
}
|
||||
process := gou.NewProcess("xiang.helper.ArrayPluck", args...)
|
||||
response := ProcessArrayPluck(process)
|
||||
assert.NotNil(t, response)
|
||||
items, ok := response.([]map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, 3, len(items))
|
||||
for _, item := range items {
|
||||
maps.Of(item).Has("城市")
|
||||
maps.Of(item).Has("行业")
|
||||
maps.Of(item).Has("计费")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessArraySplit(t *testing.T) {
|
||||
args := []interface{}{
|
||||
[]map[string]interface{}{
|
||||
{"name": "阿里云计算有限公司", "short_name": "阿里云"},
|
||||
{"name": "世纪互联蓝云", "short_name": "上海蓝云"},
|
||||
},
|
||||
}
|
||||
process := gou.NewProcess("xiang.helper.ArraySplit", args...)
|
||||
response := process.Run()
|
||||
assert.NotNil(t, response)
|
||||
res, ok := response.(map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
|
||||
columns, ok := res["columns"].([]string)
|
||||
assert.True(t, ok)
|
||||
|
||||
values, ok := res["values"].([][]interface{})
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 2, len(columns))
|
||||
assert.Equal(t, 2, len(values))
|
||||
for _, value := range values {
|
||||
assert.Equal(t, 2, len(value))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
170
helper/captcha.go
Normal file
170
helper/captcha.go
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/any"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
"github.com/yaoapp/xiang/config"
|
||||
"github.com/yaoapp/xiang/xlog"
|
||||
)
|
||||
|
||||
var captchaStore = base64Captcha.DefaultMemStore
|
||||
|
||||
// CaptchaOption 验证码配置
|
||||
type CaptchaOption struct {
|
||||
Type string
|
||||
Height int
|
||||
Width int
|
||||
Length int
|
||||
Lang string
|
||||
Background string
|
||||
}
|
||||
|
||||
// NewCaptchaOption 创建验证码配置
|
||||
func NewCaptchaOption() CaptchaOption {
|
||||
return CaptchaOption{
|
||||
Width: 240,
|
||||
Height: 80,
|
||||
Length: 4,
|
||||
Lang: "zh",
|
||||
Background: "#FFFFFF",
|
||||
}
|
||||
}
|
||||
|
||||
// CaptchaMake 制作验证码
|
||||
func CaptchaMake(option CaptchaOption) (string, string) {
|
||||
|
||||
if option.Width == 0 {
|
||||
option.Width = 240
|
||||
}
|
||||
|
||||
if option.Height == 0 {
|
||||
option.Width = 80
|
||||
}
|
||||
|
||||
if option.Length == 0 {
|
||||
option.Length = 4
|
||||
}
|
||||
|
||||
if option.Lang == "" {
|
||||
option.Lang = "zh"
|
||||
}
|
||||
|
||||
var driver base64Captcha.Driver
|
||||
switch option.Type {
|
||||
case "audio":
|
||||
driver = base64Captcha.NewDriverAudio(option.Length, option.Lang)
|
||||
break
|
||||
case "math":
|
||||
background := captchaBackground(option.Background)
|
||||
driver = base64Captcha.NewDriverMath(
|
||||
option.Height, option.Width, 3,
|
||||
base64Captcha.OptionShowHollowLine, background,
|
||||
base64Captcha.DefaultEmbeddedFonts, []string{},
|
||||
)
|
||||
break
|
||||
default:
|
||||
driver = base64Captcha.NewDriverDigit(
|
||||
option.Height, option.Width, 5,
|
||||
0.7, 80,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
c := base64Captcha.NewCaptcha(driver, captchaStore)
|
||||
id, content, err := c.Generate()
|
||||
if err != nil {
|
||||
exception.New("生成验证码出错 %s", 500, err).Throw()
|
||||
}
|
||||
|
||||
// 打印日志
|
||||
if config.IsDebug() {
|
||||
xlog.Println("图形/音频 ID:", id, "验证码:", captchaStore.Get(id, false))
|
||||
}
|
||||
|
||||
return id, content
|
||||
}
|
||||
|
||||
// CaptchaValidate 校验验证码
|
||||
func CaptchaValidate(id string, value string) bool {
|
||||
return captchaStore.Verify(id, value, true)
|
||||
}
|
||||
|
||||
func captchaBackground(s string) *color.RGBA {
|
||||
if s == "" {
|
||||
s = "#555555"
|
||||
}
|
||||
bg, err := captchaParseHexColorFast(s)
|
||||
if err != nil {
|
||||
exception.New("背景色格式错误 %s", 400, s).Throw()
|
||||
}
|
||||
return &bg
|
||||
}
|
||||
|
||||
func captchaParseHexColorFast(s string) (c color.RGBA, err error) {
|
||||
c.A = 0xff
|
||||
|
||||
if s[0] != '#' {
|
||||
return c, errors.New("invalid format")
|
||||
}
|
||||
|
||||
hexToByte := func(b byte) byte {
|
||||
switch {
|
||||
case b >= '0' && b <= '9':
|
||||
return b - '0'
|
||||
case b >= 'a' && b <= 'f':
|
||||
return b - 'a' + 10
|
||||
case b >= 'A' && b <= 'F':
|
||||
return b - 'A' + 10
|
||||
}
|
||||
err = errors.New("invalid format")
|
||||
return 0
|
||||
}
|
||||
|
||||
switch len(s) {
|
||||
case 7:
|
||||
c.R = hexToByte(s[1])<<4 + hexToByte(s[2])
|
||||
c.G = hexToByte(s[3])<<4 + hexToByte(s[4])
|
||||
c.B = hexToByte(s[5])<<4 + hexToByte(s[6])
|
||||
case 4:
|
||||
c.R = hexToByte(s[1]) * 17
|
||||
c.G = hexToByte(s[2]) * 17
|
||||
c.B = hexToByte(s[3]) * 17
|
||||
default:
|
||||
err = errors.New("invalid format")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessCaptchaValidate xiang.helper.CaptchaValidate 校验图形/音频验证码
|
||||
func ProcessCaptchaValidate(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
if !CaptchaValidate(process.ArgsString(0), process.ArgsString(1)) {
|
||||
exception.New("验证码不正确", 400).Throw()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ProcessCaptcha xiang.helper.Captcha 校验图形/音频验证码
|
||||
func ProcessCaptcha(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
option := CaptchaOption{
|
||||
Width: any.Of(process.ArgsURLValue(0, "width", "240")).CInt(),
|
||||
Height: any.Of(process.ArgsURLValue(0, "height", "80")).CInt(),
|
||||
Length: any.Of(process.ArgsURLValue(0, "height", "4")).CInt(),
|
||||
Type: process.ArgsURLValue(0, "type", "math"),
|
||||
Background: process.ArgsURLValue(0, "background", "#FFFFFF"),
|
||||
Lang: process.ArgsURLValue(0, "lang", "zh"),
|
||||
}
|
||||
id, content := CaptchaMake(option)
|
||||
return maps.Map{
|
||||
"id": id,
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
65
helper/captcha_test.go
Normal file
65
helper/captcha_test.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
)
|
||||
|
||||
func TestCaptcha(t *testing.T) {
|
||||
id, content := CaptchaMake(CaptchaOption{
|
||||
Type: "audio",
|
||||
Width: 240,
|
||||
Height: 80,
|
||||
Length: 4,
|
||||
Lang: "zh",
|
||||
})
|
||||
assert.IsType(t, "string", id)
|
||||
assert.IsType(t, "string", content)
|
||||
captchaStore.Get(id, false)
|
||||
assert.True(t, CaptchaValidate(id, captchaStore.Get(id, false)))
|
||||
|
||||
id, content = CaptchaMake(CaptchaOption{
|
||||
Type: "math",
|
||||
Width: 240,
|
||||
Height: 80,
|
||||
Length: 4,
|
||||
Lang: "zh",
|
||||
})
|
||||
assert.IsType(t, "string", id)
|
||||
assert.IsType(t, "string", content)
|
||||
captchaStore.Get(id, false)
|
||||
assert.True(t, CaptchaValidate(id, captchaStore.Get(id, false)))
|
||||
|
||||
id, content = CaptchaMake(CaptchaOption{
|
||||
Type: "digit",
|
||||
Width: 240,
|
||||
Height: 80,
|
||||
Length: 4,
|
||||
Lang: "zh",
|
||||
})
|
||||
assert.IsType(t, "string", id)
|
||||
assert.IsType(t, "string", content)
|
||||
captchaStore.Get(id, false)
|
||||
assert.True(t, CaptchaValidate(id, captchaStore.Get(id, false)))
|
||||
}
|
||||
|
||||
func TestProcessCaptcha(t *testing.T) {
|
||||
args := url.Values{}
|
||||
args.Add("type", "math")
|
||||
args.Add("lang", "zh")
|
||||
process := gou.NewProcess("xiang.helper.Captcha", args)
|
||||
res := process.Run().(maps.Map)
|
||||
assert.IsType(t, "string", res.Get("id"))
|
||||
assert.IsType(t, "string", res.Get("content"))
|
||||
|
||||
value := captchaStore.Get(res.Get("id").(string), false)
|
||||
process = gou.NewProcess("xiang.helper.CaptchaValidate", res.Get("id"), value)
|
||||
assert.True(t, process.Run().(bool))
|
||||
assert.Panics(t, func() {
|
||||
gou.NewProcess("xiang.helper.CaptchaValidate", res.Get("id"), "xxx").Run()
|
||||
})
|
||||
}
|
||||
97
helper/jwt.go
Normal file
97
helper/jwt.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/xiang/config"
|
||||
)
|
||||
|
||||
// JwtClaims 用户Token
|
||||
type JwtClaims struct {
|
||||
ID int
|
||||
Data map[string]interface{}
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// JwtValidate JWT 校验
|
||||
func JwtValidate(tokenString string) map[string]interface{} {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.Conf.JWT.Secret), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
exception.New("令牌无效", 403).Ctx(err.Error()).Throw()
|
||||
return nil
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
|
||||
return claims.Data
|
||||
}
|
||||
|
||||
exception.New("令牌无效", 403).Ctx(token.Claims).Throw()
|
||||
return nil
|
||||
}
|
||||
|
||||
// JwtMake 生成 JWT
|
||||
// subject options[0], audience options[1], issuer options[1]
|
||||
func JwtMake(id int, data map[string]interface{}, timeout int64, options ...string) string {
|
||||
now := time.Now().Unix()
|
||||
expiresAt := now + timeout
|
||||
uid := fmt.Sprintf("%d", id)
|
||||
subject := "User Token"
|
||||
audience := "Xiang Metadata Admin Panel"
|
||||
issuer := fmt.Sprintf("xiang:%d", id)
|
||||
length := len(options)
|
||||
if length > 0 {
|
||||
subject = options[0]
|
||||
}
|
||||
if length > 1 {
|
||||
audience = options[1]
|
||||
}
|
||||
if length > 2 {
|
||||
issuer = options[2]
|
||||
}
|
||||
claims := &JwtClaims{
|
||||
ID: id,
|
||||
Data: data,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
Id: uid, // 唯一ID
|
||||
Subject: subject, // 主题
|
||||
Audience: audience, // 接收人
|
||||
ExpiresAt: expiresAt, // 过期时间
|
||||
NotBefore: now, // 生效时间
|
||||
IssuedAt: now, // 签发时间
|
||||
Issuer: issuer, // 签发人
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(config.Conf.JWT.Secret))
|
||||
if err != nil {
|
||||
exception.New("生成令牌失败", 500).Ctx(err).Throw()
|
||||
}
|
||||
return tokenString
|
||||
}
|
||||
|
||||
// ProcessJwtMake xiang.helper.JwtMake 生成JWT
|
||||
func ProcessJwtMake(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(3)
|
||||
id := process.ArgsInt(0)
|
||||
data := process.ArgsMap(1)
|
||||
timeout := int64(process.ArgsInt(2))
|
||||
args := []string{}
|
||||
for i := 3; i < len(process.Args); i++ {
|
||||
args = append(args, fmt.Sprintf("%v", process.Args[i]))
|
||||
}
|
||||
return JwtMake(id, data, timeout, args...)
|
||||
}
|
||||
|
||||
// ProcessJwtValidate xiang.helper.JwtValidate 校验JWT
|
||||
func ProcessJwtValidate(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
tokenString := process.ArgsString(0)
|
||||
return JwtValidate(tokenString)
|
||||
}
|
||||
32
helper/jwt_test.go
Normal file
32
helper/jwt_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/gou"
|
||||
)
|
||||
|
||||
func TestJwt(t *testing.T) {
|
||||
data := map[string]interface{}{"hello": "world", "id": 1}
|
||||
tokenString := JwtMake(1, data, 1, "Unit Test", "Test", "UnitTest")
|
||||
res := JwtValidate(tokenString)
|
||||
assert.Equal(t, float64(1), res["id"])
|
||||
assert.Equal(t, "world", res["hello"])
|
||||
time.Sleep(2 * time.Second)
|
||||
assert.Panics(t, func() { JwtValidate(tokenString) })
|
||||
}
|
||||
|
||||
func TestProcessJwt(t *testing.T) {
|
||||
data := map[string]interface{}{"hello": "world", "id": 1}
|
||||
args := []interface{}{1, data, 1, "Unit Test", "Test", "UnitTest"}
|
||||
process := gou.NewProcess("xiang.helper.JwtMake", args...)
|
||||
token := process.Run()
|
||||
tokenString := token.(string)
|
||||
res := gou.NewProcess("xiang.helper.JwtValidate", tokenString).Run().(map[string]interface{})
|
||||
assert.Equal(t, float64(1), res["id"])
|
||||
assert.Equal(t, "world", res["hello"])
|
||||
time.Sleep(2 * time.Second)
|
||||
assert.Panics(t, func() { gou.NewProcess("xiang.helper.JwtValidate", tokenString).Run() })
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package helper
|
||||
|
||||
import "github.com/yaoapp/gou"
|
||||
|
||||
// MapValues 返回映射的数值
|
||||
func MapValues(record map[string]interface{}) []interface{} {
|
||||
values := []interface{}{}
|
||||
|
|
@ -17,3 +19,17 @@ func MapKeys(record map[string]interface{}) []string {
|
|||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// ProcessMapValues xiang.helper.MapValues 返回映射的数值
|
||||
func ProcessMapValues(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapValues(record)
|
||||
}
|
||||
|
||||
// ProcessMapKeys xiang.helper.MapKeys 返回映射的键
|
||||
func ProcessMapKeys(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapKeys(record)
|
||||
}
|
||||
|
|
|
|||
21
helper/password.go
Normal file
21
helper/password.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"github.com/yaoapp/gou"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// PasswordValidate 校验密码
|
||||
func PasswordValidate(password string, passwordHash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ProcessPasswordValidate xiang.helper.PasswordValidate 校验密码
|
||||
func ProcessPasswordValidate(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
return PasswordValidate(process.ArgsString(0), process.ArgsString(1))
|
||||
}
|
||||
27
helper/password_test.go
Normal file
27
helper/password_test.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/gou"
|
||||
)
|
||||
|
||||
func TestPassword(t *testing.T) {
|
||||
assert.True(t, PasswordValidate("U123456p+", "$2a$04$TS/rWBs66jADjQl8fa.w..ivkNAjH8d4sI1OPGvEB9Leed6EpzIF2"))
|
||||
assert.False(t, PasswordValidate("U123456p+", "123456"))
|
||||
}
|
||||
|
||||
func TestProcessPassword(t *testing.T) {
|
||||
pwd := "U123456p+"
|
||||
hash := "$2a$04$TS/rWBs66jADjQl8fa.w..ivkNAjH8d4sI1OPGvEB9Leed6EpzIF2"
|
||||
args := []interface{}{pwd, hash}
|
||||
process := gou.NewProcess("xiang.helper.PasswordValidate", args...)
|
||||
res := process.Run()
|
||||
assert.True(t, res.(bool))
|
||||
|
||||
args = []interface{}{pwd, "123456"}
|
||||
process = gou.NewProcess("xiang.helper.PasswordValidate", args...)
|
||||
res = process.Run()
|
||||
assert.False(t, res.(bool))
|
||||
}
|
||||
|
|
@ -14,88 +14,16 @@ func init() {
|
|||
gou.RegisterProcessHandler("xiang.helper.ArrayTree", ProcessArrayTree)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapKeys", ProcessMapKeys)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
||||
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
|
||||
gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate)
|
||||
gou.RegisterProcessHandler("xiang.helper.PasswordValidate", ProcessPasswordValidate)
|
||||
gou.RegisterProcessHandler("xiang.helper.JwtMake", ProcessJwtMake)
|
||||
gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate)
|
||||
gou.RegisterProcessHandler("xiang.helper.For", ProcessFor)
|
||||
gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach)
|
||||
gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint)
|
||||
}
|
||||
|
||||
// ProcessArrayPluck xiang.helper.ArrayPluck 将多个数据记录集合,合并为一个数据记录集合
|
||||
func ProcessArrayPluck(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
columns := process.ArgsStrings(0)
|
||||
pluck := process.ArgsMap(1)
|
||||
return ArrayPluck(columns, pluck)
|
||||
}
|
||||
|
||||
// ProcessArraySplit xiang.helper.ArraySplit 将多条数记录集合,分解为一个 columns:[]string 和 values: [][]interface{}
|
||||
func ProcessArraySplit(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
records := process.ArgsRecords(0)
|
||||
columns, values := ArraySplit(records)
|
||||
return map[string]interface{}{
|
||||
"columns": columns,
|
||||
"values": values,
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessArrayColumn xiang.helper.ArrayColumn 返回多条数据记录,指定字段数值。
|
||||
func ProcessArrayColumn(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
name := process.ArgsString(1)
|
||||
values := ArrayColumn(records, name)
|
||||
return values
|
||||
}
|
||||
|
||||
// ProcessArrayKeep xiang.helper.ArrayKeep 仅保留指定键名的数据
|
||||
func ProcessArrayKeep(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
columns := process.ArgsStrings(1)
|
||||
return ArrayKeep(records, columns)
|
||||
}
|
||||
|
||||
// ProcessArrayTree xiang.helper.ArrayTree 转换为属性结构
|
||||
func ProcessArrayTree(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
setting := process.ArgsMap(1)
|
||||
return ArrayTree(records, setting)
|
||||
}
|
||||
|
||||
// ProcessMapValues xiang.helper.MapValues 返回映射的数值
|
||||
func ProcessMapValues(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapValues(record)
|
||||
}
|
||||
|
||||
// ProcessMapKeys xiang.helper.MapKeys 返回映射的键
|
||||
func ProcessMapKeys(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
record := process.ArgsMap(0)
|
||||
return MapKeys(record)
|
||||
}
|
||||
|
||||
// ProcessEach xiang.helper.Each 循环过程控制
|
||||
func ProcessEach(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
v := process.Args[0]
|
||||
p := ProcessOf(process.ArgsMap(1))
|
||||
Range(v, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessFor xiang.helper.For 循环过程控制
|
||||
func ProcessFor(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(3)
|
||||
from := process.ArgsInt(0)
|
||||
to := process.ArgsInt(1)
|
||||
p := ProcessOf(process.ArgsMap(2))
|
||||
For(from, to, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessPrint xiang.helper.Print 打印语句
|
||||
func ProcessPrint(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
)
|
||||
|
||||
func TestProcessArrayPluck(t *testing.T) {
|
||||
args := []interface{}{
|
||||
[]interface{}{"城市", "行业", "计费"},
|
||||
map[string]interface{}{
|
||||
"行业": map[string]interface{}{"key": "city", "value": "数量", "items": []map[string]interface{}{{"city": "北京", "数量": 32}, {"city": "上海", "数量": 20}}},
|
||||
"计费": map[string]interface{}{"key": "city", "value": "计费种类", "items": []map[string]interface{}{{"city": "北京", "计费种类": 6}, {"city": "西安", "计费种类": 3}}},
|
||||
},
|
||||
}
|
||||
process := gou.NewProcess("xiang.helper.ArrayPluck", args...)
|
||||
response := ProcessArrayPluck(process)
|
||||
assert.NotNil(t, response)
|
||||
items, ok := response.([]map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, 3, len(items))
|
||||
for _, item := range items {
|
||||
maps.Of(item).Has("城市")
|
||||
maps.Of(item).Has("行业")
|
||||
maps.Of(item).Has("计费")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessArraySplit(t *testing.T) {
|
||||
args := []interface{}{
|
||||
[]map[string]interface{}{
|
||||
{"name": "阿里云计算有限公司", "short_name": "阿里云"},
|
||||
{"name": "世纪互联蓝云", "short_name": "上海蓝云"},
|
||||
},
|
||||
}
|
||||
process := gou.NewProcess("xiang.helper.ArraySplit", args...)
|
||||
response := process.Run()
|
||||
assert.NotNil(t, response)
|
||||
res, ok := response.(map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
|
||||
columns, ok := res["columns"].([]string)
|
||||
assert.True(t, ok)
|
||||
|
||||
values, ok := res["values"].([][]interface{})
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 2, len(columns))
|
||||
assert.Equal(t, 2, len(values))
|
||||
for _, value := range values {
|
||||
assert.Equal(t, 2, len(value))
|
||||
}
|
||||
}
|
||||
|
|
@ -139,3 +139,22 @@ func ProcessOf(v map[string]interface{}) Process {
|
|||
Args: []interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessEach xiang.helper.Each 循环过程控制
|
||||
func ProcessEach(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
v := process.Args[0]
|
||||
p := ProcessOf(process.ArgsMap(1))
|
||||
Range(v, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessFor xiang.helper.For 循环过程控制
|
||||
func ProcessFor(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(3)
|
||||
from := process.ArgsInt(0)
|
||||
to := process.ArgsInt(1)
|
||||
p := ProcessOf(process.ArgsMap(2))
|
||||
For(from, to, p)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue