From 3b167f82b5ba4acb8f977dc2c80cfaf1865c76ef Mon Sep 17 00:00:00 2001 From: yxh Date: Fri, 24 Dec 2021 15:57:07 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=82=E9=85=8D=E6=8F=92=E4=BB=B6=E5=AE=89?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/system/api/plugins_manage.go | 142 +++++++ app/system/dao/internal/plugins_manage.go | 81 ++++ app/system/dao/plugins_manage.go | 146 +++++++ app/system/model/plugins_manage.go | 24 ++ app/system/router/plugins_manage.go | 37 ++ app/system/service/plugins_manage.go | 450 ++++++++++++++++++++++ config/config.toml | 9 +- library/response.go | 42 +- library/utils.go | 13 + template/vm/vue/list-vue.template | 4 +- template/vm/vue/tree-vue.template | 4 +- 11 files changed, 915 insertions(+), 37 deletions(-) create mode 100644 app/system/api/plugins_manage.go create mode 100644 app/system/dao/internal/plugins_manage.go create mode 100644 app/system/dao/plugins_manage.go create mode 100644 app/system/model/plugins_manage.go create mode 100644 app/system/router/plugins_manage.go create mode 100644 app/system/service/plugins_manage.go diff --git a/app/system/api/plugins_manage.go b/app/system/api/plugins_manage.go new file mode 100644 index 0000000..3be6970 --- /dev/null +++ b/app/system/api/plugins_manage.go @@ -0,0 +1,142 @@ +// ========================================================================== +// GFast自动生成控制器相关代码,只生成一次,按需修改,再次生成不会覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/api/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package api + +import ( + "gfast/app/system/dao" + "gfast/app/system/service" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gvalid" +) + +type pluginsManage struct { + SystemBase +} + +var PluginsManage = new(pluginsManage) + +// List 列表 +func (c *pluginsManage) List(r *ghttp.Request) { + var req *dao.PluginsManageSearchReq + //获取参数 + if err := r.Parse(&req); err != nil { + c.FailJsonExit(r, err.(gvalid.Error).FirstString()) + } + req.Ctx = r.GetCtx() + total, page, list, err := service.PluginsManage.GetList(req) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + result := g.Map{ + "currentPage": page, + "total": total, + "list": list, + } + c.SusJsonExit(r, result) +} + +// Get 获取 +func (c *pluginsManage) Get(r *ghttp.Request) { + id := r.GetInt64("id") + info, err := service.PluginsManage.GetInfoById(r.GetCtx(), id) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + c.SusJsonExit(r, info) +} + +// ChangeStatus 修改状态 +func (c *pluginsManage) ChangeStatus(r *ghttp.Request) { + var req *dao.PluginsManageStatusReq + //获取参数 + if err := r.Parse(&req); err != nil { + c.FailJsonExit(r, err.(gvalid.Error).FirstString()) + } + if err := service.PluginsManage.ChangeStatus(r.GetCtx(), req); err != nil { + c.FailJsonExit(r, err.Error()) + } else { + c.SusJsonExit(r, "状态设置成功") + } +} + +// Install 插件安装 +func (c *pluginsManage) Install(r *ghttp.Request) { + var req *dao.PluginsManageInstallReq + //获取参数 + if err := r.Parse(&req); err != nil { + c.FailJsonExit(r, err.(gvalid.Error).FirstString()) + } + if req.RToken == "" { + c.SusJsonExit(r, g.Map{ + "code": -401, + "msg": "请登录", + }) + } + err := service.PluginsManage.Install(r.GetCtx(), req) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + c.SusJsonExit(r, "安装成功") +} + +// Captcha 获取验证码 +func (c *pluginsManage) Captcha(r *ghttp.Request) { + idKeyC, base64stringC, err := service.PluginsManage.GetCaptcha(r.GetCtx()) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + c.SusJsonExit(r, g.Map{ + "base64stringC": base64stringC, + "idKeyC": idKeyC, + }) +} + +// LoginR 登录插件服务 +func (c *pluginsManage) LoginR(r *ghttp.Request) { + var req *dao.PluginRLoginFormReq + // 通过Parse方法解析获取参数 + err := r.Parse(&req) + if err != nil { + c.FailJsonExit(r, err.(gvalid.Error).FirstString()) + } + var userInfo g.Map + userInfo, err = service.PluginsManage.LoginR(r.GetCtx(), req) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + c.SusJsonExit(r, userInfo) +} + +// InstallOffLine 离线安装 +func (c *pluginsManage) InstallOffLine(r *ghttp.Request) { + upFile := r.GetUploadFile("file") + f, err := upFile.Open() + if err != nil { + c.FailJsonExit(r, err.Error()) + } + defer f.Close() + d := make([]byte, upFile.Size) + _, err = f.Read(d) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + fileName := upFile.Filename + fileName = gstr.SubStr(fileName, gstr.PosR(fileName, "=")+1, + gstr.PosR(fileName, ".")-gstr.PosR(fileName, "=")-1) + err = service.PluginsManage.PluginIsExists(r.GetCtx(), fileName) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + err = service.PluginsManage.InstallFile(r.GetCtx(), d, fileName) + if err != nil { + c.FailJsonExit(r, err.Error()) + } + c.SusJsonExit(r) +} diff --git a/app/system/dao/internal/plugins_manage.go b/app/system/dao/internal/plugins_manage.go new file mode 100644 index 0000000..577fe97 --- /dev/null +++ b/app/system/dao/internal/plugins_manage.go @@ -0,0 +1,81 @@ +// ========================================================================== +// GFast自动生成dao internal操作代码,无需手动修改,重新生成会自动覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/dao/internal/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package internal + +import ( + "context" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" +) + +// PluginsManageDao is the manager for logic model data accessing and custom defined data operations functions management. +type PluginsManageDao struct { + Table string // Table is the underlying table name of the DAO. + Group string // Group is the database configuration group name of current DAO. + Columns PluginsManageColumns // Columns is the short type for Columns, which contains all the column names of Table for convenient usage. +} + +// PluginsManageColumns defines and stores column names for table plugins_manage. +type PluginsManageColumns struct { + Id string // ID + StoreId string // 插件在商城中的id + PName string // 插件名称英文 + PTitle string // 插件名称 + PDescription string // 插件介绍 + PAuth string // 作者 + IsInstall string // 是否安装 + Status string // 状态 + Version string // 当前版本 + Price string // 价格 + DownloadTimes string // 下载次数 + InstallPath string // 安装路径 +} + +var pluginsManageColumns = PluginsManageColumns{ + Id: "id", + StoreId: "store_id", + PName: "p_name", + PTitle: "p_title", + PDescription: "p_description", + PAuth: "p_auth", + IsInstall: "is_install", + Status: "status", + Version: "version", + Price: "price", + DownloadTimes: "download_times", + InstallPath: "install_path", +} + +// NewPluginsManageDao creates and returns a new DAO object for table data access. +func NewPluginsManageDao() *PluginsManageDao { + return &PluginsManageDao{ + Group: "default", + Table: "plugins_manage", + Columns: pluginsManageColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *PluginsManageDao) DB() gdb.DB { + return g.DB(dao.Group) +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *PluginsManageDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.Table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *PluginsManageDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/app/system/dao/plugins_manage.go b/app/system/dao/plugins_manage.go new file mode 100644 index 0000000..389fe3c --- /dev/null +++ b/app/system/dao/plugins_manage.go @@ -0,0 +1,146 @@ +// ========================================================================== +// GFast自动生成dao操作代码,无需手动修改,重新生成不会自动覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/dao/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package dao + +import ( + comModel "gfast/app/common/model" + "gfast/app/system/dao/internal" + "github.com/gogf/gf/os/gtime" +) + +// pluginsManageDao is the manager for logic model data accessing and custom defined data operations functions management. +// You can define custom methods on it to extend its functionality as you wish. +type pluginsManageDao struct { + *internal.PluginsManageDao +} + +var ( + // PluginsManage is globally public accessible object for table tools_gen_table operations. + PluginsManage = pluginsManageDao{ + internal.NewPluginsManageDao(), + } +) + +// Fill with you ideas below. + +// PluginsManageSearchReq 分页请求参数 +type PluginsManageSearchReq struct { + PName string `p:"pName"` //插件名称英文 + PTitle string `p:"pTitle"` //插件名称 + PAuth string `p:"pAuth"` //作者 + Status string `p:"status"` //状态 + comModel.PageReq +} + +// PluginsManageAddReq 添加操作请求参数 +type PluginsManageAddReq struct { + StoreId int `p:"storeId" ` + PName string `p:"pName" v:"required#插件名称英文不能为空"` + PTitle string `p:"pTitle" ` + PDescription string `p:"pDescription" ` + PAuth string `p:"pAuth" ` + Status int `p:"status" v:"required#状态不能为空"` + Version string `p:"version" ` + Price uint `p:"price" v:"required#价格不能为空"` + DownloadTimes uint `p:"downloadTimes" v:"required#下载次数不能为空"` + IsInstall int `p:"isInstall" v:"required#是否安装不能为空"` +} + +// PluginsManageEditReq 修改操作请求参数 +type PluginsManageEditReq struct { + Id uint `p:"id" v:"required#主键ID不能为空"` + StoreId int `p:"storeId" ` + PName string `p:"pName" v:"required#插件名称英文不能为空"` + PTitle string `p:"pTitle" ` + PDescription string `p:"pDescription" ` + PAuth string `p:"pAuth" ` + Status int `p:"status" v:"required#状态不能为空"` + Version string `p:"version" ` + Price uint `p:"price" v:"required#价格不能为空"` + DownloadTimes uint `p:"downloadTimes" v:"required#下载次数不能为空"` + IsInstall int `p:"isInstall" v:"required#是否安装不能为空"` +} + +// PluginsManageStatusReq 设置状态参数 +type PluginsManageStatusReq struct { + PluginId uint `p:"pluginId" v:"required#pluginId不能为空"` + Status int `p:"status" v:"required#状态不能为空"` +} + +// PluginsManageListRes 列表返回结果 +type PluginsManageListRes struct { + Id uint `json:"id" ` + PName string `json:"pName" v:"required#插件名称英文不能为空"` + PTitle string `json:"pTitle" ` + PDescription string `json:"pDescription" ` + PAuth string `json:"pAuth" ` + Status int `json:"status" v:"required#状态不能为空"` + Version string `json:"version" ` + Price uint `json:"price" v:"required#价格不能为空"` + DownloadTimes uint `json:"downloadTimes" v:"required#下载次数不能为空"` +} + +// PluginsManageInfoRes 数据返回结果 +type PluginsManageInfoRes struct { + Id uint `json:"id" ` + StoreId int `json:"storeId" ` + PName string `json:"pName" v:"required#插件名称英文不能为空"` + PTitle string `json:"pTitle" ` + PDescription string `json:"pDescription" ` + PAuth string `json:"pAuth" ` + IsInstall int `json:"isInstall" v:"required#是否安装不能为空"` + Status int `json:"status" v:"required#状态不能为空"` + Version string `json:"version" ` + Price uint `json:"price" v:"required#价格不能为空"` + DownloadTimes uint `json:"downloadTimes" v:"required#下载次数不能为空"` +} + +// CsPluginListRes 插件商城获取的插件数据 +type CsPluginListRes struct { + PluginId uint `orm:"plugin_id,primary" json:"pluginId"` // ID + PluginCateId uint `orm:"plugin_cate_id" json:"pluginCateId"` // 分类ID + PluginName string `orm:"plugin_name" json:"pluginName"` // 插件名称 + CodeName string `orm:"code_name" json:"CodeName"` // 代码名称 + PluginPrice uint `orm:"plugin_price" json:"pluginPrice"` // 售价 + PluginPriceStr string `json:"pluginPriceStr"` // 售价decimal + PluginDiscount uint `orm:"plugin_discount" json:"pluginDiscount"` // 折扣 + PluginCreater uint `orm:"plugin_creater" json:"pluginCreater"` // 开发人员ID + PublishDate *gtime.Time `orm:"publish_date" json:"publishDate"` // 发布日期 + PluginThumb string `orm:"plugin_thumb" json:"pluginThumb"` // 插件封面 + PluginImgs string `orm:"plugin_imgs" json:"pluginImgs"` // 插件预览图 + CreatedBy uint64 `orm:"created_by" json:"createdBy"` // 创建人 + CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建日期 + UpdatedAt *gtime.Time `orm:"updated_at" json:"updatedAt"` // 修改日期 + DeletedAt *gtime.Time `orm:"deleted_at" json:"deletedAt"` // 删除日期 + DownloadTimes uint64 `orm:"download_times" json:"downloadTimes"` //下载次数 + Description string `orm:"description" json:"description"` //插件描述 + PluginInfo []*struct { + InfoId uint `json:"infoId"` // ID + PluginId uint `json:"pluginId"` // 插件ID + InfoVersion string `json:"infoVersion"` // 版本号 + } `json:"pluginInfo"` + MemName string `json:"memName" orm:"mem_name"` + Status int `json:"status"` + Version string `json:"version" ` + IsInstall int `json:"isInstall"` +} + +// PluginsManageInstallReq 插件安装操作参数 +type PluginsManageInstallReq struct { + PluginId uint `p:"pluginId" v:"required#插件ID不能为空"` + Version string `p:"version"` + RToken string `p:"rToken"` +} + +// PluginRLoginFormReq 远端登录请求参数 +type PluginRLoginFormReq struct { + Username string `p:"username" v:"required#账号必须" json:"username"` + Password string `p:"password" v:"required#密码不能为空" json:"password"` + VerifyCode string `p:"verifyCode" v:"required#验证码不能为空" json:"verifyCode"` + VerifyKey string `p:"verifyKey" json:"verifyKey"` +} diff --git a/app/system/model/plugins_manage.go b/app/system/model/plugins_manage.go new file mode 100644 index 0000000..84998af --- /dev/null +++ b/app/system/model/plugins_manage.go @@ -0,0 +1,24 @@ +// ========================================================================== +// GFast自动生成model代码,无需手动修改,重新生成会自动覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/model/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package model + +// PluginsManage is the golang structure for table plugins_manage. +type PluginsManage struct { + Id uint `orm:"id,primary" json:"id"` // ID + StoreId int `orm:"store_id" json:"storeId"` // 插件在商城中的id + PName string `orm:"p_name" json:"pName"` // 插件名称英文 + PTitle string `orm:"p_title" json:"pTitle"` // 插件名称 + PDescription string `orm:"p_description" json:"pDescription"` // 插件介绍 + PAuth string `orm:"p_auth" json:"pAuth"` // 作者 + IsInstall int `orm:"is_install" json:"isInstall"` // 是否安装 + Status int `orm:"status" json:"status"` // 状态 + Version string `orm:"version" json:"version"` // 当前版本 + Price uint `orm:"price" json:"price"` // 价格 + DownloadTimes uint `orm:"download_times" json:"downloadTimes"` // 下载次数 + InstallPath string `orm:"install_oath" json:"installPath"` // 安装路径 +} diff --git a/app/system/router/plugins_manage.go b/app/system/router/plugins_manage.go new file mode 100644 index 0000000..a370b52 --- /dev/null +++ b/app/system/router/plugins_manage.go @@ -0,0 +1,37 @@ +// ========================================================================== +// GFast自动生成路由代码,只生成一次,按需修改,再次生成不会覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/router/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package router + +import ( + "gfast/app/system/api" + "gfast/middleware" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +//加载路由 +func init() { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Group("/system", func(group *ghttp.RouterGroup) { + group.Group("/pluginsManage", func(group *ghttp.RouterGroup) { + //gToken拦截器 + api.GfToken.AuthMiddleware(group) + //context拦截器 + group.Middleware(middleware.Ctx, middleware.Auth) + group.GET("list", api.PluginsManage.List) + group.GET("get", api.PluginsManage.Get) + group.PUT("changeStatus", api.PluginsManage.ChangeStatus) + group.POST("install", api.PluginsManage.Install) + group.GET("captcha", api.PluginsManage.Captcha) + group.POST("loginR", api.PluginsManage.LoginR) + group.POST("installOffLine", api.PluginsManage.InstallOffLine) + }) + }) + }) +} diff --git a/app/system/service/plugins_manage.go b/app/system/service/plugins_manage.go new file mode 100644 index 0000000..efaad7d --- /dev/null +++ b/app/system/service/plugins_manage.go @@ -0,0 +1,450 @@ +// ========================================================================== +// GFast自动生成业务逻辑层相关代码,只生成一次,按需修改,再次生成不会覆盖. +// 生成日期:2021-08-31 17:58:43 +// 生成路径: gfast/app/system/service/plugins_manage.go +// 生成人:gfast +// ========================================================================== + +package service + +import ( + "context" + "encoding/json" + "fmt" + "gfast/app/common/global" + "gfast/app/system/dao" + "gfast/app/system/model" + "gfast/library" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/encoding/gcompress" + "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/encoding/gurl" + "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/text/gstr" + "github.com/gogf/gf/util/gconv" +) + +type pluginsManage struct { +} + +var PluginsManage = new(pluginsManage) + +// GetList 获取列表 +func (s *pluginsManage) GetList(req *dao.PluginsManageSearchReq) (total, page int, list []*dao.CsPluginListRes, err error) { + //同步服务端插件商城 + total, page, list, err = s.syncFromStore(req) + return +} + +// GetInfoById 通过id获取 +func (s *pluginsManage) GetInfoById(ctx context.Context, id int64) (info *dao.PluginsManageInfoRes, err error) { + if id == 0 { + err = gerror.New("参数错误") + return + } + var data *model.PluginsManage + err = dao.PluginsManage.Ctx(ctx).Where(dao.PluginsManage.Columns.Id, id).Scan(&data) + if err != nil { + g.Log().Error(err) + } + if data == nil || err != nil { + err = gerror.New("获取信息失败") + } + info = &dao.PluginsManageInfoRes{ + Id: data.Id, + StoreId: data.StoreId, + PName: data.PName, + PTitle: data.PTitle, + PDescription: data.PDescription, + PAuth: data.PAuth, + IsInstall: data.IsInstall, + Status: data.Status, + Version: data.Version, + Price: data.Price, + DownloadTimes: data.DownloadTimes, + } + return +} + +// Add 添加 +func (s *pluginsManage) Add(ctx context.Context, req *dao.PluginsManageAddReq) (err error) { + _, err = dao.PluginsManage.Ctx(ctx).Insert(req) + return +} + +// Edit 修改 +func (s *pluginsManage) Edit(ctx context.Context, req *dao.PluginsManageEditReq) error { + _, err := dao.PluginsManage.Ctx(ctx).FieldsEx(dao.PluginsManage.Columns.Id).Where(dao.PluginsManage.Columns.Id, req.Id). + Update(req) + return err +} + +// DeleteByIds 删除 +func (s *pluginsManage) DeleteByIds(ctx context.Context, ids []int) (err error) { + if len(ids) == 0 { + err = gerror.New("参数错误") + return + } + _, err = dao.PluginsManage.Ctx(ctx).Delete(dao.PluginsManage.Columns.Id+" in (?)", ids) + if err != nil { + g.Log().Error(err) + err = gerror.New("删除失败") + } + return +} + +// ChangeStatus 修改状态 +func (s *pluginsManage) ChangeStatus(ctx context.Context, req *dao.PluginsManageStatusReq) error { + _, err := dao.PluginsManage.Ctx(ctx).Where(dao.PluginsManage.Columns.StoreId, req.PluginId).Update(g.Map{ + dao.PluginsManage.Columns.Status: req.Status, + }) + return err +} + +//同步插件商城中的插件 +func (s *pluginsManage) syncFromStore(req *dao.PluginsManageSearchReq) (total, page int, csPluginList []*dao.CsPluginListRes, err error) { + storeUrl := g.Cfg().GetString("plugin.serverUrl") + "/codeStore/pluginList" + res := (*ghttp.ClientResponse)(nil) + if req.PageNum == 0 { + req.PageNum = 1 + } + page = req.PageNum + res, err = g.Client().Ctx(req.Ctx).Get(storeUrl, g.MapStrAny{ + "pageNum": req.PageNum, + "PageSize": req.PageSize, + "pluginName": req.PTitle, + }) + if err != nil { + g.Log().Error(err) + err = gerror.New("获取插件数据失败") + return + } + defer res.Close() + var data map[string]interface{} + b := res.ReadAll() + err = json.Unmarshal(b, &data) + if err != nil { + g.Log().Error(err) + err = gerror.New("获取插件数据失败") + return + } + if gconv.Int(data["code"]) == 0 { + err = gconv.Structs((data["data"].(g.Map))["list"], &csPluginList) + if err != nil { + return + } + csPluginList, err = s.updatePlugins(req.Ctx, csPluginList) + if err != nil { + return + } + total = gconv.Int((data["data"].(g.Map))["total"]) + } else { + err = gerror.New(data["msg"].(string)) + return + } + return +} + +// 更新插件数据 +func (s *pluginsManage) updatePlugins(ctx context.Context, csList []*dao.CsPluginListRes) (newList []*dao.CsPluginListRes, err error) { + ids := make([]uint, len(csList)) + for k, v := range csList { + ids[k] = v.PluginId + } + //查询插件信息 + var pluginList []*model.PluginsManage + err = dao.PluginsManage.Ctx(ctx).Where(dao.PluginsManage.Columns.StoreId+" in(?)", ids).Scan(&pluginList) + if err != nil { + return + } + hasIds := garray.NewArraySize(len(pluginList), 100) + gmp := gmap.New() + for k, v := range pluginList { + hasIds.Set(k, v.StoreId) + gmp.Set(v.StoreId, v) + } + for _, v := range csList { + pluginId := gconv.Int(v.PluginId) + if hasIds.Len() > 0 && hasIds.Contains(pluginId) { + plugin := gmp.Get(pluginId).(*model.PluginsManage) + //修改 + version := plugin.Version + if plugin.IsInstall == 0 && len(v.PluginInfo) > 0 { + version = v.PluginInfo[0].InfoVersion + } + err = s.Edit(ctx, &dao.PluginsManageEditReq{ + Id: plugin.Id, + StoreId: pluginId, + PName: v.CodeName, + PTitle: v.PluginName, + PDescription: v.Description, + PAuth: v.MemName, + Status: plugin.Status, + Version: version, + Price: v.PluginPrice, + DownloadTimes: gconv.Uint(v.DownloadTimes), + IsInstall: plugin.IsInstall, + }) + v.Status = plugin.Status + v.Version = version + v.IsInstall = plugin.IsInstall + v.PluginPriceStr = s.Int64ToDecimal(gconv.Int64(v.PluginPrice)) + } else { + //新增 + version := "" + if len(v.PluginInfo) > 0 { + version = v.PluginInfo[0].InfoVersion + } + err = s.Add(ctx, &dao.PluginsManageAddReq{ + StoreId: pluginId, + PName: v.CodeName, + PTitle: v.PluginName, + PDescription: v.Description, + PAuth: v.MemName, + Status: 0, + Version: version, + Price: v.PluginPrice, + DownloadTimes: gconv.Uint(v.DownloadTimes), + IsInstall: 0, + }) + v.Status = 0 + v.Version = version + v.IsInstall = 0 + v.PluginPriceStr = s.Int64ToDecimal(gconv.Int64(v.PluginPrice)) + } + if err != nil { + return + } + } + newList = csList + return +} + +// DecimalToInt64 元转分 +func (s *pluginsManage) DecimalToInt64(decimal string) (i int64) { + pos := gstr.PosR(decimal, ".") + integer := gconv.Int64(gstr.SubStr(decimal, 0, pos)) * 100 + dec := int64(0) + if pos > -1 { + dec = gconv.Int64(gstr.SubStr(decimal, pos+1, 2)) + } + i = integer + dec + return +} + +// Int64ToDecimal 分转元 +func (s *pluginsManage) Int64ToDecimal(i int64) (decimal string) { + b := []byte(gconv.String(i)) + for { + if len(b) >= 2 { + break + } + b = append([]byte{'0'}, b...) + } + integer := b[:len(b)-2] + if len(integer) == 0 { + integer = []byte{'0'} + } + dec := b[len(b)-2:] + decimal = fmt.Sprintf("%s.%s", integer, dec) + return +} + +// Install 插件安装 +func (s *pluginsManage) Install(ctx context.Context, req *dao.PluginsManageInstallReq) (err error) { + //生成下载链接 + storeUrl := g.Cfg().GetString("plugin.serverUrl") + "/codeStoreFrontAdmin/getDownloadInfo" + res := (*ghttp.ClientResponse)(nil) + res, err = g.Client().Ctx(ctx).Get(fmt.Sprintf("%s?pluginId=%d&version=%s&token=%s", storeUrl, req.PluginId, req.Version, + gurl.RawEncode(req.RToken))) + if err != nil { + return + } + defer res.Close() + var data map[string]interface{} + b := res.ReadAll() + err = json.Unmarshal(b, &data) + if err != nil { + return + } + if gconv.Int(data["code"]) == 0 { + url := fmt.Sprintf("%s/%s&token=%s", g.Cfg().GetString("plugin.serverUrl"), + (data["data"]).(string), gurl.RawEncode(req.RToken)) + //下载插件并安装 + err = s.downloadAndInstall(ctx, url) + } else { + err = gerror.New(data["msg"].(string)) + } + return +} + +// GetCaptcha 获取验证码 +func (s *pluginsManage) GetCaptcha(ctx context.Context) (idKeyC, base64stringC string, err error) { + storeUrl := g.Cfg().GetString("plugin.serverUrl") + "/captcha/get" + res := (*ghttp.ClientResponse)(nil) + res, err = g.Client().Ctx(ctx).Get(storeUrl) + if err != nil { + g.Log().Error(err) + err = gerror.New("获取验证码失败") + return + } + defer res.Close() + var data map[string]interface{} + b := res.ReadAll() + err = json.Unmarshal(b, &data) + if err != nil { + g.Log().Error(err) + err = gerror.New("获取插件数据失败") + return + } + if gconv.Int(data["code"]) == 0 { + data = (data["data"]).(g.Map) + idKeyC = gconv.String(data["idKeyC"]) + base64stringC = gconv.String(data["base64stringC"]) + } else { + err = gerror.New(data["msg"].(string)) + } + return +} + +// LoginR 登录 +func (s *pluginsManage) LoginR(ctx context.Context, loginReq *dao.PluginRLoginFormReq) (userInfo g.Map, err error) { + storeUrl := g.Cfg().GetString("plugin.serverUrl") + "/codeStoreFrontAdmin/login" + res := (*ghttp.ClientResponse)(nil) + res, err = g.Client().Ctx(ctx).Post(storeUrl, loginReq) + if err != nil { + g.Log().Error(err) + err = gerror.New("获取验证码失败") + return + } + defer res.Close() + var data map[string]interface{} + b := res.ReadAll() + err = json.Unmarshal(b, &data) + if err != nil { + g.Log().Error(err) + err = gerror.New("登录失败") + return + } + if gconv.Int(data["code"]) == 0 { + userInfo = (data["data"]).(g.Map) + } else { + err = gerror.New(data["msg"].(string)) + } + return +} + +// 下载并安装插件 +func (s *pluginsManage) downloadAndInstall(ctx context.Context, url string) error { + res, err := g.Client().Ctx(ctx).Get(url) + if err != nil { + return err + } + defer res.Close() + ct := res.Header.Get("Content-Type") + if gstr.ContainsI(ct, "json") { + var data map[string]interface{} + b := res.ReadAll() + err = json.Unmarshal(b, &data) + if err != nil { + return err + } + if gconv.Int(data["code"]) != 0 { + err = gerror.New(data["msg"].(string)) + } + return err + } else { + //安装 + //获取插件名称 + fileName := res.Header.Get("content-disposition") + fileName = gstr.SubStr(fileName, gstr.PosR(fileName, "=")+1, + gstr.PosR(fileName, ".")-gstr.PosR(fileName, "=")-1) + err = s.InstallFile(ctx, res.ReadAll(), fileName) + if err != nil { + return err + } + } + return nil +} + +// InstallFile 安装插件文件 +func (s *pluginsManage) InstallFile(ctx context.Context, data []byte, fileName string) (err error) { + //获取插件下载路径 + downloadPath := library.GetExcPath() + "/data/installPlugins" + if !gfile.IsDir(downloadPath) { + err = gfile.Mkdir(downloadPath) + if err != nil { + return + } + } + g.Log().Debug(downloadPath, fileName) + // 删除安装临时文件 + defer gfile.Remove(downloadPath + "/" + fileName) + err = gcompress.UnZipContent(data, downloadPath) + if err != nil { + return + } + //获取插件配置信息 + var installCfg *gjson.Json + installCfg, err = gjson.Load(downloadPath + "/" + fileName + "/install.json") + if err != nil { + return + } + mustGfastVersion := installCfg.GetString("minGfastVersion") + if gstr.CompareVersion(mustGfastVersion, global.Version) > 0 { + err = gerror.New(fmt.Sprintf("您的gfast版本过低,此插件要求gfast版本为:%s", mustGfastVersion)) + return + } + //获取本项目安装情况 + var plugin *model.PluginsManage + err = dao.PluginsManage.Ctx(ctx).Where(dao.PluginsManage.Columns.PName, fileName).Limit(1).Scan(&plugin) + if err != nil { + return + } + if plugin == nil { + err = gerror.New("插件信息不存在,请刷新页面后再安装。") + return + } + //复制插件文件到对应目录 + //1.后端 + err = gfile.Copy(downloadPath+"/"+fileName+"/go/", library.GetExcPath()) + if err != nil { + return + } + //2.前端 + fontRoot := g.Cfg().GetString("gen.frontDir") + if !gfile.IsDir(fontRoot) { + err = gerror.New("前端路径不存在,请配置gen.frontDir") + return + } + err = gfile.Copy(downloadPath+"/"+fileName+"/vue/", fontRoot+"/src") + if err != nil { + return + } + // 安装成功后修改插件安装状态及安装路径 + _, err = dao.PluginsManage.Ctx(ctx).WherePri(plugin.Id).Update(g.Map{ + dao.PluginsManage.Columns.IsInstall: 1, + dao.PluginsManage.Columns.Status: 1, + dao.PluginsManage.Columns.InstallPath: installCfg.GetString("installPath"), + dao.PluginsManage.Columns.Version: installCfg.GetString("version"), + }) + return +} + +// PluginIsExists 判断插件是否存在 +func (s *pluginsManage) PluginIsExists(ctx context.Context, name string) error { + info := (*model.PluginsManage)(nil) + err := dao.PluginsManage.Ctx(ctx).Where(dao.PluginsManage.Columns.PName, name).Limit(1). + Fields(dao.PluginsManage.Columns.Id).Scan(&info) + if err != nil { + return err + } + if info == nil { + return gerror.New("不属于官方插件,无法安装。") + } + return nil +} diff --git a/config/config.toml b/config/config.toml index 48e74a5..5a10d1d 100644 --- a/config/config.toml +++ b/config/config.toml @@ -46,7 +46,7 @@ # Redis数据库配置 [redis] open = true #是否开启 redis 缓存 若不开启使用gchache缓存方式 - default = "127.0.0.1:6379,9?idleTimeout=20&maxActive=100" + default = "127.0.0.1:6379,2?idleTimeout=20&maxActive=100" #jwt配置 [gToken] @@ -87,4 +87,9 @@ SecretID = "填写您的SecretID" SecretKey = "填写您的SecretKey" [upload.local] #本地上传配置 - UpPath = "/pub_upload/" #上传路径 \ No newline at end of file + UpPath = "/pub_upload/" #上传路径 + + +# 插件管理 +[plugin] + serverUrl = "http://localhost:8199" #获取插件商城插件列表 \ No newline at end of file diff --git a/library/response.go b/library/response.go index 160a8af..3535bd1 100644 --- a/library/response.go +++ b/library/response.go @@ -1,11 +1,10 @@ package library import ( - "github.com/gogf/gf/frame/g" + "fmt" + "github.com/gogf/gf/encoding/gurl" "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/os/gview" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/util/gconv" ) const ( @@ -87,35 +86,16 @@ func (res *Response) FailJson(isExit bool, r *ghttp.Request, msg string, data .. //模板输出 func (res *Response) WriteTpl(r *ghttp.Request, tpl string, view *gview.View, params ...gview.Params) error { - //绑定模板中需要用到的方法 - view.BindFuncMap(g.Map{ - // 根据长度i来切割字符串 - "subStr": func(str interface{}, i int) (s string) { - s1 := gconv.String(str) - if gstr.LenRune(s1) > i { - s = gstr.SubStrRune(s1, 0, i) + "..." - return s - } - return s1 - }, - // 格式化时间戳 年月日 - "timeFormatYear": func(time interface{}) string { - return TimeStampToDate(gconv.Int64(time)) - }, - // 格式化时间戳 年月日时分秒 - "timeFormatDateTime": func(time interface{}) string { - return TimeStampToDateTime(gconv.Int64(time)) - }, - "add": func(a, b interface{}) int { - return gconv.Int(a) + gconv.Int(b) - }, - }) //设置全局变量 - domain, _ := GetDomain(r) - view.Assigns(g.Map{ - "domain": domain, - }) - return r.Response.WriteTpl(tpl, params...) + urlInfo, _ := gurl.ParseURL(r.GetUrl(), -1) + view.Assign("urlInfo", urlInfo) + r.SetView(view) + err := r.Response.WriteTpl(tpl, params...) + if err != nil { + fmt.Println(err.Error()) + r.Response.WriteExit(err.Error()) + } + return nil } func (res *Response) Redirect(r *ghttp.Request, location string, code ...int) { diff --git a/library/utils.go b/library/utils.go index 4f6f87a..ea6cbc8 100644 --- a/library/utils.go +++ b/library/utils.go @@ -14,6 +14,9 @@ import ( "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "net" + "os" + "os/exec" + "path/filepath" "strings" ) @@ -247,3 +250,13 @@ func CurrencyLong(currency interface{}) int64 { } return 0 } + +func GetExcPath() string { + file, _ := exec.LookPath(os.Args[0]) + // 获取包含可执行文件名称的路径 + path, _ := filepath.Abs(file) + // 获取可执行文件所在目录 + index := strings.LastIndex(path, string(os.PathSeparator)) + ret := path[:index] + return strings.Replace(ret, "\\", "/", -1) +} diff --git a/template/vm/vue/list-vue.template b/template/vm/vue/list-vue.template index 4ccc0df..8c58b52 100644 --- a/template/vm/vue/list-vue.template +++ b/template/vm/vue/list-vue.template @@ -277,11 +277,11 @@ {{else if eq $column.HtmlType "file"}} - + {{else if eq $column.HtmlType "files"}} - + {{end}} {{end}} diff --git a/template/vm/vue/tree-vue.template b/template/vm/vue/tree-vue.template index ade41ac..435bc78 100644 --- a/template/vm/vue/tree-vue.template +++ b/template/vm/vue/tree-vue.template @@ -281,11 +281,11 @@ {{else if eq $column.HtmlType "file"}} - + {{else if eq $column.HtmlType "files"}} - + {{end}} {{end}}