🎉初步完成用户
This commit is contained in:
20
http/api/admin/base.go
Normal file
20
http/api/admin/base.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"website-nav/http/response"
|
||||
"website-nav/http/vo"
|
||||
)
|
||||
|
||||
// GetCurrentLoginUser
|
||||
// @description: 获取当前登陆用户
|
||||
// @param c
|
||||
// @return *vo.User
|
||||
func GetCurrentLoginUser(c *gin.Context) *vo.User {
|
||||
if user, ok := c.Get("user"); ok {
|
||||
return user.(*vo.User)
|
||||
}
|
||||
response.R(c).AuthorizationFailed("暂未登陆")
|
||||
c.Abort()
|
||||
return nil
|
||||
}
|
111
http/api/admin/login.go
Normal file
111
http/api/admin/login.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.ltd/lxh/logger/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"time"
|
||||
"website-nav/component"
|
||||
"website-nav/http/param"
|
||||
"website-nav/http/response"
|
||||
"website-nav/http/vo"
|
||||
"website-nav/service"
|
||||
"website-nav/utils"
|
||||
)
|
||||
|
||||
type Login struct{}
|
||||
|
||||
func LoginAPI() Login {
|
||||
return Login{}
|
||||
}
|
||||
|
||||
// Captcha
|
||||
// @description: 获取验证码
|
||||
// @receiver login
|
||||
// @param c
|
||||
func (Login) Captcha(c *gin.Context) {
|
||||
math := base64Captcha.DriverMath{Height: 120, Width: 480, Fonts: []string{"ApothecaryFont.ttf", "3Dumb.ttf"}}
|
||||
mathDriver := math.ConvertFonts()
|
||||
|
||||
capt := base64Captcha.NewCaptcha(mathDriver, component.Captcha{})
|
||||
|
||||
id, base64Str, _, err := capt.Generate()
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError(fmt.Errorf("生成验证码失败: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OkWithData(map[string]any{
|
||||
"id": id,
|
||||
"captcha": base64Str,
|
||||
})
|
||||
}
|
||||
|
||||
// Login
|
||||
// @description: 登陆
|
||||
// @receiver login
|
||||
// @param c
|
||||
func (Login) Login(c *gin.Context) {
|
||||
var p param.Login
|
||||
if err := c.ShouldBind(&p); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证验证码是否正确
|
||||
ok := component.Captcha{}.Verify(p.CaptchaId, p.CaptchaCode, true)
|
||||
if !ok {
|
||||
response.R(c).FailedWithError("验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证码正确,查询用户信息
|
||||
user, err := service.User().GetUserByAccount(p.Account)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError("获取用户信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 对比密码
|
||||
if !utils.Password().ComparePassword(user.Password, p.Password) {
|
||||
response.R(c).FailedWithError("密码错误")
|
||||
return
|
||||
}
|
||||
|
||||
secret := component.JWT().GenerateSecret(p.Password, uuid.NewString(), time.Now().Local().String())
|
||||
// 生成token
|
||||
token, expireAt, err := component.JWT().GenerateToken(user.Id, secret)
|
||||
if err != nil {
|
||||
log.Errorf("用户[%s]生成token失败: %v", user.Account, err.Error())
|
||||
response.R(c).FailedWithError("登陆失败!")
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("X-TOKEN", secret)
|
||||
response.R(c).OkWithData(map[string]any{
|
||||
"token": token,
|
||||
"type": "Bearer",
|
||||
"expireAt": expireAt,
|
||||
})
|
||||
}
|
||||
|
||||
// Logout
|
||||
// @description: 退出登陆
|
||||
// @receiver LoginApi
|
||||
// @param c
|
||||
func (Login) Logout(c *gin.Context) {
|
||||
loginUser, ok := c.Get("user")
|
||||
if !ok {
|
||||
response.R(c).AuthorizationFailed("未登陆")
|
||||
return
|
||||
}
|
||||
|
||||
if err := component.JWT().Logout(loginUser.(*vo.User).Id); err != nil {
|
||||
response.R(c).FailedWithError("退出登陆失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
185
http/api/admin/user.go
Normal file
185
http/api/admin/user.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"website-nav/http/param"
|
||||
"website-nav/http/response"
|
||||
"website-nav/http/vo"
|
||||
"website-nav/model"
|
||||
"website-nav/service"
|
||||
"website-nav/utils"
|
||||
)
|
||||
|
||||
type User struct{}
|
||||
|
||||
func UserAPI() User {
|
||||
return User{}
|
||||
}
|
||||
|
||||
// GetLoginUser
|
||||
// @description: 获取当前登陆用户信息
|
||||
// @receiver u
|
||||
// @param c
|
||||
func (u User) GetLoginUser(c *gin.Context) {
|
||||
var loginUser *vo.User
|
||||
if loginUser = GetCurrentLoginUser(c); c.IsAborted() {
|
||||
return
|
||||
}
|
||||
response.R(c).OkWithData(loginUser)
|
||||
}
|
||||
|
||||
// List
|
||||
// @description: 用户列表
|
||||
// @receiver u
|
||||
// @param c
|
||||
func (u User) List(c *gin.Context) {
|
||||
var p param.UserList
|
||||
if err := c.ShouldBind(&p); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, total, err := service.User().List(p)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).Paginate(data, total, p.Current, p.Size)
|
||||
}
|
||||
|
||||
// Save
|
||||
// @description: 新增/编辑用户
|
||||
// @receiver u
|
||||
// @param c
|
||||
func (u User) Save(c *gin.Context) {
|
||||
var p param.SaveUser
|
||||
if err := c.ShouldBind(&p); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
ent := &model.User{
|
||||
Base: model.Base{
|
||||
Id: p.Id,
|
||||
},
|
||||
Account: p.Account,
|
||||
Password: p.Password,
|
||||
Avatar: p.Avatar,
|
||||
Nickname: p.Nickname,
|
||||
Contact: p.Contact,
|
||||
}
|
||||
|
||||
if err := service.User().CreateUser(ent); err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @description: 删除用户
|
||||
// @receiver u
|
||||
// @param c
|
||||
func (u User) Delete(c *gin.Context) {
|
||||
var id = c.Param("id")
|
||||
if id == "" || id == "undefined" {
|
||||
response.R(c).Validator(errors.New("id不能为空"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.User().Delete(id); err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
||||
|
||||
// ChangePassword
|
||||
// @description: 修改密码
|
||||
// @receiver u
|
||||
// @param c
|
||||
func (u User) ChangePassword(c *gin.Context) {
|
||||
var p param.ChangePassword
|
||||
if err := c.ShouldBind(&p); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
user := GetCurrentLoginUser(c)
|
||||
if user == nil {
|
||||
response.R(c).FailedWithError("用户信息错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 判断原密码是否对
|
||||
if !utils.Password().ComparePassword(user.Password, p.OriginalPassword) {
|
||||
response.R(c).FailedWithError("原密码错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
if err := service.User().ChangePassword(user.Id, p.NewPassword); err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
||||
|
||||
// ResetPassword
|
||||
// @description: 重置密码
|
||||
// @receiver UserApi
|
||||
// @param c
|
||||
func (u User) ResetPassword(c *gin.Context) {
|
||||
var id = c.Param("id")
|
||||
if id == "" || id == "undefined" {
|
||||
response.R(c).FailedWithError("id不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
loginUser := GetCurrentLoginUser(c)
|
||||
if loginUser == nil {
|
||||
response.R(c).FailedWithError("用户信息错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 先查询一下
|
||||
user, err := service.User().GetUserById(id)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError("获取用户信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Account == loginUser.Account {
|
||||
response.R(c).FailedWithError("当前用户不可重置密码")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
if err := service.User().ChangePassword(user.Id, "admin123"); err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
||||
|
||||
// GenerateAvatar
|
||||
// @description: 生成头像
|
||||
// @receiver UserApi
|
||||
// @param c
|
||||
func (u User) GenerateAvatar(c *gin.Context) {
|
||||
avatar, err := utils.Avatar().GenerateAvatar(false)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError(fmt.Errorf("生成头像失败: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OkWithData(fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString([]byte(avatar))))
|
||||
}
|
77
http/middleware/authorization.go
Normal file
77
http/middleware/authorization.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
"website-nav/component"
|
||||
"website-nav/http/response"
|
||||
"website-nav/service"
|
||||
"website-nav/utils"
|
||||
)
|
||||
|
||||
// Authorization
|
||||
// @description: 授权中间件
|
||||
// @return gin.HandlerFunc
|
||||
func Authorization() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" || !strings.HasPrefix(token, "Bearer ") {
|
||||
response.R(c).AuthorizationFailed("未登陆")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
hashPassword := c.Request.Header.Get("X-TOKEN")
|
||||
if hashPassword == "" {
|
||||
response.R(c).AuthorizationFailed("未登陆")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userClaims, err := component.JWT().ParseToken(token, hashPassword)
|
||||
if err != nil {
|
||||
response.R(c).AuthorizationFailed("未登陆")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果token的颁发者与请求的站点不一致那么就给它抬出去
|
||||
if !slices.Contains(strings.Split(userClaims.Issuer, ","), utils.WebSite().GetHost(c.Request.Header.Get("Referer"))) {
|
||||
response.R(c).AuthorizationFailed("未登陆")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 查询用户
|
||||
user, err := service.User().GetUserById(userClaims.ID)
|
||||
if err != nil {
|
||||
response.R(c).AuthorizationFailed("用户不存在")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户信息放入上下文
|
||||
c.Set("user", &user)
|
||||
|
||||
if c.Request.RequestURI == "/api/user/logout" {
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// 生成一个新token
|
||||
secret := component.JWT().GenerateSecret(user.Password, uuid.NewString(), time.Now().Local().String())
|
||||
tokenStr, _, err := component.JWT().GenerateToken(user.Id, secret, userClaims.ExpiresAt.Time, time.Now().Local())
|
||||
if err != nil {
|
||||
response.R(c).AuthorizationFailed("校验失败")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Authorization", fmt.Sprintf("Bearer %s", tokenStr))
|
||||
c.Writer.Header().Set("X-TOKEN", secret)
|
||||
c.Next()
|
||||
}
|
||||
}
|
10
http/param/login.go
Normal file
10
http/param/login.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package param
|
||||
|
||||
// Login
|
||||
// @description: 登陆
|
||||
type Login struct {
|
||||
Account string `json:"account" form:"account" label:"账号" binding:"required,min=2,max=20"`
|
||||
Password string `json:"password" form:"password" label:"密码" binding:"required,min=8,max=32"`
|
||||
CaptchaId string `json:"captchaId" form:"captchaId" label:"验证码ID" binding:"required"`
|
||||
CaptchaCode string `json:"captchaCode" form:"captchaCode" label:"验证码" binding:"required,max=4" `
|
||||
}
|
6
http/param/request.go
Normal file
6
http/param/request.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package param
|
||||
|
||||
type Page struct {
|
||||
Current int64 `json:"current" form:"current" label:"页码数" binding:"required"`
|
||||
Size int64 `json:"size" form:"size" label:"每页数量" binging:"required"`
|
||||
}
|
26
http/param/user.go
Normal file
26
http/param/user.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package param
|
||||
|
||||
// UserList
|
||||
// @description: 用户列表
|
||||
type UserList struct {
|
||||
Page
|
||||
}
|
||||
|
||||
// SaveUser
|
||||
// @description: 新增/编辑用户
|
||||
type SaveUser struct {
|
||||
Id string `json:"id" form:"id" label:"id" binding:"omitempty"` // id
|
||||
Account string `json:"account" form:"account" label:"账户号" binding:"required_without=Id"` // 账户号
|
||||
Password string `json:"password" form:"password" label:"密码" binding:"omitempty"` // 密码
|
||||
Nickname string `json:"nickname" form:"nickname" label:"昵称" binding:"required,min=2"` // 昵称
|
||||
Avatar string `json:"avatar" form:"avatar" label:"头像" binding:"omitempty"` // 头像
|
||||
Contact string `json:"contact" form:"contact" label:"联系方式" binding:"omitempty"` // 联系方式
|
||||
}
|
||||
|
||||
// ChangePassword
|
||||
// @description: 修改密码
|
||||
type ChangePassword struct {
|
||||
OriginalPassword string `json:"originalPassword" form:"originalPassword" label:"原密码" binding:"required,min=8,max=32"` // 原密码
|
||||
NewPassword string `json:"newPassword" form:"newPassword" label:"新密码" binding:"required,min=8,max=32"` // 新密码
|
||||
ConfirmPassword string `json:"confirmPassword" form:"confirmPassword" label:"确认密码" binding:"eqfield=NewPassword"` // 确认密码
|
||||
}
|
131
http/response/response.go
Normal file
131
http/response/response.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"website-nav/component"
|
||||
"website-nav/utils"
|
||||
)
|
||||
|
||||
type PageData[T any] struct {
|
||||
Current int `json:"current"` // 当前页码
|
||||
Size int `json:"size"` // 每页数量
|
||||
Total int64 `json:"total"` // 总数
|
||||
TotalPage int `json:"totalPage"` // 总页数
|
||||
Records T `json:"records"` // 返回数据
|
||||
}
|
||||
|
||||
type HttpResponse struct{ *gin.Context }
|
||||
|
||||
func R(c *gin.Context) HttpResponse {
|
||||
return HttpResponse{c}
|
||||
}
|
||||
|
||||
// OK
|
||||
// @description: success
|
||||
// @receiver h
|
||||
func (h HttpResponse) OK() {
|
||||
h.Context.JSON(http.StatusOK, gin.H{
|
||||
"code": http.StatusOK,
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// OkWithData
|
||||
// @description: success with data
|
||||
// @receiver h
|
||||
// @param data
|
||||
func (h HttpResponse) OkWithData(data any) {
|
||||
h.Context.JSON(http.StatusOK, gin.H{
|
||||
"code": http.StatusOK,
|
||||
"message": "success",
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
// Paginate
|
||||
// @description: success page data
|
||||
// @receiver h
|
||||
// @param data
|
||||
// @param total
|
||||
// @param current
|
||||
// @param size
|
||||
func (h HttpResponse) Paginate(data any, total int64, current, size int64) {
|
||||
// 处理一下页码、页数量
|
||||
if current == -1 {
|
||||
current = 1
|
||||
size = total
|
||||
}
|
||||
// 计算总页码
|
||||
totalPage := utils.Paginate().Generate(total, int(size))
|
||||
// 返回结果
|
||||
h.Context.JSON(http.StatusOK, map[string]any{
|
||||
"code": http.StatusOK,
|
||||
"data": &PageData[any]{Current: int(current), Size: int(size), Total: total, TotalPage: totalPage, Records: data},
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// AuthorizationFailed
|
||||
// @description: authorization failed
|
||||
// @receiver h
|
||||
// @param msg
|
||||
func (h HttpResponse) AuthorizationFailed(msg string) {
|
||||
if msg == "" {
|
||||
msg = "authorized failed"
|
||||
}
|
||||
h.Context.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": msg,
|
||||
})
|
||||
}
|
||||
|
||||
// Failed
|
||||
// @description: request bad
|
||||
// @receiver h
|
||||
func (h HttpResponse) Failed() {
|
||||
h.Context.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"message": "failed",
|
||||
})
|
||||
}
|
||||
|
||||
// FailedWithError
|
||||
// @description: custom error
|
||||
// @receiver h
|
||||
// @param err
|
||||
func (h HttpResponse) FailedWithError(err any) {
|
||||
var errStr string
|
||||
switch err.(type) {
|
||||
case error:
|
||||
errStr = err.(error).Error()
|
||||
case string:
|
||||
errStr = err.(string)
|
||||
}
|
||||
|
||||
h.Context.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"message": errStr,
|
||||
})
|
||||
}
|
||||
|
||||
// Validator
|
||||
// @description: request param error
|
||||
// @receiver h
|
||||
// @param err
|
||||
func (h HttpResponse) Validator(err error) {
|
||||
h.Context.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"message": component.Validate().Error(err),
|
||||
})
|
||||
}
|
||||
|
||||
// Internal
|
||||
// @description: server error
|
||||
// @receiver h
|
||||
func (h HttpResponse) Internal() {
|
||||
h.Context.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": http.StatusInternalServerError,
|
||||
"message": "server error",
|
||||
})
|
||||
}
|
51
http/router/admin.go
Normal file
51
http/router/admin.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"website-nav/http/api/admin"
|
||||
"website-nav/http/middleware"
|
||||
)
|
||||
|
||||
func adminAPI(r *gin.RouterGroup) {
|
||||
adminApi := r.Group("admin")
|
||||
{
|
||||
loginAPI(adminApi)
|
||||
userAPI(adminApi)
|
||||
navTypeAPI(adminApi)
|
||||
}
|
||||
}
|
||||
|
||||
// loginAPI
|
||||
// @description: 登陆相关API
|
||||
// @param r
|
||||
func loginAPI(r *gin.RouterGroup) {
|
||||
login := r.Group("/login")
|
||||
{
|
||||
login.GET("/captcha", admin.LoginAPI().Captcha) // 获取验证码
|
||||
login.POST("/token", admin.LoginAPI().Login) // 登陆
|
||||
}
|
||||
}
|
||||
|
||||
// userAPI
|
||||
// @description: 用户相关API
|
||||
// @param r
|
||||
func userAPI(r *gin.RouterGroup) {
|
||||
user := r.Group("/user")
|
||||
user.Use(middleware.Authorization())
|
||||
{
|
||||
user.GET("/info", admin.UserAPI().GetLoginUser) // 获取当前登陆用户信息
|
||||
user.GET("/list", admin.UserAPI().List) // 用户列表
|
||||
user.POST("/save", admin.UserAPI().Save) // 新增/编辑用户
|
||||
user.DELETE("/:id", admin.UserAPI().Delete) // 删除用户
|
||||
user.PUT("/change-password", admin.UserAPI().ChangePassword) // 修改密码【当前用户更改自己的】
|
||||
user.PUT("/reset-password/:id", admin.UserAPI().ResetPassword) // 重置密码 【重置其他管理员的】
|
||||
user.GET("/generate-avatar", admin.UserAPI().GenerateAvatar) // 生成头像
|
||||
}
|
||||
}
|
||||
|
||||
// navTypeAPI
|
||||
// @description: 导航分类API
|
||||
// @param r
|
||||
func navTypeAPI(r *gin.RouterGroup) {
|
||||
|
||||
}
|
33
http/router/root.go
Normal file
33
http/router/root.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Option func(engine *gin.RouterGroup)
|
||||
|
||||
var options []Option
|
||||
|
||||
func includeRouters(opts ...Option) {
|
||||
options = append(options, opts...)
|
||||
}
|
||||
|
||||
func InitRouter() *gin.Engine {
|
||||
r := gin.New()
|
||||
// 开启IP 追踪
|
||||
r.ForwardedByClientIP = true
|
||||
// 将请求打印至控制台
|
||||
r.Use(gin.Logger())
|
||||
|
||||
for _, opt := range options {
|
||||
opt(r.Group("api"))
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func Rooters() {
|
||||
includeRouters(
|
||||
adminAPI,
|
||||
)
|
||||
}
|
18
http/vo/navigation.go
Normal file
18
http/vo/navigation.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package vo
|
||||
|
||||
// TypeItem
|
||||
// @description: 导航分类
|
||||
type TypeItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
type NavigationItem struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
Icon string `json:"icon"`
|
||||
Desc string `json:"desc"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
25
http/vo/user.go
Normal file
25
http/vo/user.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package vo
|
||||
|
||||
import "website-nav/model"
|
||||
|
||||
// UserItem
|
||||
// @description: 用户列表的数据
|
||||
type UserItem struct {
|
||||
Id string `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Avatar string `json:"avatar"`
|
||||
Nickname string `json:"nickname"`
|
||||
Contact string `json:"contact"`
|
||||
CreatedAt model.JsonTime `json:"createdAt"`
|
||||
UpdatedAt model.JsonTime `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// User
|
||||
// @description: 用户信息
|
||||
type User struct {
|
||||
Id string `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Password string `json:"-"`
|
||||
Nickname string `json:"nickname"`
|
||||
Contact string `json:"contact"`
|
||||
}
|
Reference in New Issue
Block a user