mirror of
https://github.com/quarkcloudio/quark-go.git
synced 2025-10-05 16:06:53 +08:00
chore: wip
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/install"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/middleware"
|
||||
admininstall "github.com/quarkcms/quark-go/v2/pkg/app/admin/install"
|
||||
adminmiddleware "github.com/quarkcms/quark-go/v2/pkg/app/admin/middleware"
|
||||
adminservice "github.com/quarkcms/quark-go/v2/pkg/app/admin/service"
|
||||
miniappinstall "github.com/quarkcms/quark-go/v2/pkg/app/miniapp/install"
|
||||
miniappmiddleware "github.com/quarkcms/quark-go/v2/pkg/app/miniapp/middleware"
|
||||
miniappservice "github.com/quarkcms/quark-go/v2/pkg/app/miniapp/service"
|
||||
toolservice "github.com/quarkcms/quark-go/v2/pkg/app/tool/service"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
@@ -44,11 +46,17 @@ func main() {
|
||||
// WEB根目录
|
||||
b.Static("/", "./web/app")
|
||||
|
||||
// 自动构建数据库、拉取静态文件
|
||||
install.Handle()
|
||||
// 构建管理后台数据库
|
||||
admininstall.Handle()
|
||||
|
||||
// 后台中间件
|
||||
b.Use(middleware.Handle)
|
||||
// 管理后台中间件
|
||||
b.Use(adminmiddleware.Handle)
|
||||
|
||||
// 构建MiniApp数据库
|
||||
miniappinstall.Handle()
|
||||
|
||||
// MiniApp中间件
|
||||
b.Use(miniappmiddleware.Handle)
|
||||
|
||||
// 响应Get请求
|
||||
b.GET("/", func(ctx *builder.Context) error {
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/install"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/middleware"
|
||||
adminservice "github.com/quarkcms/quark-go/v2/pkg/app/admin/service"
|
||||
mixservice "github.com/quarkcms/quark-go/v2/pkg/app/mix/service"
|
||||
toolservice "github.com/quarkcms/quark-go/v2/pkg/app/tool/service"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
"gorm.io/driver/sqlite"
|
||||
@@ -22,9 +21,6 @@ func main() {
|
||||
// 加载后台服务
|
||||
providers = append(providers, adminservice.Providers...)
|
||||
|
||||
// 加载Mix服务
|
||||
providers = append(providers, mixservice.Providers...)
|
||||
|
||||
// 加载工具服务
|
||||
providers = append(providers, toolservice.Providers...)
|
||||
|
||||
|
@@ -13,6 +13,7 @@ var Providers = []interface{}{
|
||||
&logins.Index{},
|
||||
&layouts.Index{},
|
||||
&dashboards.Index{},
|
||||
&resources.User{},
|
||||
&resources.Admin{},
|
||||
&resources.Role{},
|
||||
&resources.Permission{},
|
||||
|
192
pkg/app/admin/service/resources/user.go
Normal file
192
pkg/app/admin/service/resources/user.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/component/form/fields/radio"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/component/form/rule"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/service/actions"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/service/searches"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/admin/template/resource"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/model"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/utils/hash"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
resource.Template
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (p *User) Init(ctx *builder.Context) interface{} {
|
||||
|
||||
// 标题
|
||||
p.Title = "用户"
|
||||
|
||||
// 模型
|
||||
p.Model = &model.User{}
|
||||
|
||||
// 分页
|
||||
p.PerPage = 10
|
||||
|
||||
// 是否具有导出功能
|
||||
p.WithExport = true
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// 字段
|
||||
func (p *User) Fields(ctx *builder.Context) []interface{} {
|
||||
field := &resource.Field{}
|
||||
|
||||
return []interface{}{
|
||||
field.ID("id", "ID"),
|
||||
|
||||
field.Image("avatar", "头像").OnlyOnForms(),
|
||||
|
||||
field.Text("username", "用户名", func() interface{} {
|
||||
|
||||
return "<a href='#/layout/index?api=/api/admin/user/edit&id=" + strconv.Itoa(p.Field["id"].(int)) + "'>" + p.Field["username"].(string) + "</a>"
|
||||
}).
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "用户名必须填写"),
|
||||
rule.Min(6, "用户名不能少于6个字符"),
|
||||
rule.Max(20, "用户名不能超过20个字符"),
|
||||
}).
|
||||
SetCreationRules([]*rule.Rule{
|
||||
rule.Unique("users", "username", "用户名已存在"),
|
||||
}).
|
||||
SetUpdateRules([]*rule.Rule{
|
||||
rule.Unique("users", "username", "{id}", "用户名已存在"),
|
||||
}),
|
||||
|
||||
field.Text("nickname", "昵称").
|
||||
SetEditable(true).
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "昵称必须填写"),
|
||||
}),
|
||||
|
||||
field.Text("email", "邮箱").
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "邮箱必须填写"),
|
||||
rule.Email("邮箱格式错误"),
|
||||
}).
|
||||
SetCreationRules([]*rule.Rule{
|
||||
rule.Unique("users", "email", "邮箱已存在"),
|
||||
}).
|
||||
SetUpdateRules([]*rule.Rule{
|
||||
rule.Unique("users", "email", "{id}", "邮箱已存在"),
|
||||
}),
|
||||
|
||||
field.Text("phone", "手机号").
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "手机号必须填写"),
|
||||
rule.Phone("手机号格式错误"),
|
||||
}).
|
||||
SetCreationRules([]*rule.Rule{
|
||||
rule.Unique("users", "phone", "手机号已存在"),
|
||||
}).
|
||||
SetUpdateRules([]*rule.Rule{
|
||||
rule.Unique("users", "phone", "{id}", "手机号已存在"),
|
||||
}),
|
||||
|
||||
field.Radio("sex", "性别").
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "请选择性别"),
|
||||
}).
|
||||
SetOptions([]*radio.Option{
|
||||
{
|
||||
Value: 1,
|
||||
Label: "男",
|
||||
},
|
||||
{
|
||||
Value: 2,
|
||||
Label: "女",
|
||||
},
|
||||
}).
|
||||
SetFilters(true).
|
||||
SetDefault(1),
|
||||
|
||||
field.Password("password", "密码").
|
||||
SetCreationRules([]*rule.Rule{
|
||||
rule.Required(true, "密码必须填写"),
|
||||
}).
|
||||
OnlyOnForms().
|
||||
ShowOnImporting(true),
|
||||
|
||||
field.Datetime("last_login_time", "最后登录时间", func() interface{} {
|
||||
if p.Field["last_login_time"] == nil {
|
||||
return p.Field["last_login_time"]
|
||||
}
|
||||
|
||||
if p.Field["last_login_time"].(time.Time).Format("2006-01-02 15:04:05") == "0001-01-01 00:00:00" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.Field["last_login_time"].(time.Time).Format("2006-01-02 15:04:05")
|
||||
}).OnlyOnIndex(),
|
||||
|
||||
field.Switch("status", "状态").
|
||||
SetRules([]*rule.Rule{
|
||||
rule.Required(true, "请选择状态"),
|
||||
}).
|
||||
SetTrueValue("正常").
|
||||
SetFalseValue("禁用").
|
||||
SetEditable(true).
|
||||
SetDefault(true),
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
func (p *User) Searches(ctx *builder.Context) []interface{} {
|
||||
|
||||
return []interface{}{
|
||||
searches.Input("username", "用户名"),
|
||||
searches.Input("nickname", "昵称"),
|
||||
searches.Status(),
|
||||
searches.DatetimeRange("last_login_time", "登录时间"),
|
||||
}
|
||||
}
|
||||
|
||||
// 行为
|
||||
func (p *User) Actions(ctx *builder.Context) []interface{} {
|
||||
|
||||
return []interface{}{
|
||||
actions.Import(),
|
||||
actions.CreateLink(),
|
||||
actions.BatchDelete(),
|
||||
actions.BatchDisable(),
|
||||
actions.BatchEnable(),
|
||||
actions.DetailLink(),
|
||||
actions.More().
|
||||
SetActions([]interface{}{
|
||||
actions.EditLink(),
|
||||
actions.Delete(),
|
||||
}),
|
||||
actions.FormSubmit(),
|
||||
actions.FormReset(),
|
||||
actions.FormBack(),
|
||||
actions.FormExtraBack(),
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑页面显示前回调
|
||||
func (p *User) BeforeEditing(ctx *builder.Context, data map[string]interface{}) map[string]interface{} {
|
||||
|
||||
// 编辑页面清理password
|
||||
delete(data, "password")
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 保存数据前回调
|
||||
func (p *User) BeforeSaving(ctx *builder.Context, submitData map[string]interface{}) (map[string]interface{}, error) {
|
||||
|
||||
// 加密密码
|
||||
if submitData["password"] != nil {
|
||||
submitData["password"] = hash.Make(submitData["password"].(string))
|
||||
}
|
||||
|
||||
return submitData, nil
|
||||
}
|
26
pkg/app/miniapp/install/install.go
Normal file
26
pkg/app/miniapp/install/install.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/model"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/dal/db"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 执行安装操作
|
||||
func Handle() {
|
||||
|
||||
// 迁移数据
|
||||
db.Client.AutoMigrate(
|
||||
&model.User{},
|
||||
)
|
||||
|
||||
// 如果用户不存在,初始化数据库数据
|
||||
userInfo, err := (&model.User{}).GetInfoById(1)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
panic(err)
|
||||
}
|
||||
if userInfo.Id == 0 {
|
||||
// 数据填充
|
||||
(&model.User{}).Seeder()
|
||||
}
|
||||
}
|
30
pkg/app/miniapp/middleware/middleware.go
Normal file
30
pkg/app/miniapp/middleware/middleware.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/model"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
)
|
||||
|
||||
// 中间件
|
||||
func Handle(ctx *builder.Context) error {
|
||||
|
||||
// 排除非后台路由
|
||||
if !strings.Contains(ctx.Path(), "api/miniapp/user") {
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
// 获取登录信息
|
||||
userInfo, err := (&model.User{}).GetAuthUser(ctx.Engine.GetConfig().AppKey, ctx.Token())
|
||||
if err != nil {
|
||||
return ctx.JSON(401, builder.Error(err.Error()))
|
||||
}
|
||||
|
||||
guardName := userInfo.GuardName
|
||||
if guardName != "user" {
|
||||
return ctx.JSON(401, builder.Error("401 Unauthozied"))
|
||||
}
|
||||
|
||||
return ctx.Next()
|
||||
}
|
145
pkg/app/miniapp/model/user.go
Normal file
145
pkg/app/miniapp/model/user.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
adminmodel "github.com/quarkcms/quark-go/v2/pkg/app/admin/model"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/dal/db"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/utils/hash"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 字段
|
||||
type User struct {
|
||||
Id int `json:"id" gorm:"autoIncrement"`
|
||||
Username string `json:"username" gorm:"size:20;index:Users_username_unique,unique;not null"`
|
||||
Nickname string `json:"nickname" gorm:"size:200;not null"`
|
||||
Sex int `json:"sex" gorm:"size:4;not null;default:1"`
|
||||
Email string `json:"email" gorm:"size:50;index:users_email_unique,unique;not null"`
|
||||
Phone string `json:"phone" gorm:"size:11;index:users_phone_unique,unique;not null"`
|
||||
Password string `json:"password" gorm:"size:255;not null"`
|
||||
Avatar string `json:"avatar" gorm:"size:1000"`
|
||||
LastLoginIp string `json:"last_login_ip" gorm:"size:255"`
|
||||
LastLoginTime time.Time `json:"last_login_time"`
|
||||
WxOpenid string `json:"wx_openid" gorm:"size:255"`
|
||||
WxUnionid string `json:"wx_unionid" gorm:"size:255"`
|
||||
Status int `json:"status" gorm:"size:1;not null;default:1"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at"`
|
||||
}
|
||||
|
||||
// 用户JWT结构体
|
||||
type UserClaims struct {
|
||||
Id int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Sex int `json:"sex"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Avatar string `json:"avatar"`
|
||||
GuardName string `json:"guard_name"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// 用户Seeder
|
||||
func (model *User) Seeder() {
|
||||
|
||||
// 如果菜单已存在,不执行Seeder操作
|
||||
if (&adminmodel.Menu{}).IsExist(18) {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
menuSeeders := []*adminmodel.Menu{
|
||||
{Id: 18, Name: "用户管理", GuardName: "admin", Icon: "icon-user", Type: 1, Pid: 0, Sort: 0, Path: "/user", Show: 1, IsEngine: 0, IsLink: 0, Status: 1},
|
||||
{Id: 19, Name: "用户列表", GuardName: "admin", Icon: "", Type: 2, Pid: 18, Sort: 0, Path: "/api/admin/user/index", Show: 1, IsEngine: 1, IsLink: 0, Status: 1},
|
||||
}
|
||||
db.Client.Create(&menuSeeders)
|
||||
|
||||
seeders := []User{
|
||||
{Username: "tangtanglove", Nickname: "默认用户", Email: "tangtanglove@yourweb.com", Phone: "10086", Password: hash.Make("123456"), Sex: 1, Status: 1, LastLoginTime: time.Now()},
|
||||
}
|
||||
|
||||
db.Client.Create(&seeders)
|
||||
}
|
||||
|
||||
// 获取用户JWT信息
|
||||
func (model *User) GetClaims(UserInfo *User) (userClaims *UserClaims) {
|
||||
userClaims = &UserClaims{
|
||||
UserInfo.Id,
|
||||
UserInfo.Username,
|
||||
UserInfo.Nickname,
|
||||
UserInfo.Sex,
|
||||
UserInfo.Email,
|
||||
UserInfo.Phone,
|
||||
UserInfo.Avatar,
|
||||
"user",
|
||||
jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间,默认24小时
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()), // 颁发时间
|
||||
NotBefore: jwt.NewNumericDate(time.Now()), // 不早于时间
|
||||
Issuer: "QuarkGo", // 颁发人
|
||||
Subject: "User Token", // 主题信息
|
||||
},
|
||||
}
|
||||
|
||||
return userClaims
|
||||
}
|
||||
|
||||
// 获取当前认证的用户信息,默认参数为tokenString
|
||||
func (model *User) GetAuthUser(appKey string, tokenString string) (userClaims *UserClaims, Error error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(appKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
||||
return nil, errors.New("token格式错误")
|
||||
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
|
||||
return nil, errors.New("token已过期")
|
||||
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
|
||||
return nil, errors.New("token未生效")
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("token不可用")
|
||||
}
|
||||
|
||||
// 通过ID获取用户信息
|
||||
func (model *User) GetInfoById(id interface{}) (User *User, Error error) {
|
||||
err := db.Client.Where("status = ?", 1).Where("id = ?", id).First(&User).Error
|
||||
|
||||
return User, err
|
||||
}
|
||||
|
||||
// 通过用户名获取用户信息
|
||||
func (model *User) GetInfoByUsername(username string) (User *User, Error error) {
|
||||
err := db.Client.Where("status = ?", 1).Where("username = ?", username).First(&User).Error
|
||||
if User.Avatar != "" {
|
||||
User.Avatar = (&adminmodel.Picture{}).GetPath(User.Avatar) // 获取头像地址
|
||||
}
|
||||
|
||||
return User, err
|
||||
}
|
||||
|
||||
// 更新最后一次登录数据
|
||||
func (model *User) UpdateLastLogin(uid int, lastLoginIp string, lastLoginTime time.Time) error {
|
||||
data := User{
|
||||
LastLoginIp: lastLoginIp,
|
||||
LastLoginTime: lastLoginTime,
|
||||
}
|
||||
|
||||
return db.Client.
|
||||
Where("id = ?", uid).
|
||||
Updates(&data).Error
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/component/navbar"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/template/page"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
)
|
||||
@@ -15,11 +14,6 @@ func (p *My) Init(ctx *builder.Context) interface{} {
|
||||
return p
|
||||
}
|
||||
|
||||
// 头部导航
|
||||
func (p *My) Navbar(ctx *builder.Context, navbar *navbar.Component) interface{} {
|
||||
return navbar.SetTitle("我的")
|
||||
}
|
||||
|
||||
// 组件渲染
|
||||
func (p *My) Content(ctx *builder.Context) interface{} {
|
||||
return "我的"
|
||||
|
50
pkg/app/miniapp/template/login/login.go
Normal file
50
pkg/app/miniapp/template/login/login.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/quarkcms/quark-go/v2/pkg/app/miniapp/template/page"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/builder"
|
||||
"github.com/quarkcms/quark-go/v2/pkg/dal/db"
|
||||
)
|
||||
|
||||
// 后台登录模板
|
||||
type Template struct {
|
||||
page.Template
|
||||
FromStyle string
|
||||
Api string
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (p *Template) Init(ctx *builder.Context) interface{} {
|
||||
return p
|
||||
}
|
||||
|
||||
// 初始化模板
|
||||
func (p *Template) TemplateInit(ctx *builder.Context) interface{} {
|
||||
|
||||
// 初始化数据对象
|
||||
p.DB = db.Client
|
||||
|
||||
// 标题
|
||||
p.Title = "登录"
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// 初始化路由映射
|
||||
func (p *Template) RouteInit() interface{} {
|
||||
p.GET("/api/miniapp/login/:resource/index", p.Render) // 渲染登录页面路由
|
||||
p.POST("/api/miniapp/login/:resource/handle", p.Handle) // 后台登录执行路由
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// 内容
|
||||
func (p *Template) Content(ctx *builder.Context) interface{} {
|
||||
|
||||
return "登录页面"
|
||||
}
|
||||
|
||||
// 执行表单
|
||||
func (p *Template) Handle(ctx *builder.Context) error {
|
||||
return ctx.JSONError("请自行处理表单逻辑")
|
||||
}
|
Reference in New Issue
Block a user