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 b9c4af7..46fdb22 100644 --- a/config/config.toml +++ b/config/config.toml @@ -88,3 +88,8 @@ SecretKey = "填写您的SecretKey" [upload.local] #本地上传配置 UpPath = "/pub_upload/" #上传路径 + + +# 插件管理 +[plugin] + serverUrl = "http://localhost:8199" #获取插件商城插件列表 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/packed/swagger.go b/packed/swagger.go index 0a52f8f..9bacd7e 100644 --- a/packed/swagger.go +++ b/packed/swagger.go @@ -3,7 +3,7 @@ package packed import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GCoCVQMZkACIgycDMXlienpqUX6UFovqzg/LzSElYHxbaB+4pvIs3m33QT2PdfsyO1vU/mxIrl72Yml7f8WtN9oF7Vctk3w5Nl3m9ZmcXRcPGafm7vUN1eZY1bUi4VOgUf4Lmj4vl2mrzzpWbTIEo92ofpNXcnRWicmWxif/vns9kx+7aKvsxZpcD3KEuZ5sGLdHR7ehRs1WVy5H3+Xaf1+mvviLv0N65idS1l1w7ICDZSedbW+OvXq9FKpgqmCWh2pbBvfxx+PsfheNufdxO87nt+NLq9+/W6xo9FMhUWs6Rs9+7NmMq1W8rjgOcOe/dji4+wBTu5P3EW61eTdQ0INRbOEw0sFkuddmJ3KIysTMlvwolyyVuXS60fEua7ppO+y1nz9ycnqO0vncuvzc97N+//7+/L27HsHZ33+0Lp9q+Gf7c/XyS7qucr02kbctOjdv4d3apuWFBTcNjDI3s/GxjYnSo19nmeeRNDpUk1JmTV6ilcvJXBenXo0Ktcx0klKzGLH3siVIdOsJRXOyYl+s9HzET+292yC6omvYaHNokmTFMzXb/NoTry2rkZi6pRlevsuzZ6dK7t9zx5vs8qp23cyLM66PK1166qq17a9HN21Tpd1fXTtftv+yQtpKN9U/usR+3PH+h/Xtx+ezPz/oPnhxweXln68oqGRdoOBUUe+u4b/9herTMfA5bNuMOz0ELLbmJj5dpEG2/bcQw7Opa4G0vcO9HSY8QoWHexYOb9ebJPZGpF+ptfZHe5XfD9rypz7tm73dkMbuR63r5eWOCm9lxZtF/XMrAiN0eQJnhDHxZyZusFS/tSDPNvv5rsWf5TpfbW0sznmVZC8UOFSuydHzm6xu2Pw5X+zTc+/dXGdNk+W6z1VFAw/5ex3x+cUM3tY7kQewRmLv2fe868t/zxz8876W6/91/Msvrq4fFbw62aZhs8vDx9xEC159ER+3WTrc/n3d/5mrv/vL+ma2eB3pr9TUVF7e27C2zdhb1P093Uw/V3/+1/d/Ayjo5kOR3sPneBew2Pcxp06g8Pg09E3Ojp1coasLw5wbXGa51L+uu/zk0Cfpburn+TZT1OfEi+4OPh7V3nN/b8/xX8mMgTPfhnYUXiv6dtysdnzawvK60r+vdtVfX7u5HU+tWKXG/YG29iIh8fZZHz6avn/Tvm2bX/vBb9P8F1m11X644DZs2V23ze8/j5v1273TUdrXfbN+qu5yPc0y2UzqaMxZ1jC9DZsOG/5XjO78lTfuuL5mw7Hfwx/w9Jm+EKDVaP0X5/04awatlUzFrAFpgUmND3J8TtR/G+b75vTH35cr9k66QoXS3xSc5u8ulj2VpNYyb+iOftMjl08eKR+xz+tGYLxQRw8604zS+Sk1SuFGx2ruFW289pPfQaG//8DvNk5ZMLCYo4xMzBcU2RggBUIDAy1aAUCO6JAAJcB7wP1E0G6kdUEeDMyiTAjChRkk0EFCgxsawSReIsXhFHYnQIBAgz/HQVZGLA4jJUNJM/EwMTQycDA4M8C4gECAAD//+GYqUjuBAAA"); err != nil { + if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GA4xzstmAEJiDBwMhSXJ6anpxbpQ2m9rOL8vNAQVgbG0uZDiW8iz+bddhPY91yzI7e/TeXHiuTuZRwBXy7IfxH4vUHp+BUzpZ03Y7eF8jIa3vqxfO3aLStzWRyDuW1DNLpuMEotXXn3S6rLqZpuJ+1LE4/Ir31jMY3pk8q5M0X7fpY9u+wVs3dZ5qy2HXGs3BZa2eZLHaVWOwVsMag6P3FPfBzH9dpOX2EJreyMjQFczYIbw2VCroXF65k2vO4JcJTZLf/7X+mZ1vL849lyX+W2V32/9vfarv8rjOf4CAQ2b76R2P/uBtPrRfMXGKb8Y5Lgl7/friS7UD5QZ1WNSGDg8ZAXoTEmjj0VS8xPasjKlEwXDJRPzPrRtb5T70r85jkv8o6sOc398U6yyrx3Np9nVR+Ut58jt+4r37U97fpV0dJ3/0f9u6XoFaG74WRwoPL7d/Pv3VX34GQv1+Xe/MvSwODBlY2WH1yKUj00ry5VLdiwq6XkeINqyDUx76WsuZySEQ/+vT3rtHDDHQ+Gwm8aNz9bGM/tqZ590Mv+erQrc1iH76+dlRvdtHg2Vz1cvGGJRelF0ft3Ul6vePr0bsFk8cq53Bu6boheXXS6sdbhR8z2/PfPt+3/eeb2Hfvcrcvf3zr4qrFlzuZydcYFMxK5WTaUSEjO6bSaqnSMVXnbzKu9yW8q1Mv33N///bv1wX/xkadzck6yiBp0/uXcqKA944T7/bLGq5Nvb52454cRF8++TPbfuUkrRNIZRM2NlPU3Gp2RWJf+TCxhKfetyIgvNkncL+pTl7ktd7gq5PFl+7sKfznXkF+73f99Sy51+ilqaFW6/kvf+9/7/6yy40mQNrblEjwXHfD/TOLnzfz/zvnfs/etf7+z/FLIyvOJbBvKN7U//3Sqm813r937+7/vXSu7UHXLOiJ4ZX+SsF3J2p0rz9vnsK1d/u/dxKxC+cnr0+u7Mq5uPFi21stM3LhxKrfOHe5z1WdmlZw76X9L8lqm3y29tZY3NF5tv2fcUXBjuUndrWV+hyRjqtJe7PibF+HwPuX9l6P995rLvr9/v0ud/WBz/u7jc1/633v+Z6+FaVe37P7jFgUvS4NOi2zJ8FN353u25ZHEzPh8nrzdLY2Np2Yl6O1pkpS8PIf7vJOe3MOlXjcsjyi/WfZThC+/+vr92y/kytkW/7q1xEnxvbZou6hr5gyR6dyHRZY8P+SQxZlzrt3oR/T55t5nCTbsi3/dOuKoW7qO37lwp92SI2e32N3ZcMbcUY5Nv22di9yl4OefDESmn3b2ujP1dPMfkb0n5Q4ZMMkeZTjqonwrcfknyxdJPVIuM50bOSdtspz0/Euu9O3p7z7mv6xfqPWvsfOBdk7gz82ZG0O6Lk9VcYhYkp/hM+/J7UkPHv5SmaEY78Whk7eK+UWO2n616yHPCp12x2T/kWdg+P8/wJudo1B/1aqHzAwMtxQZGGBlBQPDRbSygh1RVoCLh+rmQ4kg3chqArwZmUSYEWUNssmgsgYGtjWCSLwlD8Io7E6BAAGG/446LAxYHMbKBpJnYmBi6GRgYMhiAfEAAQAA//8tF7e5CQUAAA=="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } }