新增应用管理

This commit is contained in:
guosl 2024-07-05 18:45:12 +08:00
parent c4f066a0d5
commit 330f16a3f6
25 changed files with 1643 additions and 43 deletions

View File

@ -105,7 +105,8 @@ CREATE TABLE IF NOT EXISTS `application`(
`created_on` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '记录创建时间',
`modified_by` VARCHAR(64) DEFAULT '' COMMENT '修改人',
`modified_on` DATETIME DEFAULT CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP COMMENT '记录修改时间',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code`(`code`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='应用表';
@ -127,8 +128,9 @@ CREATE TABLE IF NOT EXISTS `menu`(
`created_on` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '记录创建时间',
`modified_by` VARCHAR(64) DEFAULT '' COMMENT '修改人',
`modified_on` DATETIME DEFAULT CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP COMMENT '记录修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
PRIMARY KEY (`id`),
UNIQUE KEY `uk_menu_code`(`app_code`,`code`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='菜单表';
-- 角色菜单权限表
CREATE TABLE IF NOT EXISTS `role_menu_permission`(

1
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect

2
go.sum
View File

@ -59,6 +59,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=

View File

@ -0,0 +1,40 @@
package application
import "time"
type ApplicationInfo struct {
ID uint `json:"id"` // id
Code string `json:"code"` // code
Secret string `json:"secret"` // secret
Name string `json:"name"` // name
Description string `json:"description"` // 描述
CreatedBy string `json:"created_by"` // 创建人
CreatedOn time.Time `json:"created_on"` // 记录创建时间
ModifiedBy string `json:"modified_by"` // 修改人
ModifiedOn time.Time `json:"modified_on"` // 记录修改时间
}
type Menu struct {
Id uint32 `json:"id"` // id
AppCode string `json:"app_code"` // 应用code
Code string `json:"code"` // 编码
Name string `json:"name"` // 名称
ParentCode string `json:"parent_code"` // 父级菜单编码
Type int32 `json:"type"` // 类型1一级导航2二级导航3三级导航
Order int32 `json:"order"` // 排序
Icon string `json:"icon"` // 图标
Path string `json:"path"` // 访问路径
ChildPath string `json:"child_path"` // 子级路径(前端用)
GoFirstChild int8 `json:"go_first_child"` // 前端用
IsShow int32 `json:"is_show"` // 是否显示01
Children []Menu `json:"children"`
}
type MenuTree struct {
Menus []Menu `json:"menus"`
}
type Application struct {
Info ApplicationInfo `json:"info"`
Menus []Menu `json:"menus"`
}

View File

@ -0,0 +1,81 @@
package application
type CreateReq struct {
Code string `json:"code" binding:"required"`
Secret string `json:"secret" binding:"required,len=32"`
Name string `json:"Name" binding:"required"`
Description string `json:"description"`
}
type ResetSecretReq struct {
Id uint `json:"id" binding:"required"`
Secret string `json:"secret" binding:"required,len=32"`
}
type Query struct {
Page int `form:"page" json:"page" `
PageSize int `form:"page_size" json:"page_size"`
Keyword string `form:"keyword" Json:"keyword"`
Sort string `form:"sort" json:"sort" binding:"sql_sort"`
}
func (q *Query) Default() {
if q.Page < 0 {
q.Page = 0
}
if q.PageSize <= 0 {
q.PageSize = 20
}
if len(q.Sort) == 0 {
q.Sort = "created_on desc"
}
}
type List struct {
List []ApplicationInfo `json:"list"`
}
type MenusReq struct {
AppCode string `json:"app_code" binding:"required"`
}
type DeleteAppReq struct {
Id uint `json:"id" binding:"required"`
}
type GetAppReq struct {
Id uint `form:"id" json:"id" binding:"required"`
}
type CreateMenuReq struct {
AppCode string `json:"app_code" binding:"required"` // 应用code
Code string `json:"code" binding:"required"` // 编码
Name string `json:"name" binding:"required"` // 名称
ParentCode string `json:"parent_code"` // 父级菜单编码
Order int32 `json:"order" binding:"required"` // 排序
Icon string `json:"icon"` // 图标
Path string `json:"path" binding:"required"` // 访问路径
ChildPath string `json:"child_path"` // 子级路径(前端用)
GoFirstChild int8 `json:"go_first_child"` // 前端用
IsShow int32 `json:"is_show" binding:"oneof=0,1"` // 是否显示01
}
type DelMenuReq struct {
Id uint `json:"id" binding:"required"`
}
type ModifyMenuReq struct {
Id uint `json:"id" binding:"required"`
AppCode string `json:"app_code" binding:"required"` // 应用code
Code string `json:"code" binding:"required"` // 编码
Name string `json:"name" binding:"required"` // 名称
ParentCode string `json:"parent_code"` // 父级菜单编码
Order int `json:"order" binding:"required"` // 排序
Icon string `json:"icon"` // 图标
Path string `json:"path" binding:"required"` // 访问路径
ChildPath string `json:"child_path"` // 子级路径(前端用)
GoFirstChild int `json:"go_first_child"` // 前端用
IsShow int `json:"is_show" binding:"oneof=0,1"` // 是否显示01
}

View File

@ -0,0 +1,96 @@
// Code generated by sql2gorm. DO NOT EDIT.
package repo
import (
"time"
"context"
"github.com/samber/do"
"gorm.io/gorm"
"fmt"
)
func init(){
do.Provide[ApplicationRepo](nil, NewApplicationRepoS)
}
// 应用表
type Application struct {
ID uint `gorm:"column:id;primary_key;AUTO_INCREMENT"` // id
Code string `gorm:"column:code;NOT NULL"` // code
Secret string `gorm:"column:secret;NOT NULL"` // secret
Name string `gorm:"column:name;NOT NULL"` // name
Description string `gorm:"column:description"` // 描述
CreatedBy string `gorm:"column:created_by"` // 创建人
CreatedOn time.Time `gorm:"column:created_on;default:CURRENT_TIMESTAMP;NOT NULL"` // 记录创建时间
ModifiedBy string `gorm:"column:modified_by"` // 修改人
ModifiedOn time.Time `gorm:"column:modified_on;default:CURRENT_TIMESTAMP"` // 记录修改时间
}
func (m *Application) TableName() string {
return "application"
}
type ApplicationRepo interface {
GetAppByCode(ctx context.Context, code string) (app Application, err error)
GetAppById(ctx context.Context, id uint) (app Application, err error)
SaveApplication(ctx context.Context, app Application) error
CreateApplication(ctx context.Context, app Application) error
Search(ctx context.Context, query Query) ([]Application, error)
ResetSecret(ctx context.Context, app Application) error
DelApp(ctx context.Context,id uint)error
}
type applicationRepoS struct {
db *gorm.DB
}
func NewApplicationRepoS(i *do.Injector) (ApplicationRepo, error) {
return &applicationRepoS{
db: do.MustInvoke[*gorm.DB](i),
}, nil
}
func (u *applicationRepoS) GetAppByCode(ctx context.Context, code string) (application Application, err error) {
err = u.db.Where("code = ?", code).Take(&application).Error
return
}
func (u *applicationRepoS)GetAppById(ctx context.Context, id uint) (app Application, err error) {
err = u.db.Where("id = ?", id).Take(&app).Error
return
}
func (u *applicationRepoS) SaveApplication(ctx context.Context, application Application) error {
if application.ID <= 0 {
return fmt.Errorf("错误应用id:%d", application.ID)
}
return u.db.Save(application).Error
}
func (u *applicationRepoS) CreateApplication(ctx context.Context, application Application) error {
return u.db.Create(application).Error
}
func (u *applicationRepoS) Search(ctx context.Context, query Query) ([]Application, error) {
applications := make([]Application, 0)
keyword := fmt.Sprintf("%%%s%%", query.Keyword)
db := u.db.Model(&Application{})
if query.Keyword != "" {
db = db.Where("code like ? or name like ?", keyword, keyword)
}
err := db.Order(query.Sort).Limit(query.PageSize).Offset(query.Page * query.PageSize).Find(&applications).Error
return applications, err
}
func (u *applicationRepoS) ResetSecret(ctx context.Context, app Application) error {
err := u.db.Model(&Application{}).Where("id = ?", app.ID).Update("secret",app.Secret).Error
return err
}
func (u *applicationRepoS) DelApp(ctx context.Context,id uint)error{
return u.db.Where("id = ?",id).Delete(&Application{}).Error
}

8
internal/repo/base.go Normal file
View File

@ -0,0 +1,8 @@
package repo
type Query struct {
Page int `form:"page" json:"page" `
PageSize int `form:"page_size" json:"page_size"`
Keyword string `form:"keyword" Json:"keyword"`
Sort string `form:"sort" json:"sort" binding:"sql_sort"`
}

61
internal/repo/convert.go Normal file
View File

@ -0,0 +1,61 @@
package repo
import (
appModel "busniess-user-center/internal/models/application"
"sort"
"github.com/jinzhu/copier"
)
func convertMenu(menu Menu) appModel.Menu {
aMenu := appModel.Menu{}
copier.Copy(aMenu, menu)
return aMenu
}
func makeTreeStructure(menuArr []Menu) []appModel.Menu {
// 1、组装树形结构
// 数组转map
var levelOneMenuArr []appModel.Menu
parentCodeMenuMap := make(map[string][]appModel.Menu)
for _, menu := range menuArr {
// 一级菜单不处理
if len(menu.ParentCode) == 0 {
levelOneMenuArr = append(levelOneMenuArr, convertMenu(menu))
continue
}
// 二级菜单以下按照parentCode分组
parentCode := menu.ParentCode
if _, ok := parentCodeMenuMap[parentCode]; !ok {
parentCodeMenuMap[parentCode] = []appModel.Menu{convertMenu(menu)}
} else {
parentCodeMenuMap[parentCode] = append(parentCodeMenuMap[parentCode], convertMenu(menu))
}
}
// 2、处理子级并排序
for _, subMenuArr := range parentCodeMenuMap {
for _, menu := range subMenuArr {
children := parentCodeMenuMap[menu.Code]
sort.Slice(children, func(i, j int) bool {
return children[i].Order < children[j].Order
})
menu.Children = children
}
}
for _, menu := range levelOneMenuArr {
children := parentCodeMenuMap[menu.Code]
sort.Slice(children, func(i, j int) bool {
return children[i].Order < children[j].Order
})
menu.Children = children
}
sort.Slice(levelOneMenuArr, func(i, j int) bool {
return levelOneMenuArr[i].Order < levelOneMenuArr[j].Order
})
return levelOneMenuArr
}

91
internal/repo/menu.go Normal file
View File

@ -0,0 +1,91 @@
// Code generated by sql2gorm. DO NOT EDIT.
package repo
import (
"context"
"time"
"github.com/samber/do"
"gorm.io/gorm"
appModel "busniess-user-center/internal/models/application"
)
func init(){
do.Provide[ApplicationRepo](nil, NewApplicationRepoS)
}
// 菜单
type Menu struct {
ID uint `gorm:"column:id;primary_key;AUTO_INCREMENT"` // id
AppCode string `gorm:"column:app_code;NOT NULL"` // 应用code
Code string `gorm:"column:code;NOT NULL"` // 编码
Name string `gorm:"column:name;NOT NULL"` // 名称
ParentCode string `gorm:"column:parent_code;NOT NULL"` // 父级菜单编码
Order int `gorm:"column:order;default:0;NOT NULL"` // 排序
Icon string `gorm:"column:icon;NOT NULL"` // 图标
Path string `gorm:"column:path;NOT NULL"` // 访问路径
ChildPath string `gorm:"column:child_path;NOT NULL"` // 子级路径(前端用)
GoFirstChild int `gorm:"column:go_first_child;default:0;NOT NULL"` // 前端用
IsShow int `gorm:"column:is_show;default:0;NOT NULL"` // 是否显示01
CreatedBy string `gorm:"column:created_by"` // 创建人
CreatedOn time.Time `gorm:"column:created_on;default:CURRENT_TIMESTAMP;NOT NULL"` // 记录创建时间
ModifiedBy string `gorm:"column:modified_by"` // 修改人
ModifiedOn time.Time `gorm:"column:modified_on;default:CURRENT_TIMESTAMP"` // 记录修改时间
}
func (m *Menu) TableName() string {
return "menu"
}
type MenuRepo interface{
GetAppMenus(ctx context.Context,appCode string)([]appModel.Menu,error)
Create(ctx context.Context,menu *Menu)(id uint,err error)
DelMenuById(ctx context.Context,id uint)error
SaveMenu(ctx context.Context,menu *Menu)error
GetMenuById(ctx context.Context,id uint)(menu Menu,err error)
}
type menuRepo struct{
db *gorm.DB
}
func NewMenuRepo(i *do.Injector)(MenuRepo,error){
return &menuRepo{
db: do.MustInvoke[*gorm.DB](i),
}, nil
}
func (m *menuRepo)GetAppMenus(ctx context.Context,appCode string)(menus []appModel.Menu,err error){
dbMenus,err := m.getAppMenu(appCode)
if err != nil{
return
}
menus = makeTreeStructure(dbMenus)
return
}
func (m *menuRepo)getAppMenu(appCode string)(menus []Menu,err error){
err = m.db.Where("app_code = ?",appCode).Find(&menus).Error
return
}
func (m *menuRepo)Create(ctx context.Context,menu *Menu)(id uint,err error){
menu.ID = 0
err = m.db.Create(menu).Error
id = menu.ID
return
}
func (m *menuRepo)DelMenuById(ctx context.Context,id uint)error{
return m.db.Where("id = ?",id).Delete(&Menu{}).Error
}
func (m *menuRepo)SaveMenu(ctx context.Context,menu *Menu)error{
return m.db.Save(&menu).Error
}
func (m *menuRepo)GetMenuById(ctx context.Context,id uint)(menu Menu,err error){
err = m.db.Where("id = ?",id).Take(&menu).Error
return
}

View File

@ -1,7 +1,7 @@
package repo
import (
"busniess-user-center/internal/models"
userModel "busniess-user-center/internal/models/user"
"context"
"fmt"
"time"
@ -40,8 +40,8 @@ type UserRepo interface {
GetUserByEmail(ctx context.Context, mobile string) (user User, err error)
SaveUser(ctx context.Context, user User) error
CreateUser(ctx context.Context, user *User) error
SetUserStatus(ctx context.Context, id uint, status models.UserStatus) error
Search(ctx context.Context, query *models.Query) ([]User, error)
SetUserStatus(ctx context.Context, id uint, status userModel.UserStatus) error
Search(ctx context.Context, query *userModel.Query) ([]User, error)
ResetPwd(ctx context.Context, user User) error
}
@ -82,12 +82,12 @@ func (u *userRepoS) GetUserByEmail(ctx context.Context, email string) (user User
return
}
func (u *userRepoS) SetUserStatus(ctx context.Context, id uint, status models.UserStatus) error {
func (u *userRepoS) SetUserStatus(ctx context.Context, id uint, status userModel.UserStatus) error {
err := u.db.Model(&User{}).Where("id = ?", id).Update("status", status).Error
return err
}
func (u *userRepoS) Search(ctx context.Context, query *models.Query) ([]User, error) {
func (u *userRepoS) Search(ctx context.Context, query *userModel.Query) ([]User, error) {
users := make([]User, 0)
keyword := fmt.Sprintf("%%%s%%", query.Keyword)

View File

@ -0,0 +1,145 @@
package application
import (
"busniess-user-center/config"
appModel "busniess-user-center/internal/models/application"
"busniess-user-center/internal/repo"
"busniess-user-center/pkg/redis"
"context"
"fmt"
"github.com/jinzhu/copier"
"github.com/samber/do"
"go.uber.org/zap"
"gorm.io/gorm"
)
func init() {
do.Provide(nil, NewApplicationService)
}
type applicationService struct {
logger *zap.SugaredLogger
appRepo repo.ApplicationRepo
menuRepo repo.MenuRepo
redis *redis.Redis
conf *config.AppConfig
}
func NewApplicationService(i *do.Injector) (ApplicationService, error) {
return &applicationService{
logger: do.MustInvoke[*zap.SugaredLogger](i),
appRepo: do.MustInvoke[repo.ApplicationRepo](i),
menuRepo: do.MustInvoke[repo.MenuRepo](i),
redis: do.MustInvoke[*redis.Redis](i),
conf: do.MustInvoke[*config.AppConfig](i),
}, nil
}
func (a *applicationService) Create(ctx context.Context, info *appModel.CreateReq) error {
dbApp := repo.Application{}
err := copier.Copy(dbApp, info)
if err != nil {
return err
}
return a.appRepo.CreateApplication(ctx, dbApp)
}
func (a *applicationService) ResetSecret(ctx context.Context, info *appModel.ResetSecretReq) error {
dbApp := repo.Application{}
err := copier.Copy(dbApp, info)
if err != nil {
return err
}
// todo 校验权限
return a.appRepo.ResetSecret(ctx, dbApp)
}
func (a *applicationService) Delete(ctx context.Context, req *appModel.DeleteAppReq) error {
// todo 校验权限
return a.appRepo.DelApp(ctx, req.Id)
}
func (a *applicationService) GetApp(ctx context.Context, req *appModel.GetAppReq) (app appModel.ApplicationInfo, err error) {
dbApp, err := a.appRepo.GetAppById(ctx, req.Id)
if err != nil && err != gorm.ErrRecordNotFound {
return
}
if err == gorm.ErrRecordNotFound {
return
}
err = copier.Copy(app, dbApp)
return
}
func (a *applicationService) Search(ctx context.Context, query *appModel.Query) (rsp appModel.List, err error) {
query.Default()
dbQuery := repo.Query{}
err = copier.Copy(dbQuery, query)
if err != nil {
return
}
dbList, err := a.appRepo.Search(ctx, dbQuery)
if err != nil {
return
}
for _, item := range dbList {
info := appModel.ApplicationInfo{}
copier.Copy(info, item)
rsp.List = append(rsp.List, info)
}
return
}
func (a *applicationService) Menus(ctx context.Context, info *appModel.MenusReq) (rsp []appModel.Menu, err error) {
return a.menuRepo.GetAppMenus(ctx, info.AppCode)
}
func (a *applicationService) CreateMenu(ctx context.Context, info *appModel.CreateMenuReq) error {
// todo 校验权限
menu := repo.Menu{}
err := copier.Copy(menu, info)
if err != nil {
return err
}
_, err = a.menuRepo.Create(ctx, &menu)
return err
}
func (a *applicationService) ModifyMenu(ctx context.Context, info *appModel.ModifyMenuReq) error {
// todo 校验权限
dMenu, err := a.menuRepo.GetMenuById(ctx, info.Id)
if err != nil && err != gorm.ErrRecordNotFound {
return err
}
if err == gorm.ErrRecordNotFound {
return fmt.Errorf("菜单不存在")
}
dMenu.Name = info.Name
dMenu.ParentCode = info.ParentCode
dMenu.Order = info.Order
dMenu.Icon = info.Icon
dMenu.Path = info.Path
dMenu.ChildPath = info.ChildPath
dMenu.GoFirstChild = info.GoFirstChild
dMenu.IsShow = info.IsShow
err = a.menuRepo.SaveMenu(ctx, &dMenu)
return err
}
func (a *applicationService) DeleteMenu(ctx context.Context, info *appModel.DelMenuReq) error {
// todo 校验权限
err := a.menuRepo.DelMenuById(ctx, info.Id)
return err
}

View File

@ -0,0 +1,15 @@
package application
import (
appModel "busniess-user-center/internal/models/application"
"context"
)
type ApplicationService interface {
Create(ctx context.Context, info *appModel.CreateReq) error
ResetSecret(ctx context.Context, info *appModel.ResetSecretReq) error
Delete(ctx context.Context, req *appModel.DeleteAppReq) error
GetApp(ctx context.Context, req *appModel.GetAppReq) (app appModel.ApplicationInfo, err error)
Search(ctx context.Context, query *appModel.Query) (rsp appModel.List, err error)
Menus(ctx context.Context, info *appModel.MenusReq) (rsp []appModel.Menu, err error)
}

View File

@ -0,0 +1 @@
package application

View File

@ -1,18 +1,19 @@
package user
import (
"busniess-user-center/internal/models"
userModel "busniess-user-center/internal/models/user"
"context"
)
type UserService interface {
Add(ctx context.Context, info *models.AddInfo) (id uint, err error)
Login(ctx context.Context, lInfo models.LoginInfo) error
Add(ctx context.Context, info *userModel.AddInfo) (id uint, err error)
Login(ctx context.Context, lInfo userModel.LoginInfo) error
Logout(ctx context.Context) error
Modify(ctx context.Context, mInfo *models.ModifyInfo) error
Disable(ctx context.Context, req *models.Enable) error
Enable(ctx context.Context, req *models.Enable) error
Search(ctx context.Context, query *models.Query) ([]models.User, error)
ResetPwd(ctx context.Context, req *models.ResetPwdReq) error
GetUser(ctx context.Context, req *models.GetUserReq) (user models.User, err error)
Modify(ctx context.Context, mInfo *userModel.ModifyInfo) error
Disable(ctx context.Context, req *userModel.Enable) error
Enable(ctx context.Context, req *userModel.Enable) error
Search(ctx context.Context, query *userModel.Query) ([]userModel.User, error)
ResetPwd(ctx context.Context, req *userModel.ResetPwdReq) error
GetUser(ctx context.Context, req *userModel.GetUserReq) (user userModel.User, err error)
}

View File

@ -2,7 +2,7 @@ package user
import (
"busniess-user-center/config"
"busniess-user-center/internal/models"
userModel "busniess-user-center/internal/models/user"
"busniess-user-center/internal/repo"
"busniess-user-center/pkg/redis"
contextUtil "busniess-user-center/pkg/utils/context"
@ -45,7 +45,7 @@ func NewUserService(i *do.Injector) (UserService, error) {
}, nil
}
func (u *userService) Add(ctx context.Context, info *models.AddInfo) (id uint, err error) {
func (u *userService) Add(ctx context.Context, info *userModel.AddInfo) (id uint, err error) {
session, err := contextUtil.GetSession(ctx)
if err != nil {
return
@ -101,7 +101,7 @@ func (u *userService) Add(ctx context.Context, info *models.AddInfo) (id uint, e
return
}
func (u *userService) Login(ctx context.Context, info models.LoginInfo) error {
func (u *userService) Login(ctx context.Context, info userModel.LoginInfo) error {
// 通过account获取用户信息
user, err := u.repo.GetUserByAccount(ctx, info.Account)
if err == gorm.ErrRecordNotFound {
@ -140,7 +140,7 @@ func (u *userService) Logout(ctx context.Context) error {
return nil
}
func (u *userService) Modify(ctx context.Context, mInfo *models.ModifyInfo) error {
func (u *userService) Modify(ctx context.Context, mInfo *userModel.ModifyInfo) error {
// 获取当前操作用户
session, err := contextUtil.GetSession(ctx)
if err != nil {
@ -158,7 +158,7 @@ func (u *userService) Modify(ctx context.Context, mInfo *models.ModifyInfo) erro
return u.repo.SaveUser(ctx, user)
}
func (u *userService) Disable(ctx context.Context, req *models.Enable) error {
func (u *userService) Disable(ctx context.Context, req *userModel.Enable) error {
// 获取操作用户
_, err := contextUtil.GetSession(ctx)
if err != nil {
@ -168,10 +168,10 @@ func (u *userService) Disable(ctx context.Context, req *models.Enable) error {
// todo 判断是否有权限
// 修改对应用户状态
return u.repo.SetUserStatus(ctx, req.Id, models.DisableUserStatus)
return u.repo.SetUserStatus(ctx, req.Id, userModel.DisableUserStatus)
}
func (u *userService) Enable(ctx context.Context, req *models.Enable) error {
func (u *userService) Enable(ctx context.Context, req *userModel.Enable) error {
_, err := contextUtil.GetSession(ctx)
if err != nil {
return err
@ -180,10 +180,10 @@ func (u *userService) Enable(ctx context.Context, req *models.Enable) error {
// todo 判断是否有权限
// 修改对应用户状态
return u.repo.SetUserStatus(ctx, req.Id, models.EnableUserStatus)
return u.repo.SetUserStatus(ctx, req.Id, userModel.EnableUserStatus)
}
func (u *userService) Search(ctx context.Context, query *models.Query) ([]models.User, error) {
func (u *userService) Search(ctx context.Context, query *userModel.Query) ([]userModel.User, error) {
// 获取操作用户
_, err := contextUtil.GetSession(ctx)
if err != nil {
@ -200,7 +200,7 @@ func (u *userService) Search(ctx context.Context, query *models.Query) ([]models
return list, err
}
func (u *userService) ResetPwd(ctx context.Context, req *models.ResetPwdReq) error {
func (u *userService) ResetPwd(ctx context.Context, req *userModel.ResetPwdReq) error {
// 获取操作用户
session, err := contextUtil.GetSession(ctx)
if err != nil {
@ -243,7 +243,7 @@ func (u *userService) ResetPwd(ctx context.Context, req *models.ResetPwdReq) err
return nil
}
func (u *userService) GetUser(ctx context.Context, req *models.GetUserReq) (user models.User, err error) {
func (u *userService) GetUser(ctx context.Context, req *userModel.GetUserReq) (user userModel.User, err error) {
rUser, err := u.repo.GetUserByAccount(ctx, req.Account)
if err != nil {
return

View File

@ -1,7 +1,7 @@
package user
import (
"busniess-user-center/internal/models"
userModel "busniess-user-center/internal/models/user"
"busniess-user-center/internal/repo"
"context"
"fmt"
@ -68,8 +68,8 @@ func (u *userService) removeCookie(ctx context.Context) {
}
}
func convertUserList(users []repo.User) []models.User {
list := make([]models.User, len(users))
func convertUserList(users []repo.User) []userModel.User {
list := make([]userModel.User, len(users))
for _, item := range users {
list = append(list, convertUser(item))
}
@ -77,10 +77,10 @@ func convertUserList(users []repo.User) []models.User {
return list
}
func convertUser(user repo.User) models.User {
return models.User{
func convertUser(user repo.User) userModel.User {
return userModel.User{
Id: user.ID,
UserInfo: models.UserInfo{
UserInfo: userModel.UserInfo{
Name: user.Name,
Account: user.Account,
Mobile: user.Mobile,

View File

@ -0,0 +1,60 @@
package application
import (
"context"
appModel "busniess-user-center/internal/models/application"
applicationService "busniess-user-center/internal/service/application"
ginUtil "busniess-user-center/pkg/utils/gin"
"github.com/gin-gonic/gin"
"github.com/samber/do"
"go.uber.org/zap"
)
func init() {
do.Provide(nil, NewApplicationServer)
}
type ApplicationServer struct {
applicationService applicationService.ApplicationService
logger *zap.SugaredLogger
}
func NewApplicationServer(i *do.Injector) (*ApplicationServer, error) {
return &ApplicationServer{
applicationService: do.MustInvoke[applicationService.ApplicationService](i),
logger: do.MustInvoke[*zap.SugaredLogger](i),
}, nil
}
func RegisterRoute(api *gin.RouterGroup) {
server := do.MustInvoke[*ApplicationServer](nil)
api.POST("/create", ginUtil.WrapNoRsp(server.Create))
api.POST("/reset", ginUtil.WrapNoRsp(server.ResetSecret))
api.POST("/delete", ginUtil.WrapNoRsp(server.DelApp))
api.GET("/search", ginUtil.Wrap(server.Search))
api.GET("/menus", ginUtil.WrapNoRsp(server.ResetSecret))
}
func (u *ApplicationServer) Create(ctx context.Context, req *appModel.CreateReq) (err error) {
// 转换dto
return u.applicationService.Create(ctx, req)
}
func (u *ApplicationServer) Get(ctx context.Context, req *appModel.GetAppReq) (app appModel.ApplicationInfo, err error) {
return u.applicationService.GetApp(ctx, req)
}
func (u *ApplicationServer) Search(ctx context.Context, query *appModel.Query) (appModel.List, error) {
query.Default()
return u.applicationService.Search(ctx, query)
}
func (u *ApplicationServer) ResetSecret(ctx context.Context, req *appModel.ResetSecretReq) error {
return u.applicationService.ResetSecret(ctx, req)
}
func (u *ApplicationServer) DelApp(ctx context.Context, req *appModel.DeleteAppReq) error {
return u.applicationService.Delete(ctx, req)
}

View File

@ -1,7 +1,7 @@
package user
import (
"busniess-user-center/internal/models"
userModel "busniess-user-center/internal/models/user"
"busniess-user-center/internal/service/user"
ginUtil "busniess-user-center/pkg/utils/gin"
@ -42,7 +42,7 @@ func RegisterRoute(api *gin.RouterGroup) {
api.POST("/disable", ginUtil.WrapNoRsp(server.Disable))
}
func (u *UserServer) Add(ctx context.Context, req *models.AddInfo) (rsp proto.AddResponse, err error) {
func (u *UserServer) Add(ctx context.Context, req *userModel.AddInfo) (rsp proto.AddResponse, err error) {
// 转换dto
id, err := u.userService.Add(ctx, req)
if err != nil {
@ -59,7 +59,7 @@ func (u *UserServer) Add(ctx context.Context, req *models.AddInfo) (rsp proto.Ad
func (u *UserServer) Login(ctx context.Context, req *proto.LoginRequest) (err error) {
// 转换dto
info := models.LoginInfo{
info := userModel.LoginInfo{
Account: req.Account,
Pwd: req.Pwd,
}
@ -71,27 +71,27 @@ func (u *UserServer) Logout(ctx context.Context) error {
return u.userService.Logout(ctx)
}
func (u *UserServer) Modify(ctx context.Context, req *models.ModifyInfo) error {
func (u *UserServer) Modify(ctx context.Context, req *userModel.ModifyInfo) error {
return u.userService.Modify(ctx, req)
}
func (u *UserServer) Disable(ctx context.Context, req *models.Enable) error {
func (u *UserServer) Disable(ctx context.Context, req *userModel.Enable) error {
return u.userService.Disable(ctx, req)
}
func (u *UserServer) Enable(ctx context.Context, req *models.Enable) error {
func (u *UserServer) Enable(ctx context.Context, req *userModel.Enable) error {
return u.userService.Enable(ctx, req)
}
func (u *UserServer) Search(ctx context.Context, query *models.Query) ([]models.User, error) {
func (u *UserServer) Search(ctx context.Context, query *userModel.Query) ([]userModel.User, error) {
query.Default()
return u.userService.Search(ctx, query)
}
func (u *UserServer) GetUser(ctx context.Context, req *models.GetUserReq) (models.User, error) {
func (u *UserServer) GetUser(ctx context.Context, req *userModel.GetUserReq) (userModel.User, error) {
return u.userService.GetUser(ctx, req)
}
func (u *UserServer) ResetPwd(ctx context.Context, req *models.ResetPwdReq) error {
func (u *UserServer) ResetPwd(ctx context.Context, req *userModel.ResetPwdReq) error {
return u.userService.ResetPwd(ctx, req)
}

2
vendor/github.com/jinzhu/copier/.gitignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/
ttt/

20
vendor/github.com/jinzhu/copier/License generated vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 Jinzhu
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

132
vendor/github.com/jinzhu/copier/README.md generated vendored Normal file
View File

@ -0,0 +1,132 @@
# Copier
I am a copier, I copy everything from one to another
[![test status](https://github.com/jinzhu/copier/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/copier/actions)
## Features
* Copy from field to field with same name
* Copy from method to field with same name
* Copy from field to method with same name
* Copy from slice to slice
* Copy from struct to slice
* Copy from map to map
* Enforce copying a field with a tag
* Ignore a field with a tag
* Deep Copy
## Usage
```go
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Role string
Age int32
EmployeeCode int64 `copier:"EmployeeNum"` // specify field name
// Explicitly ignored in the destination struct.
Salary int
}
func (user *User) DoubleAge() int32 {
return 2 * user.Age
}
// Tags in the destination Struct provide instructions to copier.Copy to ignore
// or enforce copying and to panic or return an error if a field was not copied.
type Employee struct {
// Tell copier.Copy to panic if this field is not copied.
Name string `copier:"must"`
// Tell copier.Copy to return an error if this field is not copied.
Age int32 `copier:"must,nopanic"`
// Tell copier.Copy to explicitly ignore copying this field.
Salary int `copier:"-"`
DoubleAge int32
EmployeeId int64 `copier:"EmployeeNum"` // specify field name
SuperRole string
}
func (employee *Employee) Role(role string) {
employee.SuperRole = "Super " + role
}
func main() {
var (
user = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000}
users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}}
employee = Employee{Salary: 150000}
employees = []Employee{}
)
copier.Copy(&employee, &user)
fmt.Printf("%#v \n", employee)
// Employee{
// Name: "Jinzhu", // Copy from field
// Age: 18, // Copy from field
// Salary:150000, // Copying explicitly ignored
// DoubleAge: 36, // Copy from method
// EmployeeId: 0, // Ignored
// SuperRole: "Super Admin", // Copy to method
// }
// Copy struct to slice
copier.Copy(&employees, &user)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeeId: 0, SuperRole: "Super Admin"}
// }
// Copy slice to slice
employees = []Employee{}
copier.Copy(&employees, &users)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeeId: 0, SuperRole: "Super Admin"},
// {Name: "jinzhu 2", Age: 30, Salary:0, DoubleAge: 60, EmployeeId: 0, SuperRole: "Super Dev"},
// }
// Copy map to map
map1 := map[int]int{3: 6, 4: 8}
map2 := map[int32]int8{}
copier.Copy(&map2, map1)
fmt.Printf("%#v \n", map2)
// map[int32]int8{3:6, 4:8}
}
```
### Copy with Option
```go
copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true})
```
## Contributing
You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do.
# Author
**jinzhu**
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
## License
Released under the [MIT License](https://github.com/jinzhu/copier/blob/master/License).

828
vendor/github.com/jinzhu/copier/copier.go generated vendored Normal file
View File

@ -0,0 +1,828 @@
package copier
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"strings"
"sync"
"unicode"
)
// These flags define options for tag handling
const (
// Denotes that a destination field must be copied to. If copying fails then a panic will ensue.
tagMust uint8 = 1 << iota
// Denotes that the program should not panic when the must flag is on and
// value is not copied. The program will return an error instead.
tagNoPanic
// Ignore a destination field from being copied to.
tagIgnore
// Denotes that the value as been copied
hasCopied
// Some default converter types for a nicer syntax
String string = ""
Bool bool = false
Int int = 0
Float32 float32 = 0
Float64 float64 = 0
)
// Option sets copy options
type Option struct {
// setting this value to true will ignore copying zero values of all the fields, including bools, as well as a
// struct having all it's fields set to their zero values respectively (see IsZero() in reflect/value.go)
IgnoreEmpty bool
CaseSensitive bool
DeepCopy bool
Converters []TypeConverter
// Custom field name mappings to copy values with different names in `fromValue` and `toValue` types.
// Examples can be found in `copier_field_name_mapping_test.go`.
FieldNameMapping []FieldNameMapping
}
func (opt Option) converters() map[converterPair]TypeConverter {
var converters = map[converterPair]TypeConverter{}
// save converters into map for faster lookup
for i := range opt.Converters {
pair := converterPair{
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
DstType: reflect.TypeOf(opt.Converters[i].DstType),
}
converters[pair] = opt.Converters[i]
}
return converters
}
type TypeConverter struct {
SrcType interface{}
DstType interface{}
Fn func(src interface{}) (dst interface{}, err error)
}
type converterPair struct {
SrcType reflect.Type
DstType reflect.Type
}
func (opt Option) fieldNameMapping() map[converterPair]FieldNameMapping {
var mapping = map[converterPair]FieldNameMapping{}
for i := range opt.FieldNameMapping {
pair := converterPair{
SrcType: reflect.TypeOf(opt.FieldNameMapping[i].SrcType),
DstType: reflect.TypeOf(opt.FieldNameMapping[i].DstType),
}
mapping[pair] = opt.FieldNameMapping[i]
}
return mapping
}
type FieldNameMapping struct {
SrcType interface{}
DstType interface{}
Mapping map[string]string
}
// Tag Flags
type flags struct {
BitFlags map[string]uint8
SrcNames tagNameMapping
DestNames tagNameMapping
}
// Field Tag name mapping
type tagNameMapping struct {
FieldNameToTag map[string]string
TagToFieldName map[string]string
}
// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
return copier(toValue, fromValue, Option{})
}
// CopyWithOption copy with option
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error) {
return copier(toValue, fromValue, opt)
}
func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) {
var (
isSlice bool
amount = 1
from = indirect(reflect.ValueOf(fromValue))
to = indirect(reflect.ValueOf(toValue))
converters = opt.converters()
mappings = opt.fieldNameMapping()
)
if !to.CanAddr() {
return ErrInvalidCopyDestination
}
// Return is from value is invalid
if !from.IsValid() {
return ErrInvalidCopyFrom
}
fromType, isPtrFrom := indirectType(from.Type())
toType, _ := indirectType(to.Type())
if fromType.Kind() == reflect.Interface {
fromType = reflect.TypeOf(from.Interface())
}
if toType.Kind() == reflect.Interface {
toType, _ = indirectType(reflect.TypeOf(to.Interface()))
oldTo := to
to = reflect.New(reflect.TypeOf(to.Interface())).Elem()
defer func() {
oldTo.Set(to)
}()
}
// Just set it if possible to assign for normal types
if from.Kind() != reflect.Slice && from.Kind() != reflect.Struct && from.Kind() != reflect.Map && (from.Type().AssignableTo(to.Type()) || from.Type().ConvertibleTo(to.Type())) {
if !isPtrFrom || !opt.DeepCopy {
to.Set(from.Convert(to.Type()))
} else {
fromCopy := reflect.New(from.Type())
fromCopy.Set(from.Elem())
to.Set(fromCopy.Convert(to.Type()))
}
return
}
if from.Kind() != reflect.Slice && fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map {
if !fromType.Key().ConvertibleTo(toType.Key()) {
return ErrMapKeyNotMatch
}
if to.IsNil() {
to.Set(reflect.MakeMapWithSize(toType, from.Len()))
}
for _, k := range from.MapKeys() {
toKey := indirect(reflect.New(toType.Key()))
isSet, err := set(toKey, k, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
}
elemType := toType.Elem()
if elemType.Kind() != reflect.Slice {
elemType, _ = indirectType(elemType)
}
toValue := indirect(reflect.New(elemType))
isSet, err = set(toValue, from.MapIndex(k), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
return err
}
}
for {
if elemType == toType.Elem() {
to.SetMapIndex(toKey, toValue)
break
}
elemType = reflect.PtrTo(elemType)
toValue = toValue.Addr()
}
}
return
}
if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice {
if to.IsNil() {
slice := reflect.MakeSlice(reflect.SliceOf(to.Type().Elem()), from.Len(), from.Cap())
to.Set(slice)
}
if fromType.ConvertibleTo(toType) {
for i := 0; i < from.Len(); i++ {
if to.Len() < i+1 {
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
}
isSet, err := set(to.Index(i), from.Index(i), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
if err != nil {
continue
}
}
}
return
}
}
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
// skip not supported type
return
}
if len(converters) > 0 {
if ok, e := set(to, from, opt.DeepCopy, converters); e == nil && ok {
// converter supported
return
}
}
if from.Kind() == reflect.Slice || 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)
}
if len(converters) > 0 {
if ok, e := set(dest, source, opt.DeepCopy, converters); e == nil && ok {
if isSlice {
// FIXME: maybe should check the other types?
if to.Type().Elem().Kind() == reflect.Ptr {
to.Index(i).Set(dest.Addr())
} else {
if to.Len() < i+1 {
reflect.Append(to, dest)
} else {
to.Index(i).Set(dest)
}
}
} else {
to.Set(dest)
}
continue
}
}
destKind := dest.Kind()
initDest := false
if destKind == reflect.Interface {
initDest = true
dest = indirect(reflect.New(toType))
}
// Get tag options
flgs, err := getFlags(dest, source, toType, fromType)
if err != nil {
return err
}
// check source
if source.IsValid() {
copyUnexportedStructFields(dest, source)
// Copy from source field to dest field or method
fromTypeFields := deepFields(fromType)
for _, field := range fromTypeFields {
name := field.Name
// Get bit flags for field
fieldFlags := flgs.BitFlags[name]
// Check if we should ignore copying
if (fieldFlags & tagIgnore) != 0 {
continue
}
fieldNamesMapping := getFieldNamesMapping(mappings, fromType, toType)
srcFieldName, destFieldName := getFieldName(name, flgs, fieldNamesMapping)
if fromField := fieldByNameOrZeroValue(source, srcFieldName); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) {
// process for nested anonymous field
destFieldNotSet := false
if f, ok := dest.Type().FieldByName(destFieldName); ok {
// only initialize parent embedded struct pointer in the path
for idx := range f.Index[:len(f.Index)-1] {
destField := dest.FieldByIndex(f.Index[:idx+1])
if destField.Kind() != reflect.Ptr {
continue
}
if !destField.IsNil() {
continue
}
if !destField.CanSet() {
destFieldNotSet = true
break
}
// destField is a nil pointer that can be set
newValue := reflect.New(destField.Type().Elem())
destField.Set(newValue)
}
}
if destFieldNotSet {
break
}
toField := fieldByName(dest, destFieldName, opt.CaseSensitive)
if toField.IsValid() {
if toField.CanSet() {
isSet, err := set(toField, fromField, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
return err
}
}
if fieldFlags != 0 {
// Note that a copy was made
flgs.BitFlags[name] = fieldFlags | hasCopied
}
}
} else {
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(destFieldName)
} else {
toMethod = dest.MethodByName(destFieldName)
}
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
// Copy from from method to dest field
for _, field := range deepFields(toType) {
name := field.Name
srcFieldName, destFieldName := getFieldName(name, flgs, getFieldNamesMapping(mappings, fromType, toType))
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(srcFieldName)
} else {
fromMethod = source.MethodByName(srcFieldName)
}
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) {
if toField := fieldByName(dest, destFieldName, opt.CaseSensitive); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0], opt.DeepCopy, converters)
}
}
}
}
}
if isSlice && to.Kind() == reflect.Slice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest.Addr()))
} else {
isSet, err := set(to.Index(i), dest.Addr(), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt)
if err != nil {
continue
}
}
}
} else if dest.Type().AssignableTo(to.Type().Elem()) {
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest))
} else {
isSet, err := set(to.Index(i), dest, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt)
if err != nil {
continue
}
}
}
}
} else if initDest {
to.Set(dest)
}
err = checkBitFlags(flgs.BitFlags)
}
return
}
func getFieldNamesMapping(mappings map[converterPair]FieldNameMapping, fromType reflect.Type, toType reflect.Type) map[string]string {
var fieldNamesMapping map[string]string
if len(mappings) > 0 {
pair := converterPair{
SrcType: fromType,
DstType: toType,
}
if v, ok := mappings[pair]; ok {
fieldNamesMapping = v.Mapping
}
}
return fieldNamesMapping
}
func fieldByNameOrZeroValue(source reflect.Value, fieldName string) (value reflect.Value) {
defer func() {
if err := recover(); err != nil {
value = reflect.Value{}
}
}()
return source.FieldByName(fieldName)
}
func copyUnexportedStructFields(to, from reflect.Value) {
if from.Kind() != reflect.Struct || to.Kind() != reflect.Struct || !from.Type().AssignableTo(to.Type()) {
return
}
// create a shallow copy of 'to' to get all fields
tmp := indirect(reflect.New(to.Type()))
tmp.Set(from)
// revert exported fields
for i := 0; i < to.NumField(); i++ {
if tmp.Field(i).CanSet() {
tmp.Field(i).Set(to.Field(i))
}
}
to.Set(tmp)
}
func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
return ignoreEmpty && v.IsZero()
}
var deepFieldsLock sync.RWMutex
var deepFieldsMap = make(map[reflect.Type][]reflect.StructField)
func deepFields(reflectType reflect.Type) []reflect.StructField {
deepFieldsLock.RLock()
cache, ok := deepFieldsMap[reflectType]
deepFieldsLock.RUnlock()
if ok {
return cache
}
var res []reflect.StructField
if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
fields := make([]reflect.StructField, 0, reflectType.NumField())
for i := 0; i < reflectType.NumField(); i++ {
v := reflectType.Field(i)
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
if v.PkgPath == "" {
fields = append(fields, v)
if v.Anonymous {
// also consider fields of anonymous fields as fields of the root
fields = append(fields, deepFields(v.Type)...)
}
}
}
res = fields
}
deepFieldsLock.Lock()
deepFieldsMap[reflectType] = res
deepFieldsLock.Unlock()
return res
}
func indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
return reflectValue
}
func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
reflectType = reflectType.Elem()
isPtr = true
}
return reflectType, isPtr
}
func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) (bool, error) {
if !from.IsValid() {
return true, nil
}
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
return false, err
} else if ok {
return true, nil
}
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, nil
} else if to.IsNil() {
// `from` -> `to`
// sql.NullString -> *string
if fromValuer, ok := driverValuer(from); ok {
v, err := fromValuer.Value()
if err != nil {
return true, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true, nil
}
}
// allocate new `to` variable with default value (eg. *string -> new(string))
to.Set(reflect.New(to.Type().Elem()))
}
// depointer `to`
to = to.Elem()
}
if deepCopy {
toKind := to.Kind()
if toKind == reflect.Interface && to.IsNil() {
if reflect.TypeOf(from.Interface()) != nil {
to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
toKind = reflect.TypeOf(to.Interface()).Kind()
}
}
if from.Kind() == reflect.Ptr && from.IsNil() {
return true, nil
}
if _, ok := to.Addr().Interface().(sql.Scanner); !ok && (toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice) {
return false, nil
}
}
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
// `from` -> `to`
// *string -> sql.NullString
if from.Kind() == reflect.Ptr {
// if `from` is nil do nothing with `to`
if from.IsNil() {
return true, nil
}
// depointer `from`
from = indirect(from)
}
// `from` -> `to`
// string -> sql.NullString
// set `to` by invoking method Scan(`from`)
err := toScanner.Scan(from.Interface())
if err != nil {
return false, nil
}
} else if fromValuer, ok := driverValuer(from); ok {
// `from` -> `to`
// sql.NullString -> string
v, err := fromValuer.Value()
if err != nil {
return false, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true, nil
}
rv := reflect.ValueOf(v)
if rv.Type().AssignableTo(to.Type()) {
to.Set(rv)
} else if to.CanSet() && rv.Type().ConvertibleTo(to.Type()) {
to.Set(rv.Convert(to.Type()))
}
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
} else {
return false, nil
}
return true, nil
}
// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
func lookupAndCopyWithConverter(to, from reflect.Value, converters map[converterPair]TypeConverter) (copied bool, err error) {
pair := converterPair{
SrcType: from.Type(),
DstType: to.Type(),
}
if cnv, ok := converters[pair]; ok {
result, err := cnv.Fn(from.Interface())
if err != nil {
return false, err
}
if result != nil {
to.Set(reflect.ValueOf(result))
} else {
// in case we've got a nil value to copy
to.Set(reflect.Zero(to.Type()))
}
return true, nil
}
return false, nil
}
// parseTags Parses struct tags and returns uint8 bit flags.
func parseTags(tag string) (flg uint8, name string, err error) {
for _, t := range strings.Split(tag, ",") {
switch t {
case "-":
flg = tagIgnore
return
case "must":
flg = flg | tagMust
case "nopanic":
flg = flg | tagNoPanic
default:
if unicode.IsUpper([]rune(t)[0]) {
name = strings.TrimSpace(t)
} else {
err = ErrFieldNameTagStartNotUpperCase
}
}
}
return
}
// getTagFlags Parses struct tags for bit flags, field name.
func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, error) {
flgs := flags{
BitFlags: map[string]uint8{},
SrcNames: tagNameMapping{
FieldNameToTag: map[string]string{},
TagToFieldName: map[string]string{},
},
DestNames: tagNameMapping{
FieldNameToTag: map[string]string{},
TagToFieldName: map[string]string{},
},
}
var toTypeFields, fromTypeFields []reflect.StructField
if dest.IsValid() {
toTypeFields = deepFields(toType)
}
if src.IsValid() {
fromTypeFields = deepFields(fromType)
}
// Get a list dest of tags
for _, field := range toTypeFields {
tags := field.Tag.Get("copier")
if tags != "" {
var name string
var err error
if flgs.BitFlags[field.Name], name, err = parseTags(tags); err != nil {
return flags{}, err
} else if name != "" {
flgs.DestNames.FieldNameToTag[field.Name] = name
flgs.DestNames.TagToFieldName[name] = field.Name
}
}
}
// Get a list source of tags
for _, field := range fromTypeFields {
tags := field.Tag.Get("copier")
if tags != "" {
var name string
var err error
if _, name, err = parseTags(tags); err != nil {
return flags{}, err
} else if name != "" {
flgs.SrcNames.FieldNameToTag[field.Name] = name
flgs.SrcNames.TagToFieldName[name] = field.Name
}
}
}
return flgs, nil
}
// checkBitFlags Checks flags for error or panic conditions.
func checkBitFlags(flagsList map[string]uint8) (err error) {
// Check flag conditions were met
for name, flgs := range flagsList {
if flgs&hasCopied == 0 {
switch {
case flgs&tagMust != 0 && flgs&tagNoPanic != 0:
err = fmt.Errorf("field %s has must tag but was not copied", name)
return
case flgs&(tagMust) != 0:
panic(fmt.Sprintf("Field %s has must tag but was not copied", name))
}
}
}
return
}
func getFieldName(fieldName string, flgs flags, fieldNameMapping map[string]string) (srcFieldName string, destFieldName string) {
// get dest field name
if name, ok := fieldNameMapping[fieldName]; ok {
srcFieldName = fieldName
destFieldName = name
return
}
if srcTagName, ok := flgs.SrcNames.FieldNameToTag[fieldName]; ok {
destFieldName = srcTagName
if destTagName, ok := flgs.DestNames.TagToFieldName[srcTagName]; ok {
destFieldName = destTagName
}
} else {
if destTagName, ok := flgs.DestNames.TagToFieldName[fieldName]; ok {
destFieldName = destTagName
}
}
if destFieldName == "" {
destFieldName = fieldName
}
// get source field name
if destTagName, ok := flgs.DestNames.FieldNameToTag[fieldName]; ok {
srcFieldName = destTagName
if srcField, ok := flgs.SrcNames.TagToFieldName[destTagName]; ok {
srcFieldName = srcField
}
} else {
if srcField, ok := flgs.SrcNames.TagToFieldName[fieldName]; ok {
srcFieldName = srcField
}
}
if srcFieldName == "" {
srcFieldName = fieldName
}
return
}
func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {
if !v.CanAddr() {
i, ok = v.Interface().(driver.Valuer)
return
}
i, ok = v.Addr().Interface().(driver.Valuer)
return
}
func fieldByName(v reflect.Value, name string, caseSensitive bool) reflect.Value {
if caseSensitive {
return v.FieldByName(name)
}
return v.FieldByNameFunc(func(n string) bool { return strings.EqualFold(n, name) })
}

11
vendor/github.com/jinzhu/copier/errors.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package copier
import "errors"
var (
ErrInvalidCopyDestination = errors.New("copy destination must be non-nil and addressable")
ErrInvalidCopyFrom = errors.New("copy from must be non-nil and addressable")
ErrMapKeyNotMatch = errors.New("map's key type doesn't match")
ErrNotSupported = errors.New("not supported")
ErrFieldNameTagStartNotUpperCase = errors.New("copier field name tag must be start upper case")
)

3
vendor/modules.txt vendored
View File

@ -132,6 +132,9 @@ github.com/hashicorp/hcl/hcl/token
github.com/hashicorp/hcl/json/parser
github.com/hashicorp/hcl/json/scanner
github.com/hashicorp/hcl/json/token
# github.com/jinzhu/copier v0.4.0
## explicit; go 1.13
github.com/jinzhu/copier
# github.com/jinzhu/inflection v1.0.0
## explicit
github.com/jinzhu/inflection