tiger1103 / gfast Goto Github PK
View Code? Open in Web Editor NEW基于GF(Go Frame)的后台管理系统
Home Page: http://www.g-fast.cn/
License: Apache License 2.0
基于GF(Go Frame)的后台管理系统
Home Page: http://www.g-fast.cn/
License: Apache License 2.0
这个工作流功能能单独提取出来吗?这个看上去不错。
gfast的框架不错,借着学习Go语言的机会,码了个学习社区,https://pub6.top
大神,会添加站点配置,SEO,字符替换,URL自定义,伪静态等功能吗?
☁ gfast [os-v3] ⚡ go mod tidy -v
go: downloading github.com/gogf/gf/v2 v2.1.0
go: finding module for package github.com/gogf/gf/v2/protocol/goai
github.com/tiger1103/gfast/v3/internal/cmd imports
github.com/gogf/gf/v2/protocol/goai: module github.com/gogf/gf/v2@latest found (v2.1.2, replaced by github.com/gogf/gf/[email protected]), but does not contain package github.com/gogf/gf/v2/protocol/goai
原函数:
func (a *adapterCasbin) initPolicy(ctx context.Context) {
// Because the DB is empty at first,
// so we need to load the policy from the file adapter (.CSV) first.
e, err := casbin.NewSyncedEnforcer(g.Cfg().MustGet(ctx, "casbin.modelFile").String(), a)
if err != nil {
a.EnforcerErr = err
return
}
// This is a trick to save the current policy to the DB.
// We can't call e.SavePolicy() because the adapter in the enforcer is still the file adapter.
// The current policy means the policy in the Casbin enforcer (aka in memory).
//err = a.SavePolicy(e.GetModel())
//if err != nil {
// return err
//}
//set adapter
//e.SetAdapter(a)
// Clear the current policy.
e.ClearPolicy()
a.Enforcer = e
// Load the policy from DB.
err = a.LoadPolicy(e.GetModel())
if err != nil {
a.EnforcerErr = err
return
}
}
这行代码:e, err := casbin.NewSyncedEnforcer(g.Cfg().MustGet(ctx, "casbin.modelFile").String(), a)
(源码调用见casbin\casbin\[email protected]\enforcer.go第178行)
它内部会 *adapterCasbin 的 LoadPolicy方法。
而下面的代码又手动再调用了一次:err = a.LoadPolicy(e.GetModel())
这样它就在数据库里查了两次
建议生成的代码里面可以增加批量导入导出功能
1、目录命名冗余 例如:
比如,service目录下,一些目录命名还带service
2、变量命名驼峰和下划线混用,建议变量命名用驼峰法
感谢大佬的分享。大佬有没有集成swagger的计划呢?感觉现在查看API接口不太方便
job_name 写成了 job_mame
gfast/app/model/admin/sys_job/sys_job.go
Line 18 in fae3d8b
Error: Cannot find module './plugin/blog/classification/list' at o (chunk-09e67496.3d5711de.js:13) at r (chunk-09e67496.3d5711de.js:13) at app.0af133c8.js:1
但是在你的demo后台地址上是可以打开的,大佬求把新的代码上传~~~
在os-v3里登出的路由是 api/v1/system/loginOut ,为什么使用loginOut 这个词语,是有意写成这样的吗?
当修改了,
字典管理 中的 字典类型 ,
字典数据 中的 字典类型 没有跟着更新,
导致 点击 字典管理中的 字典类型, 查不到数据.
请问后续会增加注册用户和商城相关功能吗?
目前go好像没看到有弄商城的,我觉得gfast可以往这方面发展下。
http://demo.g-fast.cn/tool/gen/batchGenCode?tables=user 404
看前端有
/** 生成代码操作 */
handleGenTable(row) {
const tableNames = row.tableName || this.tableNames;
if (tableNames == "") {
this.msgError("请选择要生成的数据");
return;
}
downLoadZip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi");
},
后端代码并没有看到对应的接口
//代码生成
group.Group("/tools", func(group *ghttp.RouterGroup) {
group.ALL("/gen", new(admin.Gen))
})
感谢开源
方便对应文档学习
登录界面请求了验证码 /sysLogin/public/verify 报了404?
将所有功能抽象为插件构建系统,基于插件设计模式以易于进行拓展
RuoYi-Vue 3.0今天发布了
烦请作者指教一下,万分感谢
您好,我有两个项目都用的gfast开发的,现在遇到个问题,两个项目都在同一个浏览器里登录时,先登录的会被后登录的项目给顶掉,前面的那个项目会提示无权限需要重新登录,请问如何解决呢,是gtoken的那里的问题吗?
We are new to setting up of gfast, but I had set up gfast in the local by following all the steps, able to login, loaded data in MySQL tables, I'm able to see the Menu, pages and content are all in Mandarin language, I'm not able to convert to English and see, Is there any other way I can see the whole gfast application in English? Any other mandarin key fonts I need to install?
能不能把使用 说的详细一点,我登录报错这个,没找到问题在哪,能写一个使用手册更好了
我用get请求,到服务端后需要下载文件,使用以下代码,浏览器并不会弹出下载,请问应该怎么做,谢谢
file := "git-" + todayDate + ".xlsx"
// 读取文件
downFile := gfile.GetBytes(file)
req.Response.Header().Set("Content-Type", "application/octet-stream")
req.Response.Header().Set("Content-Disposition", "attachment; filename="+file)
req.Response.Write(downFile)
req.Exit()
我们这大部分项目业务服和后台服是分离的,不想写多套dao层导致一致性问题
这样重新生成模型后,有好几处直接调用delete findone的地方报错.
gentable的EditReq这个结构,写在了model文件里,gf gen model重新生成的时候,会被覆盖,导致编译出错.
实在是不知道什么原因了,特来麻烦你
如果是用个gcache, ForceLogout 函数没有删除缓存
数据库类型:utf8mb4 字符排序 utf8mb4_general_ci 插入编码:65001 UTF-8
插入提示:
SQL] Query gfast-v3 start
[ERR] 1292 - Incorrect datetime value: '0000-00-00 00:00:00' for column 'last_login_time' at row 1
[ERR]
INSERT INTO sys_login_log
VALUES (643, 'zsa', '127.0.0.1', '内网IP', 'ApiPOST', '', 0, '账号密码错误', '2022-03-16 17:18:41', '系统后台');
INSERT INTO sys_login_log
VALUES (644, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 0, '账号密码错误', '2022-03-16 17:18:48', '系统后台');
INSERT INTO sys_login_log
VALUES (645, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 0, '账号密码错误', '2022-03-16 17:19:45', '系统后台');
INSERT INTO sys_login_log
VALUES (646, 'demo55', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 0, '账号密码错误', '2022-03-16 17:21:06', '系统后台');
INSERT INTO sys_login_log
VALUES (647, 'demo4564646', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 0, '账号密码错误', '2022-03-16 17:21:21', '系统后台');
INSERT INTO sys_login_log
VALUES (648, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:21:56', '系统后台');
INSERT INTO sys_login_log
VALUES (649, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:22:52', '系统后台');
INSERT INTO sys_login_log
VALUES (650, 'demo2', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 0, '账号密码错误', '2022-03-16 17:24:11', '系统后台');
INSERT INTO sys_login_log
VALUES (651, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:24:37', '系统后台');
INSERT INTO sys_login_log
VALUES (652, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:25:12', '系统后台');
INSERT INTO sys_login_log
VALUES (653, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:25:22', '系统后台');
INSERT INTO sys_login_log
VALUES (654, 'demo', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:25:27', '系统后台');
INSERT INTO sys_login_log
VALUES (655, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 0, '账号密码错误', '2022-03-16 17:25:56', '系统后台');
INSERT INTO sys_login_log
VALUES (656, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:26:03', '系统后台');
INSERT INTO sys_login_log
VALUES (657, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:26:08', '系统后台');
INSERT INTO sys_login_log
VALUES (658, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:27:17', '系统后台');
INSERT INTO sys_login_log
VALUES (659, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:27:52', '系统后台');
INSERT INTO sys_login_log
VALUES (660, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:28:07', '系统后台');
INSERT INTO sys_login_log
VALUES (661, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:28:10', '系统后台');
INSERT INTO sys_login_log
VALUES (662, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:28:31', '系统后台');
INSERT INTO sys_login_log
VALUES (663, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:29:00', '系统后台');
INSERT INTO sys_login_log
VALUES (664, 'zs', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 1, '登录成功', '2022-03-16 17:30:28', '系统后台');
INSERT INTO sys_login_log
VALUES (665, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:30:46', '系统后台');
INSERT INTO sys_login_log
VALUES (666, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:31:12', '系统后台');
INSERT INTO sys_login_log
VALUES (667, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:31:13', '系统后台');
INSERT INTO sys_login_log
VALUES (668, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:32:00', '系统后台');
INSERT INTO sys_login_log
VALUES (669, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:37:39', '系统后台');
INSERT INTO sys_login_log
VALUES (670, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:42:16', '系统后台');
INSERT INTO sys_login_log
VALUES (671, 'zs', '127.0.0.1', '内网IP', 'ApiPOST', '', 1, '登录成功', '2022-03-16 17:44:00', '系统后台');
INSERT INTO sys_login_log
VALUES (672, 'zsss', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 0, '账号密码错误', '2022-03-16 17:45:23', '系统后台');
INSERT INTO sys_login_log
VALUES (673, 'zsss', '::1', '云南省 曲靖市', 'Chrome', 'Windows 10', 0, '账号密码错误', '2022-03-16 17:45:2
[SQL] Finished with error
您好! 请问目前有计划升级到GoFrame v2 吗?
Hello 👋
I run a security community that finds and fixes vulnerabilities in OSS. A researcher (@cokeBeer) has found a potential issue, which I would be eager to share with you.
Could you add a SECURITY.md
file with an e-mail address for me to send further details to? GitHub recommends a security policy to ensure issues are responsibly disclosed, and it would help direct researchers in the future.
Looking forward to hearing from you 👍
(cc @huntr-helper)
建议搞个用户整合LDAP功能
你写的这个框架很完善很强大,要是在做一个代码生成器就更完美了。
2.0.6存在无法将http修改为0.0.0.0:8080 的问题 会导致 too many colons in address 比较影响使用
建议开发者将库版本升级到 2.1.0 及以上
更换到 2.1.0 之后需要吧 _ "github.com/gogf/gf/contrib/drivers/mysql/v2" 加入到main
使用生成器生成的路由即使设置了中间件后台权限验证依然可以无需登录直接获得内容
//扩展业务
group.Group("/module", func(group *ghttp.RouterGroup) {
group.Middleware(middleWare.Auth) //后台权限验证
//后台操作日志记录
group.Hook("/*", ghttp.HOOK_AFTER_OUTPUT, hook.OperationLog)
group.ALL("/accounts", new(module.Fb_accounts))
})
经测试直接通过
GET http://localhost:8200/module/accounts/list
Accept: application/json
可以获得内容,而不会提示“用户信息验证失败”。
我同时在一个电脑上跑两个gfast项目,配置不同的gtoken的信息,但是两个项目不能同时登录,不然会相互影响,提示用户信息验证失败,我用的gotken模式1,没用redis,难道是哪里还需要配置嘛?
我在 app/service/admin/upload_service/upload.go
中新增了oss上传代码,可作为参考
后台参数管理新增:
名称:文件上传驱动
键名:sys.uploadFile.driver
键值:oss | local
配置config.toml代码:
#OSS文件存储
[oss]
accessID = ""
accessSecret = ""
bucketName = ""
endpoint = "oss-cn-hangzhou.aliyuncs.com"
resUrl = "http://www.xxxx.cn/"
upload.go
package upload_service
import (
"fmt"
"gfast/app/model/admin/sys_config"
"gfast/app/service/admin/params_service"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
"strconv"
"strings"
)
const (
upPath = "/pub_upload/"
driverOSS = "oss"
driverLocal = "local"
)
var uploadPath string
//上传得文件信息
type FileInfo struct {
FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"`
FileUrl string `json:"fileUrl"`
FileType string `json:"fileType"`
}
func init() {
uploadPath = g.Cfg().GetString("server.ServerRoot") + upPath
}
//上传图片
func UpImg(file *ghttp.UploadFile) (fileInfo *FileInfo, err error) {
return upByType(file, "img")
}
//上传文件
func UpFile(file *ghttp.UploadFile) (fileInfo *FileInfo, err error) {
return upByType(file, "file")
}
//批量上传图片
func UpImgs(files []*ghttp.UploadFile) (fileInfos []*FileInfo, err error) {
return UpBathByType(files, "img")
}
//批量上传文件
func UpFiles(files []*ghttp.UploadFile) (fileInfos []*FileInfo, err error) {
return UpBathByType(files, "file")
}
//文件上传 img|file
func upByType(file *ghttp.UploadFile, fType string) (fileInfo *FileInfo, err error) {
if file == nil {
err = gerror.New("未上传任何文件")
return
}
var (
typeKey string
sizeKey string
)
if fType == "img" {
typeKey = "sys.uploadFile.imageType"
sizeKey = "sys.uploadFile.imageSize"
} else if fType == "file" {
typeKey = "sys.uploadFile.fileType"
sizeKey = "sys.uploadFile.fileSize"
}
//获取上传类型配置
config, err := getUpConfig(typeKey)
if err != nil {
return
}
//检测文件类型
rightType := checkFileType(file.Filename, config.ConfigValue)
if !rightType {
err = gerror.New("上传文件类型错误,只能包含后缀为:" + config.ConfigValue + "的文件。")
return
}
//获取上传大小配置
config, err = getUpConfig(sizeKey)
if err != nil {
return
}
rightSize, err := checkSize(config.ConfigValue, file.Size)
if err != nil {
return
}
if !rightSize {
err = gerror.New("上传文件超过最大尺寸:" + config.ConfigValue)
return
}
//获取文件上传驱动
config, err = getUpConfig("sys.uploadFile.driver")
if err != nil {
return
}
fileUrl := ""
fileName := ""
switch {
case config.ConfigValue == driverOSS:
fileName, err = uploadToOss(file)
if err != nil {
return
}
fileUrl = getOssUrl(fileName)
case config.ConfigValue == driverLocal:
path := getUpPath()
fileName, err = file.Save(path, true)
if err != nil {
return
}
fileUrl = getUrl(path, fileName)
default:
err = gerror.New("未配置上传驱动,请在后台参数管理中新增配置Key=sys.uploadFile.driver")
return
}
fileInfo = &FileInfo{
FileName: file.Filename,
FileSize: file.Size,
FileUrl: fileUrl,
FileType: file.Header.Get("Content-type"),
}
return
}
//批量上传 img|file
func UpBathByType(files []*ghttp.UploadFile, fType string) (fileInfos []*FileInfo, err error) {
if len(files) == 0 {
err = gerror.New("未上传任何文件")
return
}
var (
typeKey string
sizeKey string
)
if fType == "img" {
typeKey = "sys.uploadFile.imageType"
sizeKey = "sys.uploadFile.imageSize"
} else if fType == "file" {
typeKey = "sys.uploadFile.fileType"
sizeKey = "sys.uploadFile.fileSize"
}
//获取上传类型配置
configType, err := getUpConfig(typeKey)
if err != nil {
return
}
//获取上传大小配置
configSize, err := getUpConfig(sizeKey)
if err != nil {
return
}
for _, file := range files {
//检测文件类型
rightType := checkFileType(file.Filename, configType.ConfigValue)
if !rightType {
err = gerror.New("上传文件类型错误,只能包含后缀为:" + configType.ConfigValue + "的文件。")
return
}
var rightSize bool
rightSize, err = checkSize(configSize.ConfigValue, file.Size)
if err != nil {
return
}
if !rightSize {
err = gerror.New("上传文件超过最大尺寸:" + configSize.ConfigValue)
return
}
}
path := getUpPath()
for _, file := range files {
var fileName string
fileName, err = file.Save(path, true)
if err != nil {
return
}
fileInfo := &FileInfo{
FileName: file.Filename,
FileSize: file.Size,
FileUrl: getUrl(path, fileName),
FileType: file.Header.Get("Content-type"),
}
fileInfos = append(fileInfos, fileInfo)
}
return
}
//检查文件大小是否合法
func checkSize(configSize string, fileSize int64) (bool, error) {
match, err := gregex.MatchString(`^([0-9]+)(?i:([a-z]*))$`, configSize)
if err != nil {
return false, err
}
if len(match) == 0 {
err = gerror.New("上传文件大小未设置,请在后台配置,格式为(30M,30k,30MB)")
return false, err
}
var cfSize int64
switch gstr.ToUpper(match[2]) {
case "MB", "M":
cfSize = gconv.Int64(match[1]) * 1024 * 1024
case "KB", "K":
cfSize = gconv.Int64(match[1]) * 1024
case "":
cfSize = gconv.Int64(match[1])
}
if cfSize == 0 {
err = gerror.New("上传文件大小未设置,请在后台配置,格式为(30M,30k,30MB),最大单位为MB")
return false, err
}
return cfSize >= fileSize, nil
}
//获取上传配置
func getUpConfig(key string) (config *sys_config.Entity, err error) {
config, err = params_service.GetConfigByKey(key)
if err != nil {
return
}
if config == nil {
err = gerror.New("上传文件类型未设置,请在后台配置")
return
}
return
}
//判断上传文件类型是否合法
func checkFileType(fileName, typeString string) bool {
suffix := gstr.SubStrRune(fileName, gstr.PosRRune(fileName, ".")+1, gstr.LenRune(fileName)-1)
imageType := gstr.Split(typeString, ",")
rightType := false
for _, v := range imageType {
if gstr.Equal(suffix, v) {
rightType = true
break
}
}
return rightType
}
func getUpPath() (upPath string) {
upPath = uploadPath + gtime.Date() + "/"
return
}
func getUrl(path, fileName string) string {
url := gstr.SubStr(path, gstr.Pos(path, upPath)+1) + fileName
return url
}
//--------------------------------OSS Upload-----------------------------------
const objPrefix = "admin/%s"
func getOssUrl(fileName string) string {
resUrl := g.Cfg().GetString("oss.resUrl")
objKey := fmt.Sprintf(objPrefix, fileName)
return resUrl + objKey
}
func uploadToOss(file *ghttp.UploadFile) (string, error) {
f, err := file.Open()
if err != nil {
g.Log().Error(err)
return "", err
}
accessID := g.Cfg().GetString("oss.accessID")
accessSecret := g.Cfg().GetString("oss.accessSecret")
endpoint := g.Cfg().GetString("oss.endpoint")
bucketName := g.Cfg().GetString("oss.bucketName")
client, err := oss.New(endpoint, accessID, accessSecret)
if err != nil {
g.Log().Error(err)
return "", err
}
//获取存储空间
bucket, err := client.Bucket(bucketName)
if err != nil {
g.Log().Error(err)
return "", err
}
//生成随机文件名
name := gfile.Basename(file.Filename)
name = strings.ToLower(strconv.FormatInt(gtime.TimestampNano(), 36) + grand.S(10))
name = name + gfile.Ext(file.Filename)
objKey := fmt.Sprintf(objPrefix, name)
//上传Byte数组
if err := bucket.PutObject(objKey, f); err != nil {
g.Log().Error(err)
return "", err
}
if err := bucket.SetObjectACL(objKey, oss.ACLPublicRead); err != nil {
g.Log().Error(err)
}
return name, nil
}
{
code: 1003,
message: '定向重置....',
data: {...}
}
我看没有这样的
比如运行时间,时长,有无异常,方便监控。
插入角色没有删除缓存么
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.