mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
first
This commit is contained in:
41
base/assert.go
Normal file
41
base/assert.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func BizErrIsNil(err error, msg string) {
|
||||
if err != nil {
|
||||
panic(NewBizErr(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func ErrIsNil(err error, msg string) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func IsTrue(exp bool, msg string) {
|
||||
if !exp {
|
||||
panic(NewBizErr(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func NotEmpty(str string, msg string) {
|
||||
if str == "" {
|
||||
panic(NewBizErr(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func NotNil(data interface{}, msg string) {
|
||||
if reflect.ValueOf(data).IsNil() {
|
||||
panic(NewBizErr(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func Nil(data interface{}, msg string) {
|
||||
if !reflect.ValueOf(data).IsNil() {
|
||||
panic(NewBizErr(msg))
|
||||
}
|
||||
}
|
||||
27
base/bizerror.go
Normal file
27
base/bizerror.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package base
|
||||
|
||||
// 业务错误
|
||||
type BizError struct {
|
||||
code int16
|
||||
err string
|
||||
}
|
||||
|
||||
// 错误消息
|
||||
func (e *BizError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// 错误码
|
||||
func (e *BizError) Code() int16 {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBizErr(msg string) BizError {
|
||||
return BizError{code: BizErrorCode, err: msg}
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,可设置指定错误code
|
||||
func NewBizErrCode(code int16, msg string) BizError {
|
||||
return BizError{code: code, err: msg}
|
||||
}
|
||||
141
base/controller.go
Normal file
141
base/controller.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/validation"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// 获取数据函数
|
||||
type getDataFunc func(loginAccount *LoginAccount) interface{}
|
||||
|
||||
// 操作函数,无返回数据
|
||||
type operationFunc func(loginAccount *LoginAccount)
|
||||
|
||||
// 将请求体的json赋值给指定的结构体
|
||||
func (c *Controller) UnmarshalBody(data interface{}) {
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, data)
|
||||
BizErrIsNil(err, "request body解析错误")
|
||||
}
|
||||
|
||||
// 校验表单数据
|
||||
func (c *Controller) validForm(form interface{}) {
|
||||
valid := validation.Validation{}
|
||||
b, err := valid.Valid(form)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !b {
|
||||
e := valid.Errors[0]
|
||||
panic(NewBizErr(e.Field + " " + e.Message))
|
||||
}
|
||||
}
|
||||
|
||||
// 将请求体的json赋值给指定的结构体,并校验表单数据
|
||||
func (c *Controller) UnmarshalBodyAndValid(data interface{}) {
|
||||
c.UnmarshalBody(data)
|
||||
c.validForm(data)
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
// @param checkToken 是否校验token
|
||||
// @param getData 获取数据的回调函数
|
||||
func (c *Controller) ReturnData(checkToken bool, getData getDataFunc) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.parseErr(err)
|
||||
}
|
||||
}()
|
||||
var loginAccount *LoginAccount
|
||||
if checkToken {
|
||||
loginAccount = c.CheckToken()
|
||||
}
|
||||
c.Success(getData(loginAccount))
|
||||
}
|
||||
|
||||
// 无返回数据的操作,如新增修改等无需返回数据的操作
|
||||
// @param checkToken 是否校验token
|
||||
func (c *Controller) Operation(checkToken bool, operation operationFunc) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.parseErr(err)
|
||||
}
|
||||
}()
|
||||
var loginAccount *LoginAccount
|
||||
if checkToken {
|
||||
loginAccount = c.CheckToken()
|
||||
}
|
||||
operation(loginAccount)
|
||||
c.SuccessNoData()
|
||||
}
|
||||
|
||||
// 校验token,并返回登录者账号信息
|
||||
func (c *Controller) CheckToken() *LoginAccount {
|
||||
tokenStr := c.Ctx.Input.Header("Authorization")
|
||||
loginAccount, err := ParseToken(tokenStr)
|
||||
if err != nil || loginAccount == nil {
|
||||
panic(NewBizErrCode(TokenErrorCode, TokenErrorMsg))
|
||||
}
|
||||
return loginAccount
|
||||
}
|
||||
|
||||
// 获取分页参数
|
||||
func (c *Controller) GetPageParam() *PageParam {
|
||||
pn, err := c.GetInt("pageNum", 1)
|
||||
BizErrIsNil(err, "pageNum参数错误")
|
||||
ps, serr := c.GetInt("pageSize", 10)
|
||||
BizErrIsNil(serr, "pageSize参数错误")
|
||||
return &PageParam{PageNum: pn, PageSize: ps}
|
||||
}
|
||||
|
||||
// 统一返回Result json对象
|
||||
func (c *Controller) Result(result *Result) {
|
||||
c.Data["json"] = result
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// 返回成功结果
|
||||
func (c *Controller) Success(data interface{}) {
|
||||
c.Result(Success(data))
|
||||
}
|
||||
|
||||
// 返回成功结果
|
||||
func (c *Controller) SuccessNoData() {
|
||||
c.Result(SuccessNoData())
|
||||
}
|
||||
|
||||
// 返回业务错误
|
||||
func (c *Controller) BizError(bizError BizError) {
|
||||
c.Result(Error(bizError.Code(), bizError.Error()))
|
||||
}
|
||||
|
||||
// 返回服务器错误结果
|
||||
func (c *Controller) ServerError() {
|
||||
c.Result(ServerError())
|
||||
}
|
||||
|
||||
// 解析error,并对不同error返回不同result
|
||||
func (c *Controller) parseErr(err interface{}) {
|
||||
switch t := err.(type) {
|
||||
case BizError:
|
||||
c.BizError(t)
|
||||
break
|
||||
case error:
|
||||
c.ServerError()
|
||||
logs.Error(t)
|
||||
panic(err)
|
||||
//break
|
||||
case string:
|
||||
c.ServerError()
|
||||
logs.Error(t)
|
||||
panic(err)
|
||||
//break
|
||||
default:
|
||||
logs.Error(t)
|
||||
}
|
||||
}
|
||||
161
base/httpclient/httpclient.go
Normal file
161
base/httpclient/httpclient.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 默认超时
|
||||
const DefTimeout = 60
|
||||
|
||||
type RequestWrapper struct {
|
||||
url string
|
||||
method string
|
||||
timeout int
|
||||
body io.Reader
|
||||
header map[string]string
|
||||
}
|
||||
|
||||
// 创建一个请求
|
||||
func NewRequest(url string) *RequestWrapper {
|
||||
return &RequestWrapper{url: url}
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) Url(url string) *RequestWrapper {
|
||||
r.url = url
|
||||
return r
|
||||
}
|
||||
func (r *RequestWrapper) Timeout(timeout int) *RequestWrapper {
|
||||
r.timeout = timeout
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) GetByParam(paramMap map[string]string) ResponseWrapper {
|
||||
var params string
|
||||
for k, v := range paramMap {
|
||||
if params != "" {
|
||||
params += "&"
|
||||
} else {
|
||||
params += "?"
|
||||
}
|
||||
params += k + "=" + v
|
||||
}
|
||||
r.url += "?" + params
|
||||
return r.Get()
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) Get() ResponseWrapper {
|
||||
r.method = "GET"
|
||||
r.body = nil
|
||||
return request(r)
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) PostJson(body string) ResponseWrapper {
|
||||
buf := bytes.NewBufferString(body)
|
||||
r.method = "POST"
|
||||
r.body = buf
|
||||
if r.header == nil {
|
||||
r.header = make(map[string]string)
|
||||
}
|
||||
r.header["Content-type"] = "application/json"
|
||||
return request(r)
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) PostObj(body interface{}) ResponseWrapper {
|
||||
marshal, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return createRequestError(errors.New("解析json obj错误"))
|
||||
}
|
||||
return r.PostJson(string(marshal))
|
||||
}
|
||||
|
||||
func (r *RequestWrapper) PostParams(params string) ResponseWrapper {
|
||||
buf := bytes.NewBufferString(params)
|
||||
r.method = "POST"
|
||||
r.body = buf
|
||||
if r.header == nil {
|
||||
r.header = make(map[string]string)
|
||||
}
|
||||
r.header["Content-type"] = "application/x-www-form-urlencoded"
|
||||
return request(r)
|
||||
}
|
||||
|
||||
type ResponseWrapper struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
func (r *ResponseWrapper) IsSuccess() bool {
|
||||
return r.StatusCode == 200
|
||||
}
|
||||
|
||||
func (r *ResponseWrapper) ToObj(obj interface{}) {
|
||||
if !r.IsSuccess() {
|
||||
return
|
||||
}
|
||||
_ = json.Unmarshal([]byte(r.Body), &obj)
|
||||
}
|
||||
|
||||
func (r *ResponseWrapper) ToMap() map[string]interface{} {
|
||||
if !r.IsSuccess() {
|
||||
return nil
|
||||
}
|
||||
var res map[string]interface{}
|
||||
err := json.Unmarshal([]byte(r.Body), &res)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func request(rw *RequestWrapper) ResponseWrapper {
|
||||
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
|
||||
client := &http.Client{}
|
||||
timeout := rw.timeout
|
||||
if timeout > 0 {
|
||||
client.Timeout = time.Duration(timeout) * time.Second
|
||||
} else {
|
||||
timeout = DefTimeout
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(rw.method, rw.url, rw.body)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
setRequestHeader(req, rw.header)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
wrapper.StatusCode = resp.StatusCode
|
||||
wrapper.Body = string(body)
|
||||
wrapper.Header = resp.Header
|
||||
|
||||
return wrapper
|
||||
}
|
||||
|
||||
func setRequestHeader(req *http.Request, header map[string]string) {
|
||||
req.Header.Set("User-Agent", "golang/mayflyjob")
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func createRequestError(err error) ResponseWrapper {
|
||||
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
|
||||
return ResponseWrapper{0, errorMessage, make(http.Header)}
|
||||
}
|
||||
216
base/model.go
Normal file
216
base/model.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/siddontang/go/log"
|
||||
"mayfly-go/base/utils"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id uint64 `orm:"column(id);auto" json:"id"`
|
||||
CreateTime time.Time `orm:"column(create_time);type(datetime);null" json:"createTime"`
|
||||
CreatorId uint64 `orm:"column(creator_id)" json:"creatorId"`
|
||||
Creator string `orm:"column(creator)" json:"creator"`
|
||||
UpdateTime time.Time `orm:"column(update_time);type(datetime);null" json:"updateTime"`
|
||||
ModifierId uint64 `orm:"column(modifier_id)" json:"modifierId"`
|
||||
Modifier string `orm:"column(modifier)" json:"modifier"`
|
||||
}
|
||||
|
||||
// 获取orm querySeter
|
||||
func QuerySetter(table interface{}) orm.QuerySeter {
|
||||
return getOrm().QueryTable(table)
|
||||
}
|
||||
|
||||
// 获取分页结果
|
||||
func GetPage(seter orm.QuerySeter, pageParam *PageParam, models interface{}, toModels interface{}) PageResult {
|
||||
count, _ := seter.Count()
|
||||
if count == 0 {
|
||||
return PageResult{Total: 0, List: nil}
|
||||
}
|
||||
_, qerr := seter.Limit(pageParam.PageSize, pageParam.PageNum-1).All(models, getFieldNames(toModels)...)
|
||||
BizErrIsNil(qerr, "查询错误")
|
||||
err := utils.Copy(toModels, models)
|
||||
BizErrIsNil(err, "实体转换错误")
|
||||
return PageResult{Total: count, List: toModels}
|
||||
}
|
||||
|
||||
// 根据sql获取分页对象
|
||||
func GetPageBySql(sql string, toModel interface{}, param *PageParam, args ...interface{}) PageResult {
|
||||
selectIndex := strings.Index(sql, "SELECT ") + 7
|
||||
fromIndex := strings.Index(sql, " FROM")
|
||||
selectCol := sql[selectIndex:fromIndex]
|
||||
countSql := strings.Replace(sql, selectCol, "COUNT(*) AS total ", 1)
|
||||
// 查询count
|
||||
o := getOrm()
|
||||
type TotalRes struct {
|
||||
Total int64
|
||||
}
|
||||
var totalRes TotalRes
|
||||
_ = o.Raw(countSql, args).QueryRow(&totalRes)
|
||||
total := totalRes.Total
|
||||
if total == 0 {
|
||||
return PageResult{Total: 0, List: nil}
|
||||
}
|
||||
// 分页查询
|
||||
limitSql := sql + " LIMIT " + strconv.Itoa(param.PageNum-1) + ", " + strconv.Itoa(param.PageSize)
|
||||
var maps []orm.Params
|
||||
_, err := o.Raw(limitSql, args).Values(&maps)
|
||||
if err != nil {
|
||||
panic(errors.New("查询错误 : " + err.Error()))
|
||||
}
|
||||
e := ormParams2Struct(maps, toModel)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return PageResult{Total: total, List: toModel}
|
||||
}
|
||||
|
||||
func GetListBySql(sql string, params ...interface{}) *[]orm.Params {
|
||||
var maps []orm.Params
|
||||
_, err := getOrm().Raw(sql, params).Values(&maps)
|
||||
if err != nil {
|
||||
log.Error("根据sql查询数据列表失败:%s", err.Error())
|
||||
}
|
||||
return &maps
|
||||
}
|
||||
|
||||
// 获取所有列表数据
|
||||
func GetList(seter orm.QuerySeter, model interface{}, toModel interface{}) {
|
||||
_, _ = seter.All(model, getFieldNames(toModel)...)
|
||||
err := utils.Copy(toModel, model)
|
||||
BizErrIsNil(err, "实体转换错误")
|
||||
}
|
||||
|
||||
// 根据toModel结构体字段查询单条记录,并将值赋值给toModel
|
||||
func GetOne(seter orm.QuerySeter, model interface{}, toModel interface{}) error {
|
||||
err := seter.One(model, getFieldNames(toModel)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cerr := utils.Copy(toModel, model)
|
||||
BizErrIsNil(cerr, "实体转换错误")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据实体以及指定字段值查询实体,若字段数组为空,则默认用id查
|
||||
func GetBy(model interface{}, fs ...string) error {
|
||||
err := getOrm().Read(model, fs...)
|
||||
if err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return errors.New("该数据不存在")
|
||||
} else {
|
||||
return errors.New("查询失败")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Insert(model interface{}) error {
|
||||
_, err := getOrm().Insert(model)
|
||||
if err != nil {
|
||||
return errors.New("数据插入失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Update(model interface{}, fs ...string) error {
|
||||
_, err := getOrm().Update(model, fs...)
|
||||
if err != nil {
|
||||
return errors.New("数据更新失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Delete(model interface{}, fs ...string) error {
|
||||
_, err := getOrm().Delete(model, fs...)
|
||||
if err != nil {
|
||||
return errors.New("数据删除失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOrm() orm.Ormer {
|
||||
return orm.NewOrm()
|
||||
}
|
||||
|
||||
// 结果模型缓存
|
||||
var resultModelCache = make(map[string][]string)
|
||||
|
||||
// 获取实体对象的字段名
|
||||
func getFieldNames(obj interface{}) []string {
|
||||
objType := indirectType(reflect.TypeOf(obj))
|
||||
cacheKey := objType.PkgPath() + "." + objType.Name()
|
||||
cache := resultModelCache[cacheKey]
|
||||
if cache != nil {
|
||||
return cache
|
||||
}
|
||||
cache = getFieldNamesByType("", reflect.TypeOf(obj))
|
||||
resultModelCache[cacheKey] = cache
|
||||
return cache
|
||||
}
|
||||
|
||||
func indirectType(reflectType reflect.Type) reflect.Type {
|
||||
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
|
||||
reflectType = reflectType.Elem()
|
||||
}
|
||||
return reflectType
|
||||
}
|
||||
|
||||
func getFieldNamesByType(namePrefix string, reflectType reflect.Type) []string {
|
||||
var fieldNames []string
|
||||
|
||||
if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
|
||||
for i := 0; i < reflectType.NumField(); i++ {
|
||||
t := reflectType.Field(i)
|
||||
tName := t.Name
|
||||
// 判断结构体字段是否为结构体,是的话则跳过
|
||||
it := indirectType(t.Type)
|
||||
if it.Kind() == reflect.Struct {
|
||||
itName := it.Name()
|
||||
// 如果包含Time或time则表示为time类型,无需递归该结构体字段
|
||||
if !strings.Contains(itName, "BaseModel") && !strings.Contains(itName, "Time") &&
|
||||
!strings.Contains(itName, "time") {
|
||||
fieldNames = append(fieldNames, getFieldNamesByType(tName+"__", it)...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if t.Anonymous {
|
||||
fieldNames = append(fieldNames, getFieldNamesByType("", t.Type)...)
|
||||
} else {
|
||||
fieldNames = append(fieldNames, namePrefix+tName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fieldNames
|
||||
}
|
||||
|
||||
func ormParams2Struct(maps []orm.Params, structs interface{}) error {
|
||||
structsV := reflect.Indirect(reflect.ValueOf(structs))
|
||||
valType := structsV.Type()
|
||||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
length := len(maps)
|
||||
|
||||
valSlice := structsV
|
||||
if valSlice.IsNil() {
|
||||
// Make a new slice to hold our result, same size as the original data.
|
||||
valSlice = reflect.MakeSlice(sliceType, length, length)
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
err := utils.Map2Struct(maps[i], valSlice.Index(i).Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
structsV.Set(valSlice)
|
||||
return nil
|
||||
}
|
||||
71
base/model_test.go
Normal file
71
base/model_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/orm"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/controllers/vo"
|
||||
"mayfly-go/models"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type AccountDetailVO struct {
|
||||
Id int64
|
||||
Username string
|
||||
}
|
||||
|
||||
func init() {
|
||||
orm.RegisterDriver("mysql", orm.DRMySQL)
|
||||
|
||||
orm.RegisterDataBase("default", "mysql", "root:111049@tcp(localhost:3306)/mayfly-go?charset=utf8")
|
||||
orm.Debug = true
|
||||
}
|
||||
|
||||
func TestGetList(t *testing.T) {
|
||||
query := QuerySetter(new(models.Account)).OrderBy("-Id")
|
||||
list := new([]AccountDetailVO)
|
||||
GetList(query, new([]models.Account), list)
|
||||
fmt.Println(list)
|
||||
}
|
||||
|
||||
func TestGetOne(t *testing.T) {
|
||||
model := new(models.Account)
|
||||
query := QuerySetter(model).Filter("Id", 2)
|
||||
adv := new(AccountDetailVO)
|
||||
GetOne(query, model, adv)
|
||||
fmt.Println(adv)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
//o := getOrm()
|
||||
//
|
||||
////v := new([]Account)
|
||||
//var maps []orm.Params
|
||||
//_, err := o.Raw("SELECT a.Id, a.Username, r.Id AS 'Role.Id', r.Name AS 'Role.Name' FROM " +
|
||||
// "t_account a JOIN t_role r ON a.id = r.account_id").Values(&maps)
|
||||
//fmt.Println(err)
|
||||
//////res := new([]Account)
|
||||
////model := &Account{}
|
||||
////o.QueryTable("t_account").Filter("id", 1).RelatedSel().One(model)
|
||||
////o.LoadRelated(model, "Role")
|
||||
res := new([]vo.AccountVO)
|
||||
sql := "SELECT a.Id, a.Username, r.Id AS 'Role.Id', r.Name AS 'Role.Name' FROM t_account a JOIN t_role r ON a.id = r.account_id"
|
||||
//limitSql := sql + " LIMIT 1, 3"
|
||||
//selectIndex := strings.Index(sql, "SELECT ") + 7
|
||||
//fromIndex := strings.Index(sql, " FROM")
|
||||
//selectCol := sql[selectIndex:fromIndex]
|
||||
//countSql := strings.Replace(sql, selectCol, "COUNT(*)", 1)
|
||||
//fmt.Println(limitSql)
|
||||
//fmt.Println(selectCol)
|
||||
//fmt.Println(countSql)
|
||||
page := GetPageBySql(sql, res, &PageParam{PageNum: 1, PageSize: 1})
|
||||
fmt.Println(page)
|
||||
//return res
|
||||
}
|
||||
|
||||
func TestCase2Camel(t *testing.T) {
|
||||
fmt.Println(utils.Case2Camel("create_time"))
|
||||
fmt.Println(strings.Title("username"))
|
||||
}
|
||||
13
base/page.go
Normal file
13
base/page.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package base
|
||||
|
||||
// 分页参数
|
||||
type PageParam struct {
|
||||
PageNum int `json:"pageNum"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// 分页结果
|
||||
type PageResult struct {
|
||||
Total int64 `json:"total"`
|
||||
List interface{} `json:"list"`
|
||||
}
|
||||
66
base/result.go
Normal file
66
base/result.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
SuccessCode = 200
|
||||
SuccessMsg = "success"
|
||||
|
||||
BizErrorCode = 400
|
||||
BizErrorMsg = "error"
|
||||
|
||||
ServerErrorCode = 500
|
||||
ServerErrorMsg = "server error"
|
||||
|
||||
TokenErrorCode = 501
|
||||
TokenErrorMsg = "token error"
|
||||
)
|
||||
|
||||
// 统一返回结果结构体
|
||||
type Result struct {
|
||||
Code int16 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// 将Result转为json字符串
|
||||
func (r *Result) ToJson() string {
|
||||
jsonData, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
fmt.Println("data转json错误")
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
// 判断该Result是否为成功状态
|
||||
func (r *Result) IsSuccess() bool {
|
||||
return r.Code == SuccessCode
|
||||
}
|
||||
|
||||
// 返回成功状态的Result
|
||||
// @param data 成功附带的数据消息
|
||||
func Success(data interface{}) *Result {
|
||||
return &Result{Code: SuccessCode, Msg: SuccessMsg, Data: data}
|
||||
}
|
||||
|
||||
// 返回成功状态的Result
|
||||
// @param data 成功不附带数据
|
||||
func SuccessNoData() *Result {
|
||||
return &Result{Code: SuccessCode, Msg: SuccessMsg}
|
||||
}
|
||||
|
||||
// 返回服务器错误Result
|
||||
func ServerError() *Result {
|
||||
return &Result{Code: ServerErrorCode, Msg: ServerErrorMsg}
|
||||
}
|
||||
|
||||
func Error(code int16, msg string) *Result {
|
||||
return &Result{Code: code, Msg: msg}
|
||||
}
|
||||
|
||||
func TokenError() *Result {
|
||||
return &Result{Code: TokenErrorCode, Msg: TokenErrorMsg}
|
||||
}
|
||||
49
base/token.go
Normal file
49
base/token.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
JwtKey = "mykey"
|
||||
ExpTime = time.Hour * 24 * 7
|
||||
)
|
||||
|
||||
type LoginAccount struct {
|
||||
Id uint64
|
||||
Username string
|
||||
}
|
||||
|
||||
// 创建用户token
|
||||
func CreateToken(userId uint64, username string) string {
|
||||
// 带权限创建令牌
|
||||
// 设置有效期,过期需要重新登录获取token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"id": userId,
|
||||
"username": username,
|
||||
"exp": time.Now().Add(ExpTime).Unix(),
|
||||
})
|
||||
|
||||
// 使用自定义字符串加密 and get the complete encoded token as a string
|
||||
tokenString, err := token.SignedString([]byte(JwtKey))
|
||||
BizErrIsNil(err, "token创建失败")
|
||||
return tokenString
|
||||
}
|
||||
|
||||
// 解析token,并返回登录者账号信息
|
||||
func ParseToken(tokenStr string) (*LoginAccount, error) {
|
||||
if tokenStr == "" {
|
||||
return nil, errors.New("token error")
|
||||
}
|
||||
// Parse token
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(JwtKey), nil
|
||||
})
|
||||
if err != nil || token == nil {
|
||||
return nil, err
|
||||
}
|
||||
i := token.Claims.(jwt.MapClaims)
|
||||
return &LoginAccount{Id: uint64(i["id"].(float64)), Username: i["username"].(string)}, nil
|
||||
}
|
||||
23
base/utils/map_utils.go
Normal file
23
base/utils/map_utils.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func GetString4Map(m map[string]interface{}, key string) string {
|
||||
return m[key].(string)
|
||||
}
|
||||
|
||||
func GetInt4Map(m map[string]interface{}, key string) int {
|
||||
i := m[key]
|
||||
iKind := reflect.TypeOf(i).Kind()
|
||||
if iKind == reflect.Int {
|
||||
return i.(int)
|
||||
}
|
||||
if iKind == reflect.String {
|
||||
i, _ := strconv.Atoi(i.(string))
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
89
base/utils/str_utils.go
Normal file
89
base/utils/str_utils.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// 可判断中文
|
||||
func StrLen(str string) int {
|
||||
return len([]rune(str))
|
||||
}
|
||||
|
||||
// 去除字符串左右空字符
|
||||
func StrTrim(str string) string {
|
||||
return strings.Trim(str, " ")
|
||||
}
|
||||
|
||||
func SubString(str string, begin, end int) (substr string) {
|
||||
// 将字符串的转换成[]rune
|
||||
rs := []rune(str)
|
||||
lth := len(rs)
|
||||
|
||||
// 简单的越界判断
|
||||
if begin < 0 {
|
||||
begin = 0
|
||||
}
|
||||
if begin >= lth {
|
||||
begin = lth
|
||||
}
|
||||
if end > lth {
|
||||
end = lth
|
||||
}
|
||||
|
||||
// 返回子串
|
||||
return string(rs[begin:end])
|
||||
}
|
||||
|
||||
func UnicodeIndex(str, substr string) int {
|
||||
// 子串在字符串的字节位置
|
||||
result := strings.Index(str, substr)
|
||||
if result >= 0 {
|
||||
// 获得子串之前的字符串并转换成[]byte
|
||||
prefix := []byte(str)[0:result]
|
||||
// 将子串之前的字符串转换成[]rune
|
||||
rs := []rune(string(prefix))
|
||||
// 获得子串之前的字符串的长度,便是子串在字符串的字符位置
|
||||
result = len(rs)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 字符串模板解析
|
||||
func TemplateResolve(temp string, data interface{}) string {
|
||||
t, _ := template.New("string-temp").Parse(temp)
|
||||
var tmplBytes bytes.Buffer
|
||||
|
||||
err := t.Execute(&tmplBytes, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmplBytes.String()
|
||||
}
|
||||
|
||||
func ReverStrTemplate(temp, str string, res map[string]interface{}) {
|
||||
index := UnicodeIndex(temp, "{")
|
||||
ei := UnicodeIndex(temp, "}") + 1
|
||||
next := StrTrim(temp[ei:])
|
||||
nextContain := UnicodeIndex(next, "{")
|
||||
nextIndexValue := next
|
||||
if nextContain != -1 {
|
||||
nextIndexValue = SubString(next, 0, nextContain)
|
||||
}
|
||||
key := temp[index+1 : ei-1]
|
||||
// 如果后面没有内容了,则取字符串的长度即可
|
||||
var valueLastIndex int
|
||||
if nextIndexValue == "" {
|
||||
valueLastIndex = StrLen(str)
|
||||
} else {
|
||||
valueLastIndex = UnicodeIndex(str, nextIndexValue)
|
||||
}
|
||||
value := StrTrim(SubString(str, index, valueLastIndex))
|
||||
res[key] = value
|
||||
// 如果后面的还有需要解析的,则递归调用解析
|
||||
if nextContain != -1 {
|
||||
ReverStrTemplate(next, StrTrim(SubString(str, UnicodeIndex(str, value)+StrLen(value), StrLen(str))), res)
|
||||
}
|
||||
}
|
||||
629
base/utils/struct_utils.go
Normal file
629
base/utils/struct_utils.go
Normal file
@@ -0,0 +1,629 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Copy copy things,引用至copier
|
||||
func Copy(toValue interface{}, fromValue interface{}) (err error) {
|
||||
var (
|
||||
isSlice bool
|
||||
amount = 1
|
||||
from = Indirect(reflect.ValueOf(fromValue))
|
||||
to = Indirect(reflect.ValueOf(toValue))
|
||||
)
|
||||
|
||||
if !to.CanAddr() {
|
||||
return errors.New("copy to value is unaddressable")
|
||||
}
|
||||
|
||||
// Return is from value is invalid
|
||||
if !from.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
fromType := IndirectType(from.Type())
|
||||
toType := IndirectType(to.Type())
|
||||
|
||||
// Just set it if possible to assign
|
||||
// And need to do copy anyway if the type is struct
|
||||
if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
|
||||
to.Set(from)
|
||||
return
|
||||
}
|
||||
|
||||
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
if to.Kind() == reflect.Slice {
|
||||
isSlice = true
|
||||
if from.Kind() == reflect.Slice {
|
||||
amount = from.Len()
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < amount; i++ {
|
||||
var dest, source reflect.Value
|
||||
|
||||
if isSlice {
|
||||
// source
|
||||
if from.Kind() == reflect.Slice {
|
||||
source = Indirect(from.Index(i))
|
||||
} else {
|
||||
source = Indirect(from)
|
||||
}
|
||||
// dest
|
||||
dest = Indirect(reflect.New(toType).Elem())
|
||||
} else {
|
||||
source = Indirect(from)
|
||||
dest = Indirect(to)
|
||||
}
|
||||
|
||||
// check source
|
||||
if source.IsValid() {
|
||||
fromTypeFields := deepFields(fromType)
|
||||
//fmt.Printf("%#v", fromTypeFields)
|
||||
// Copy from field to field or method
|
||||
for _, field := range fromTypeFields {
|
||||
name := field.Name
|
||||
|
||||
if fromField := source.FieldByName(name); fromField.IsValid() {
|
||||
// has field
|
||||
if toField := dest.FieldByName(name); toField.IsValid() {
|
||||
if toField.CanSet() {
|
||||
if !set(toField, fromField) {
|
||||
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to set to method
|
||||
var toMethod reflect.Value
|
||||
if dest.CanAddr() {
|
||||
toMethod = dest.Addr().MethodByName(name)
|
||||
} else {
|
||||
toMethod = dest.MethodByName(name)
|
||||
}
|
||||
|
||||
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
|
||||
toMethod.Call([]reflect.Value{fromField})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from method to field
|
||||
for _, field := range deepFields(toType) {
|
||||
name := field.Name
|
||||
|
||||
var fromMethod reflect.Value
|
||||
if source.CanAddr() {
|
||||
fromMethod = source.Addr().MethodByName(name)
|
||||
} else {
|
||||
fromMethod = source.MethodByName(name)
|
||||
}
|
||||
|
||||
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
|
||||
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
|
||||
values := fromMethod.Call([]reflect.Value{})
|
||||
if len(values) >= 1 {
|
||||
set(toField, values[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if isSlice {
|
||||
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
|
||||
to.Set(reflect.Append(to, dest.Addr()))
|
||||
} else if dest.Type().AssignableTo(to.Type().Elem()) {
|
||||
to.Set(reflect.Append(to, dest))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func deepFields(reflectType reflect.Type) []reflect.StructField {
|
||||
var fields []reflect.StructField
|
||||
|
||||
if reflectType = IndirectType(reflectType); reflectType.Kind() == reflect.Struct {
|
||||
for i := 0; i < reflectType.NumField(); i++ {
|
||||
v := reflectType.Field(i)
|
||||
if v.Anonymous {
|
||||
fields = append(fields, deepFields(v.Type)...)
|
||||
} else {
|
||||
fields = append(fields, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func Indirect(reflectValue reflect.Value) reflect.Value {
|
||||
for reflectValue.Kind() == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
return reflectValue
|
||||
}
|
||||
|
||||
func IndirectType(reflectType reflect.Type) reflect.Type {
|
||||
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
|
||||
reflectType = reflectType.Elem()
|
||||
}
|
||||
return reflectType
|
||||
}
|
||||
|
||||
func set(to, from reflect.Value) bool {
|
||||
if from.IsValid() {
|
||||
if to.Kind() == reflect.Ptr {
|
||||
//set `to` to nil if from is nil
|
||||
if from.Kind() == reflect.Ptr && from.IsNil() {
|
||||
to.Set(reflect.Zero(to.Type()))
|
||||
return true
|
||||
} else if to.IsNil() {
|
||||
to.Set(reflect.New(to.Type().Elem()))
|
||||
}
|
||||
to = to.Elem()
|
||||
}
|
||||
|
||||
if from.Type().ConvertibleTo(to.Type()) {
|
||||
to.Set(from.Convert(to.Type()))
|
||||
} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
|
||||
err := scanner.Scan(from.Interface())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
} else if from.Kind() == reflect.Ptr {
|
||||
return set(to, from.Elem())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Map2Struct(m map[string]interface{}, s interface{}) error {
|
||||
toValue := Indirect(reflect.ValueOf(s))
|
||||
if !toValue.CanAddr() {
|
||||
return errors.New("to value is unaddressable")
|
||||
}
|
||||
|
||||
innerStructMaps := getInnerStructMaps(m)
|
||||
if len(innerStructMaps) != 0 {
|
||||
for k, v := range innerStructMaps {
|
||||
var fieldV reflect.Value
|
||||
if strings.Contains(k, ".") {
|
||||
fieldV = getFiledValueByPath(k, toValue)
|
||||
} else {
|
||||
fieldV = toValue.FieldByName(k)
|
||||
}
|
||||
|
||||
if !fieldV.CanSet() || !fieldV.CanAddr() {
|
||||
continue
|
||||
}
|
||||
fieldT := fieldV.Type().Elem()
|
||||
if fieldT.Kind() != reflect.Struct {
|
||||
return errors.New(k + "不是结构体")
|
||||
}
|
||||
// 如果值为nil,则默认创建一个并赋值
|
||||
if fieldV.IsNil() {
|
||||
fieldV.Set(reflect.New(fieldT))
|
||||
}
|
||||
err := Map2Struct(v, fieldV.Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
for k, v := range m {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
k = strings.Title(k)
|
||||
// 如果key含有下划线,则将其转为驼峰
|
||||
if strings.Contains(k, "_") {
|
||||
k = Case2Camel(k)
|
||||
}
|
||||
|
||||
fieldV := toValue.FieldByName(k)
|
||||
if !fieldV.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
err = decode(k, v, fieldV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Maps2Structs(maps []map[string]interface{}, structs interface{}) error {
|
||||
structsV := reflect.Indirect(reflect.ValueOf(structs))
|
||||
valType := structsV.Type()
|
||||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
length := len(maps)
|
||||
|
||||
valSlice := structsV
|
||||
if valSlice.IsNil() {
|
||||
// Make a new slice to hold our result, same size as the original data.
|
||||
valSlice = reflect.MakeSlice(sliceType, length, length)
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
err := Map2Struct(maps[i], valSlice.Index(i).Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
structsV.Set(valSlice)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFiledValueByPath(path string, value reflect.Value) reflect.Value {
|
||||
split := strings.Split(path, ".")
|
||||
for _, v := range split {
|
||||
if value.Type().Kind() == reflect.Ptr {
|
||||
// 如果值为nil,则创建并赋值
|
||||
if value.IsNil() {
|
||||
value.Set(reflect.New(IndirectType(value.Type())))
|
||||
}
|
||||
value = value.Elem()
|
||||
}
|
||||
value = value.FieldByName(v)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func getInnerStructMaps(m map[string]interface{}) map[string]map[string]interface{} {
|
||||
key2map := make(map[string]map[string]interface{})
|
||||
for k, v := range m {
|
||||
if !strings.Contains(k, ".") {
|
||||
continue
|
||||
}
|
||||
lastIndex := strings.LastIndex(k, ".")
|
||||
prefix := k[0:lastIndex]
|
||||
m2 := key2map[prefix]
|
||||
if m2 == nil {
|
||||
key2map[prefix] = map[string]interface{}{k[lastIndex+1:]: v}
|
||||
} else {
|
||||
m2[k[lastIndex+1:]] = v
|
||||
}
|
||||
delete(m, k)
|
||||
}
|
||||
return key2map
|
||||
}
|
||||
|
||||
// decode等方法摘抄自mapstructure库
|
||||
|
||||
func decode(name string, input interface{}, outVal reflect.Value) error {
|
||||
var inputVal reflect.Value
|
||||
if input != nil {
|
||||
inputVal = reflect.ValueOf(input)
|
||||
|
||||
// We need to check here if input is a typed nil. Typed nils won't
|
||||
// match the "input == nil" below so we check that here.
|
||||
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
|
||||
input = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !inputVal.IsValid() {
|
||||
// If the input value is invalid, then we just set the value
|
||||
// to be the zero value.
|
||||
outVal.Set(reflect.Zero(outVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
outputKind := getKind(outVal)
|
||||
switch outputKind {
|
||||
case reflect.Int:
|
||||
err = decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = decodeFloat(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = decodeString(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
_, err = decodePtr(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeInt(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetInt(dataVal.Int())
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetInt(int64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetInt(int64(dataVal.Float()))
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetInt(1)
|
||||
} else {
|
||||
val.SetInt(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetInt(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as int: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Int64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
val.SetInt(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUint(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
i := dataVal.Int()
|
||||
if i < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %d overflows uint",
|
||||
name, i)
|
||||
}
|
||||
val.SetUint(uint64(i))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetUint(dataVal.Uint())
|
||||
case dataKind == reflect.Float32:
|
||||
f := dataVal.Float()
|
||||
if f < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %f overflows uint",
|
||||
name, f)
|
||||
}
|
||||
val.SetUint(uint64(f))
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetUint(1)
|
||||
} else {
|
||||
val.SetUint(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetUint(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Int64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
if i < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %d overflows uint",
|
||||
name, i)
|
||||
}
|
||||
val.SetUint(uint64(i))
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeFloat(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetFloat(float64(dataVal.Int()))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetFloat(float64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetFloat(dataVal.Float())
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetFloat(1)
|
||||
} else {
|
||||
val.SetFloat(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetFloat(f)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as float: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Float64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
val.SetFloat(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeString(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
converted := true
|
||||
switch {
|
||||
case dataKind == reflect.String:
|
||||
val.SetString(dataVal.String())
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetString("1")
|
||||
} else {
|
||||
val.SetString("0")
|
||||
}
|
||||
case dataKind == reflect.Int:
|
||||
val.SetString(strconv.FormatInt(dataVal.Int(), 10))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetString(strconv.FormatUint(dataVal.Uint(), 10))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))
|
||||
case dataKind == reflect.Slice,
|
||||
dataKind == reflect.Array:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
switch elemKind {
|
||||
case reflect.Uint8:
|
||||
var uints []uint8
|
||||
if dataKind == reflect.Array {
|
||||
uints = make([]uint8, dataVal.Len(), dataVal.Len())
|
||||
for i := range uints {
|
||||
uints[i] = dataVal.Index(i).Interface().(uint8)
|
||||
}
|
||||
} else {
|
||||
uints = dataVal.Interface().([]uint8)
|
||||
}
|
||||
val.SetString(string(uints))
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
|
||||
if !converted {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodePtr(name string, data interface{}, val reflect.Value) (bool, error) {
|
||||
// If the input data is nil, then we want to just set the output
|
||||
// pointer to be nil as well.
|
||||
isNil := data == nil
|
||||
if !isNil {
|
||||
switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {
|
||||
case reflect.Chan,
|
||||
reflect.Func,
|
||||
reflect.Interface,
|
||||
reflect.Map,
|
||||
reflect.Ptr,
|
||||
reflect.Slice:
|
||||
isNil = v.IsNil()
|
||||
}
|
||||
}
|
||||
if isNil {
|
||||
if !val.IsNil() && val.CanSet() {
|
||||
nilValue := reflect.New(val.Type()).Elem()
|
||||
val.Set(nilValue)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Create an element of the concrete (non pointer) type and decode
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
if val.CanSet() {
|
||||
realVal := val
|
||||
if realVal.IsNil() {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
|
||||
if err := decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
val.Set(realVal)
|
||||
} else {
|
||||
if err := decode(name, data, reflect.Indirect(val)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getKind(val reflect.Value) reflect.Kind {
|
||||
kind := val.Kind()
|
||||
|
||||
switch {
|
||||
case kind >= reflect.Int && kind <= reflect.Int64:
|
||||
return reflect.Int
|
||||
case kind >= reflect.Uint && kind <= reflect.Uint64:
|
||||
return reflect.Uint
|
||||
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
||||
return reflect.Float32
|
||||
default:
|
||||
return kind
|
||||
}
|
||||
}
|
||||
|
||||
// 下划线写法转为驼峰写法
|
||||
func Case2Camel(name string) string {
|
||||
name = strings.Replace(name, "_", " ", -1)
|
||||
name = strings.Title(name)
|
||||
return strings.Replace(name, " ", "", -1)
|
||||
}
|
||||
|
||||
func isBlank(value reflect.Value) bool {
|
||||
switch value.Kind() {
|
||||
case reflect.String:
|
||||
return value.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !value.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return value.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return value.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return value.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return value.IsNil()
|
||||
}
|
||||
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
|
||||
}
|
||||
195
base/utils/struct_utils_test.go
Normal file
195
base/utils/struct_utils_test.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Src struct {
|
||||
Id *int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
CreateTime time.Time `json:"time"`
|
||||
UpdateTime time.Time
|
||||
Inner *SrcInner
|
||||
}
|
||||
|
||||
type SrcInner struct {
|
||||
Name string
|
||||
Desc string
|
||||
Id int64
|
||||
Dest *Dest
|
||||
}
|
||||
|
||||
type Dest struct {
|
||||
Username string
|
||||
Id int64
|
||||
CreateTime time.Time
|
||||
Inner *DestInner
|
||||
}
|
||||
|
||||
type DestInner struct {
|
||||
Desc string
|
||||
}
|
||||
|
||||
func TestDeepFields(t *testing.T) {
|
||||
////src := Src{Username: "test", Id: 1000, CreateTime: time.Now()}
|
||||
//si := SrcInner{Desc: "desc"}
|
||||
//src.Inner = &si
|
||||
////src.Id = 1222
|
||||
//dest := new(Dest)
|
||||
//err := structutils.Copy(dest, src)
|
||||
//if err != nil {
|
||||
// fmt.Println(err.Error())
|
||||
//} else {
|
||||
// fmt.Println(dest)
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
func TestGetFieldNames(t *testing.T) {
|
||||
//names := structutils.GetFieldNames(new(Src))
|
||||
//fmt.Println(names)
|
||||
}
|
||||
|
||||
func TestMaps2Structs(t *testing.T) {
|
||||
mapInstance := make(map[string]interface{})
|
||||
mapInstance["Username"] = "liang637210"
|
||||
mapInstance["Id"] = 28
|
||||
mapInstance["CreateTime"] = time.Now()
|
||||
mapInstance["Creator"] = "createor"
|
||||
mapInstance["Inner.Id"] = 10
|
||||
mapInstance["Inner.Name"] = "hahah"
|
||||
mapInstance["Inner.Desc"] = "inner desc"
|
||||
mapInstance["Inner.Dest.Username"] = "inner dest uername"
|
||||
mapInstance["Inner.Dest.Inner.Desc"] = "inner dest inner desc"
|
||||
|
||||
mapInstance2 := make(map[string]interface{})
|
||||
mapInstance2["Username"] = "liang6372102"
|
||||
mapInstance2["Id"] = 282
|
||||
mapInstance2["CreateTime"] = time.Now()
|
||||
mapInstance2["Creator"] = "createor2"
|
||||
mapInstance2["Inner.Id"] = 102
|
||||
mapInstance2["Inner.Name"] = "hahah2"
|
||||
mapInstance2["Inner.Desc"] = "inner desc2"
|
||||
mapInstance2["Inner.Dest.Username"] = "inner dest uername2"
|
||||
mapInstance2["Inner.Dest.Inner.Desc"] = "inner dest inner desc2"
|
||||
|
||||
maps := make([]map[string]interface{}, 2)
|
||||
maps[0] = mapInstance
|
||||
maps[1] = mapInstance2
|
||||
res := new([]Src)
|
||||
err := Maps2Structs(maps, res)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMap2Struct(t *testing.T) {
|
||||
mapInstance := make(map[string]interface{})
|
||||
mapInstance["Username"] = "liang637210"
|
||||
mapInstance["Id"] = 12
|
||||
mapInstance["CreateTime"] = time.Now()
|
||||
mapInstance["Creator"] = "createor"
|
||||
mapInstance["Inner.Id"] = nil
|
||||
mapInstance["Inner.Name"] = "hahah"
|
||||
mapInstance["Inner.Desc"] = "inner desc"
|
||||
mapInstance["Inner.Dest.Username"] = "inner dest uername"
|
||||
mapInstance["Inner.Dest.Inner.Desc"] = "inner dest inner desc"
|
||||
|
||||
//innerMap := make(map[string]interface{})
|
||||
//innerMap["Name"] = "Innername"
|
||||
|
||||
//a := new(Src)
|
||||
////a.Inner = new(SrcInner)
|
||||
//
|
||||
//stime := time.Now().UnixNano()
|
||||
//for i := 0; i < 1000000; i++ {
|
||||
// err := structutils.Map2Struct(mapInstance, a)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//}
|
||||
//etime := time.Now().UnixNano()
|
||||
//fmt.Println(etime - stime)
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//} else {
|
||||
// fmt.Println(a)
|
||||
//}
|
||||
|
||||
s := new(Src)
|
||||
//name, b := structutils.IndirectType(reflect.TypeOf(s)).FieldByName("Inner")
|
||||
//if structutils.IndirectType(name.Type).Kind() != reflect.Struct {
|
||||
// fmt.Println(name.Name + "不是结构体")
|
||||
//} else {
|
||||
// //innerType := name.Type
|
||||
// innerValue := structutils.Indirect(reflect.ValueOf(s)).FieldByName("Inner")
|
||||
// //if innerValue.IsValid() && innerValue.IsNil() {
|
||||
// // innerValue.Set(reflect.New(innerValue.Type().Elem()))
|
||||
// //}
|
||||
// if !innerValue.IsValid() {
|
||||
// fmt.Println("is valid")
|
||||
// } else {
|
||||
// //innerValue.Set(reflect.New(innerValue.Type()))
|
||||
// fmt.Println(innerValue.CanSet())
|
||||
// fmt.Println(innerValue.CanAddr())
|
||||
// //mapstructure.Decode(innerMap, innerValue.Addr().Interface())
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//fmt.Println(name, b)
|
||||
//将 map 转换为指定的结构体
|
||||
if err := mapstructure.Decode(mapInstance, &s); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Printf("map2struct后得到的 struct 内容为:%v", s)
|
||||
}
|
||||
|
||||
func getPrefixKeyMap(m map[string]interface{}) map[string]map[string]interface{} {
|
||||
key2map := make(map[string]map[string]interface{})
|
||||
for k, v := range m {
|
||||
if !strings.Contains(k, ".") {
|
||||
continue
|
||||
}
|
||||
lastIndex := strings.LastIndex(k, ".")
|
||||
prefix := k[0:lastIndex]
|
||||
m2 := key2map[prefix]
|
||||
if m2 == nil {
|
||||
key2map[prefix] = map[string]interface{}{k[lastIndex+1:]: v}
|
||||
} else {
|
||||
m2[k[lastIndex+1:]] = v
|
||||
}
|
||||
delete(m, k)
|
||||
}
|
||||
return key2map
|
||||
}
|
||||
|
||||
func TestReflect(t *testing.T) {
|
||||
type dog struct {
|
||||
LegCount int
|
||||
}
|
||||
// 获取dog实例的反射值对象
|
||||
valueOfDog := reflect.ValueOf(&dog{}).Elem()
|
||||
|
||||
// 获取legCount字段的值
|
||||
vLegCount := valueOfDog.FieldByName("LegCount")
|
||||
|
||||
fmt.Println(vLegCount.CanSet())
|
||||
fmt.Println(vLegCount.CanAddr())
|
||||
// 尝试设置legCount的值(这里会发生崩溃)
|
||||
vLegCount.SetInt(4)
|
||||
}
|
||||
|
||||
func TestTemplateResolve(t *testing.T) {
|
||||
d := make(map[string]string)
|
||||
d["Name"] = "黄先生"
|
||||
d["Age"] = "23jlfdsjf"
|
||||
resolve := TemplateResolve("{{.Name}} is name, and {{.Age}} is age", d)
|
||||
fmt.Println(resolve)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user