🎨 🔥 🚑 集成前端代码的&兼容容器化部署插件的插件改造&提供了一个新的轮播图插件

This commit is contained in:
osi 2025-09-19 20:44:33 +08:00
parent edb673ee34
commit f254132e18
61 changed files with 2569 additions and 16 deletions

View File

@ -0,0 +1,45 @@
## 轮播图管理
### 简介
### 使用说明
### 迁移或安装
1安装 HotGo (2.17.8及以上)
项目介绍https://github.com/bufanyun/hotgo
2将当前插件项目拷贝进 HotGo 根目录的 server/addons 目录下
3 HotGo 根目录的 server/addons/modules 目录下创建go文件:flashbanner.go内容如下
```go
package modules
import _ "hotgo/addons/flashbanner"
```
4HotGo 后台进入 开发工具->插件管理->找到 轮播图管理 (flashbanner) 进行安装
5重启服务即可生效
### 常用命令行
```shell
# 接口维护-gen service
gf gen service -s=addons/flashbanner/logic -d=addons/flashbanner/service
```
### `gf gen dao` 之后迁移model文件
```
cp .\internal\dao\internal\banner* .\addons\flashbanner\dao\internal\
cp .\internal\model\entity\banner* .\addons\flashbanner\model\entity\
```

View File

@ -0,0 +1,65 @@
// Package banner
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package banner
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
"hotgo/internal/model/input/form"
)
// CreateReq 创建轮播图
type CreateReq struct {
g.Meta `path:"/banner/create" method:"post" tags:"轮播图" summary:"创建轮播图"`
sysin.BannerCreateInp
}
type CreateRes struct{}
// ListReq 查询列表
type ListReq struct {
g.Meta `path:"/banner/list" method:"get" tags:"轮播图" summary:"获取轮播图列表"`
sysin.BannerListInp
}
type ListRes struct {
List []*sysin.BannerListModel `json:"list" dc:"数据列表"`
form.PageRes
}
// ViewReq 获取指定信息
type ViewReq struct {
g.Meta `path:"/banner/view" method:"get" tags:"轮播图" summary:"获取指定轮播图信息"`
sysin.BannerViewInp
}
type ViewRes struct {
*sysin.BannerViewModel
}
// EditReq 修改/新增轮播图
type EditReq struct {
g.Meta `path:"/banner/edit" method:"post" tags:"轮播图" summary:"修改/新增轮播图"`
sysin.BannerEditInp
}
type EditRes struct{}
// DeleteReq 删除轮播图
type DeleteReq struct {
g.Meta `path:"/banner/delete" method:"post" tags:"轮播图" summary:"删除轮播图"`
sysin.BannerDeleteInp
}
type DeleteRes struct{}
// StatusReq 更新轮播图状态
type StatusReq struct {
g.Meta `path:"/banner/status" method:"post" tags:"轮播图" summary:"更新轮播图状态"`
sysin.BannerStatusInp
}
type StatusRes struct{}

View File

@ -0,0 +1,30 @@
// Package config
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package config
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
)
// GetReq 获取指定分组的配置
type GetReq struct {
g.Meta `path:"/config/get" method:"get" tags:"轮播图管理" summary:"获取指定分组的配置"`
sysin.GetConfigInp
}
type GetRes struct {
*sysin.GetConfigModel
}
// UpdateReq 获取指定分组的配置
type UpdateReq struct {
g.Meta `path:"/config/update" method:"post" tags:"轮播图管理" summary:"获取指定分组的配置"`
sysin.UpdateConfigInp
}
type UpdateRes struct {
}

View File

@ -0,0 +1,33 @@
// Package banner
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package banner
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
"hotgo/internal/model/input/form"
)
// ListReq 查询列表
type ListReq struct {
g.Meta `path:"/banner/list" method:"get" tags:"轮播图" summary:"获取轮播图列表"`
sysin.BannerListInp
}
type ListRes struct {
List []*sysin.BannerListModel `json:"list" dc:"数据列表"`
form.PageRes
}
// ViewReq 获取指定信息
type ViewReq struct {
g.Meta `path:"/banner/view" method:"get" tags:"轮播图" summary:"获取指定轮播图信息"`
sysin.BannerViewInp
}
type ViewRes struct {
*sysin.BannerViewModel
}

View File

@ -0,0 +1,23 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"轮播图管理" summary:"测试后台API"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}

View File

@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"轮播图管理" summary:"测试前台API"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}

View File

@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" summary:"轮播图管理" tags:"测试首页"`
sysin.IndexTestInp
}
type TestRes struct {
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
}

View File

@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"轮播图管理" summary:"测试websocket"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}

View File

@ -0,0 +1,9 @@
// Package consts
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
// 常量枚举.
// 插件中的常量枚举可以统一在这目录下

View File

@ -0,0 +1,81 @@
// Package banner
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package banner
import (
"context"
"hotgo/addons/flashbanner/api/admin/banner"
"hotgo/addons/flashbanner/service"
)
var (
Banner = cBanner{}
)
type cBanner struct{}
// Create 创建轮播图
func (c *cBanner) Create(ctx context.Context, req *banner.CreateReq) (res *banner.CreateRes, err error) {
err = service.SysBanner().Create(ctx, &req.BannerCreateInp)
if err != nil {
return nil, err
}
res = &banner.CreateRes{}
return res, nil
}
// List 获取轮播图列表
func (c *cBanner) List(ctx context.Context, req *banner.ListReq) (res *banner.ListRes, err error) {
list, totalCount, err := service.SysBanner().List(ctx, &req.BannerListInp)
if err != nil {
return nil, err
}
res = &banner.ListRes{
List: list,
}
res.PageRes.Pack(&req.BannerListInp, totalCount)
return res, nil
}
// View 获取指定轮播图信息
func (c *cBanner) View(ctx context.Context, req *banner.ViewReq) (res *banner.ViewRes, err error) {
model, err := service.SysBanner().View(ctx, &req.BannerViewInp)
if err != nil {
return nil, err
}
res = &banner.ViewRes{model}
return res, nil
}
// Edit 修改/新增轮播图
func (c *cBanner) Edit(ctx context.Context, req *banner.EditReq) (res *banner.EditRes, err error) {
err = service.SysBanner().Edit(ctx, &req.BannerEditInp)
if err != nil {
return nil, err
}
res = &banner.EditRes{}
return res, nil
}
// Delete 删除轮播图
func (c *cBanner) Delete(ctx context.Context, req *banner.DeleteReq) (res *banner.DeleteRes, err error) {
err = service.SysBanner().Delete(ctx, &req.BannerDeleteInp)
if err != nil {
return nil, err
}
res = &banner.DeleteRes{}
return res, nil
}
// Status 更新轮播图状态
func (c *cBanner) Status(ctx context.Context, req *banner.StatusReq) (res *banner.StatusRes, err error) {
err = service.SysBanner().Status(ctx, &req.BannerStatusInp)
if err != nil {
return nil, err
}
res = &banner.StatusRes{}
return res, nil
}

View File

@ -0,0 +1,14 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"hotgo/addons/flashbanner/controller/admin/banner"
)
var (
Banner = banner.Banner
)

View File

@ -0,0 +1,33 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"hotgo/addons/flashbanner/api/admin/config"
"hotgo/addons/flashbanner/service"
)
var (
Config = cConfig{}
)
type cConfig struct{}
// GetConfig 获取指定分组的配置
func (c *cConfig) GetConfig(ctx context.Context, req *config.GetReq) (res *config.GetRes, err error) {
data, err := service.SysConfig().GetConfigByGroup(ctx, &req.GetConfigInp)
res = new(config.GetRes)
res.GetConfigModel = data
return
}
// UpdateConfig 更新指定分组的配置
func (c *cConfig) UpdateConfig(ctx context.Context, req *config.UpdateReq) (res *config.UpdateRes, err error) {
err = service.SysConfig().UpdateConfigByGroup(ctx, &req.UpdateConfigInp)
return
}

View File

@ -0,0 +1,30 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"hotgo/addons/flashbanner/api/admin/index"
"hotgo/addons/flashbanner/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}

View File

@ -0,0 +1,41 @@
// Package banner
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package banner
import (
"context"
"hotgo/addons/flashbanner/api/admin/banner"
"hotgo/addons/flashbanner/service"
)
var (
BannerIndex = cBanner{}
)
type cBanner struct{}
// List 获取轮播图列表
func (c *cBanner) List(ctx context.Context, req *banner.ListReq) (res *banner.ListRes, err error) {
list, totalCount, err := service.SysBanner().List(ctx, &req.BannerListInp)
if err != nil {
return nil, err
}
res = &banner.ListRes{
List: list,
}
res.PageRes.Pack(&req.BannerListInp, totalCount)
return res, nil
}
// View 获取指定轮播图信息
func (c *cBanner) View(ctx context.Context, req *banner.ViewReq) (res *banner.ViewRes, err error) {
model, err := service.SysBanner().View(ctx, &req.BannerViewInp)
if err != nil {
return nil, err
}
res = &banner.ViewRes{model}
return res, nil
}

View File

@ -0,0 +1,31 @@
// Package api
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package api
import (
"context"
"hotgo/addons/flashbanner/api/api/index"
"hotgo/addons/flashbanner/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}

View File

@ -0,0 +1,34 @@
// Package home
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package home
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/flashbanner/api/home/index"
"hotgo/addons/flashbanner/service"
"hotgo/internal/model"
isc "hotgo/internal/service"
)
// Index 基础
var Index = cIndex{}
type cIndex struct{}
func (a *cIndex) Index(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
isc.View().RenderTpl(ctx, "home/index.html", model.View{Data: g.Map{
"name": data.Name,
"module": data.Module,
"time": data.Time,
}})
return
}

View File

@ -0,0 +1,30 @@
// Package websocket
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package websocket
import (
"context"
"hotgo/addons/flashbanner/api/websocket/index"
"hotgo/addons/flashbanner/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}

View File

@ -0,0 +1,9 @@
// Package crons
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package crons
// 定时任务.
// 插件中的定时任务可以统一在这里注册和处理

View File

@ -0,0 +1,12 @@
// Package dao
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dao
import "hotgo/addons/flashbanner/dao/internal"
var (
Banner = internal.NewBannerDao()
)

View File

@ -0,0 +1,89 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// BannerDao is the data access object for table hg_banner.
type BannerDao 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 BannerColumns // columns contains all the column names of Table for convenient usage.
}
// BannerColumns defines and stores column names for table hg_banner.
type BannerColumns struct {
Id string //
Name string // 轮播图名称
Cover string // 图片URL
Link string // 跳转链接,小程序内用相对地址
Type string // 类型默认不传
Status string // 1可用
Sort string // 排序,数字越大越靠前
CreatedAt string //
UpdatedAt string //
}
// bannerColumns holds the columns for table hg_banner.
var bannerColumns = BannerColumns{
Id: "id",
Name: "name",
Cover: "cover",
Link: "link",
Type: "type",
Status: "status",
Sort: "sort",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewBannerDao creates and returns a new DAO object for table data access.
func NewBannerDao() *BannerDao {
return &BannerDao{
group: "default",
table: "hg_banner",
columns: bannerColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *BannerDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *BannerDao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *BannerDao) Columns() BannerColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *BannerDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *BannerDao) 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 *BannerDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@ -0,0 +1,12 @@
// Package global
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package global
import "hotgo/internal/library/addons"
var (
skeleton *addons.Skeleton // 插件架子
)

View File

@ -0,0 +1,22 @@
// Package global
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package global
import (
"context"
"hotgo/internal/library/addons"
)
func Init(ctx context.Context, sk *addons.Skeleton) {
skeleton = sk
}
func GetSkeleton() *addons.Skeleton {
if skeleton == nil {
panic("addon skeleton not initialized.")
}
return skeleton
}

View File

View File

@ -0,0 +1,9 @@
// ==========================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package logic
import (
_ "hotgo/addons/flashbanner/logic/sys"
)

View File

@ -0,0 +1,173 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"hotgo/addons/flashbanner/dao"
"hotgo/addons/flashbanner/model/entity"
"hotgo/addons/flashbanner/model/input/sysin"
"hotgo/addons/flashbanner/service"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type sSysBanner struct{}
func NewSysBanner() *sSysBanner {
return &sSysBanner{}
}
func init() {
service.RegisterSysBanner(NewSysBanner())
}
// GetMaxSort 获取最大排序值
func (s *sSysBanner) GetMaxSort(ctx context.Context) (maxSort int, err error) {
var result struct {
MaxSort int `json:"maxSort"`
}
err = dao.Banner.Ctx(ctx).Fields("MAX(sort) as maxSort").Scan(&result)
if err != nil {
return 0, gerror.Wrap(err, "获取最大排序值失败")
}
return result.MaxSort, nil
}
// Create 创建轮播图
func (s *sSysBanner) Create(ctx context.Context, in *sysin.BannerCreateInp) (err error) {
// 如果没有设置排序值,自动设置为最大值+1
if in.Sort == 0 {
maxSort, err := s.GetMaxSort(ctx)
if err != nil {
return err
}
in.Sort = maxSort + 1
}
_, err = dao.Banner.Ctx(ctx).Data(g.Map{
dao.Banner.Columns().Name: in.Name,
dao.Banner.Columns().Cover: in.Cover,
dao.Banner.Columns().Link: in.Link,
dao.Banner.Columns().Type: in.Type,
dao.Banner.Columns().Status: 1, // 默认启用
dao.Banner.Columns().Sort: in.Sort,
dao.Banner.Columns().CreatedAt: gtime.Now(),
dao.Banner.Columns().UpdatedAt: gtime.Now(),
}).Insert()
if err != nil {
err = gerror.Wrap(err, "创建轮播图失败")
return
}
return
}
// Edit 修改/新增轮播图
func (s *sSysBanner) Edit(ctx context.Context, in *sysin.BannerEditInp) (err error) {
if in.Id > 0 {
// 修改
_, err = dao.Banner.Ctx(ctx).Where(dao.Banner.Columns().Id, in.Id).Data(g.Map{
dao.Banner.Columns().Name: in.Name,
dao.Banner.Columns().Cover: in.Cover,
dao.Banner.Columns().Link: in.Link,
dao.Banner.Columns().Type: in.Type,
dao.Banner.Columns().Status: in.Status,
dao.Banner.Columns().Sort: in.Sort,
dao.Banner.Columns().UpdatedAt: gtime.Now(),
}).Update()
if err != nil {
err = gerror.Wrap(err, "修改轮播图失败")
return
}
} else {
// 新增
_, err = dao.Banner.Ctx(ctx).Data(g.Map{
dao.Banner.Columns().Name: in.Name,
dao.Banner.Columns().Cover: in.Cover,
dao.Banner.Columns().Link: in.Link,
dao.Banner.Columns().Type: in.Type,
dao.Banner.Columns().Status: in.Status,
dao.Banner.Columns().Sort: in.Sort,
dao.Banner.Columns().CreatedAt: gtime.Now(),
dao.Banner.Columns().UpdatedAt: gtime.Now(),
}).Insert()
if err != nil {
err = gerror.Wrap(err, "新增轮播图失败")
return
}
}
return
}
// Delete 删除轮播图
func (s *sSysBanner) Delete(ctx context.Context, in *sysin.BannerDeleteInp) (err error) {
_, err = dao.Banner.Ctx(ctx).Where(dao.Banner.Columns().Id, in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, "删除轮播图失败")
return
}
return
}
// View 获取指定轮播图信息
func (s *sSysBanner) View(ctx context.Context, in *sysin.BannerViewInp) (res *sysin.BannerViewModel, err error) {
var banner entity.Banner
err = dao.Banner.Ctx(ctx).Where(dao.Banner.Columns().Id, in.Id).Scan(&banner)
if err != nil {
err = gerror.Wrap(err, "获取轮播图信息失败")
return
}
if banner.Id == 0 {
err = gerror.New("轮播图不存在")
return
}
res = &sysin.BannerViewModel{Banner: banner}
return
}
// List 获取轮播图列表
func (s *sSysBanner) List(ctx context.Context, in *sysin.BannerListInp) (list []*sysin.BannerListModel, totalCount int, err error) {
m := dao.Banner.Ctx(ctx)
// 条件查询
if in.Name != "" {
m = m.WhereLike(dao.Banner.Columns().Name, "%"+in.Name+"%")
}
if in.Type > 0 {
m = m.Where(dao.Banner.Columns().Type, in.Type)
}
var banners []*entity.Banner
if in.Page > 0 && in.PerPage > 0 {
err = m.Page(in.Page, in.PerPage).OrderDesc(dao.Banner.Columns().Sort).OrderDesc(dao.Banner.Columns().Id).ScanAndCount(&banners, &totalCount, false)
} else {
err = m.OrderDesc(dao.Banner.Columns().Sort).OrderDesc(dao.Banner.Columns().Id).ScanAndCount(&banners, &totalCount, false)
}
if err != nil {
err = gerror.Wrap(err, "获取轮播图列表失败")
return
}
list = make([]*sysin.BannerListModel, len(banners))
for i, banner := range banners {
list[i] = &sysin.BannerListModel{Banner: *banner}
}
return
}
// Status 更新轮播图状态
func (s *sSysBanner) Status(ctx context.Context, in *sysin.BannerStatusInp) (err error) {
_, err = dao.Banner.Ctx(ctx).Where(dao.Banner.Columns().Id, in.Id).Data(g.Map{
dao.Banner.Columns().Status: in.Status,
dao.Banner.Columns().UpdatedAt: gtime.Now(),
}).Update()
if err != nil {
err = gerror.Wrap(err, "更新轮播图状态失败")
return
}
return
}

View File

@ -0,0 +1,59 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/addons/flashbanner/global"
"hotgo/addons/flashbanner/model"
"hotgo/addons/flashbanner/model/input/sysin"
"hotgo/addons/flashbanner/service"
isc "hotgo/internal/service"
)
type sSysConfig struct{}
func NewSysConfig() *sSysConfig {
return &sSysConfig{}
}
func init() {
service.RegisterSysConfig(NewSysConfig())
}
// GetBasic 获取基础配置
func (s *sSysConfig) GetBasic(ctx context.Context) (conf *model.BasicConfig, err error) {
var in sysin.GetConfigInp
in.GetAddonsConfigInp.AddonName = global.GetSkeleton().Name
in.GetAddonsConfigInp.Group = "basic"
models, err := isc.SysAddonsConfig().GetConfigByGroup(ctx, &in.GetAddonsConfigInp)
if err != nil {
return
}
err = gconv.Struct(models.List, &conf)
return
}
// GetConfigByGroup 获取指定分组配置
func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in *sysin.GetConfigInp) (res *sysin.GetConfigModel, err error) {
in.GetAddonsConfigInp.AddonName = global.GetSkeleton().Name
models, err := isc.SysAddonsConfig().GetConfigByGroup(ctx, &in.GetAddonsConfigInp)
if err != nil {
return
}
res = new(sysin.GetConfigModel)
res.List = models.List
return
}
// UpdateConfigByGroup 更新指定分组的配置
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateConfigInp) error {
in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name
return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, &in.UpdateAddonsConfigInp)
}

View File

@ -0,0 +1,35 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"fmt"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/addons/flashbanner/global"
"hotgo/addons/flashbanner/model/input/sysin"
"hotgo/addons/flashbanner/service"
"hotgo/internal/library/contexts"
)
type sSysIndex struct{}
func NewSysIndex() *sSysIndex {
return &sSysIndex{}
}
func init() {
service.RegisterSysIndex(NewSysIndex())
}
// Test 测试
func (s *sSysIndex) Test(ctx context.Context, in *sysin.IndexTestInp) (res *sysin.IndexTestModel, err error) {
res = new(sysin.IndexTestModel)
res.Name = in.Name
res.Module = fmt.Sprintf("当前插件模块是:%s当前应用模块是%s", global.GetSkeleton().Name, contexts.Get(ctx).Module)
res.Time = gtime.Now()
return
}

View File

@ -0,0 +1,176 @@
// Package flashbanner
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package flashbanner
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
_ "hotgo/addons/flashbanner/crons"
"hotgo/addons/flashbanner/global"
_ "hotgo/addons/flashbanner/logic"
_ "hotgo/addons/flashbanner/queues"
"hotgo/addons/flashbanner/router"
"hotgo/addons/migrations"
"hotgo/internal/library/addons"
"hotgo/internal/service"
"sync"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
)
type module struct {
skeleton *addons.Skeleton
ctx context.Context
sync.Mutex
}
func init() {
newModule()
}
func newModule() {
m := &module{
skeleton: &addons.Skeleton{
Label: `轮播图管理`,
Name: `flashbanner`,
Group: 6,
Logo: "",
Brief: ``,
Description: ``,
Author: ``,
Version: `v1.0.0`, // 当该版本号高于已安装的版本号时,会提示可以更新
},
ctx: gctx.New(),
}
addons.RegisterModule(m)
}
// Start 启动模块
func (m *module) Start(option *addons.Option) (err error) {
// 初始化模块
global.Init(m.ctx, m.skeleton)
// 注册插件路由
option.Server.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().Addon)
router.Admin(m.ctx, group)
router.Api(m.ctx, group)
router.Home(m.ctx, group)
router.WebSocket(m.ctx, group)
})
return
}
// Stop 停止模块
func (m *module) Stop() (err error) {
return
}
// Ctx 上下文
func (m *module) Ctx() context.Context {
return m.ctx
}
// GetSkeleton 获取模块
func (m *module) GetSkeleton() *addons.Skeleton {
return m.skeleton
}
// Install 安装模块
func (m *module) Install(ctx context.Context) (err error) {
// 执行数据库安装文件
// 获取当前目录
sqlPath := gfile.Pwd() + gfile.Separator + "addons/migrations/flashbanner/install.sql"
g.Log().Debug(ctx, "安装模块", m.skeleton.Label, "路径", sqlPath)
result, err := migrations.DoSqlContent(ctx, sqlPath)
if err != nil {
g.Log().Error(ctx, "安装模块", m.skeleton.Label, "失败", err)
return
}
g.Log().Debug(ctx, "安装模块", m.skeleton.Label, "成功", result)
// 复制web目录下的文件到管理后台对应位置
// 插件的前端配置文件位于插件目录下的web子目录
sourceWebPath := gfile.Pwd() + gfile.Separator + "addons/" + m.skeleton.Name + "/web/src/views/addons/" + m.skeleton.Name
targetWebPath := "../web/src/views/addons/" + m.skeleton.Name
g.Log().Debug(ctx, "复制前端配置文件", "源路径:", sourceWebPath, "目标路径:", targetWebPath)
// 检查源路径是否存在
if gfile.Exists(sourceWebPath) {
err = gfile.CopyDir(sourceWebPath, targetWebPath)
if err != nil {
g.Log().Error(ctx, "复制前端配置文件失败:", err)
} else {
g.Log().Debug(ctx, "复制前端配置文件成功")
}
} else {
g.Log().Warning(ctx, "前端配置文件源路径不存在:", sourceWebPath)
}
// 复制API文件
sourceApiPath := gfile.Pwd() + gfile.Separator + "addons/" + m.skeleton.Name + "/web/src/api/addons/" + m.skeleton.Name
targetApiPath := "../web/src/api/addons/" + m.skeleton.Name
g.Log().Debug(ctx, "复制API文件", "源路径:", sourceApiPath, "目标路径:", targetApiPath)
if gfile.Exists(sourceApiPath) {
err = gfile.CopyDir(sourceApiPath, targetApiPath)
if err != nil {
g.Log().Error(ctx, "复制API文件失败:", err)
} else {
g.Log().Debug(ctx, "复制API文件成功")
}
} else {
g.Log().Warning(ctx, "API文件源路径不存在:", sourceApiPath)
}
return
}
// Upgrade 更新模块
func (m *module) Upgrade(ctx context.Context) (err error) {
// ...
return
}
// UnInstall 卸载模块
func (m *module) UnInstall(ctx context.Context) (err error) {
// ...
// 移除数据库安装文件
sqlPath := gfile.Pwd() + gfile.Separator + "addons/migrations/flashbanner/uninstall.sql"
g.Log().Debug(ctx, "卸载模块", m.skeleton.Label, "路径", sqlPath)
result, err := migrations.DoSqlContent(ctx, sqlPath)
if err != nil {
g.Log().Error(ctx, "卸载模块", m.skeleton.Label, "失败", err)
return
}
g.Log().Debug(ctx, "卸载模块", m.skeleton.Label, "成功", result)
// 删除前端文件
targetWebPath := "../web/src/views/addons/" + m.skeleton.Name
targetApiPath := "../web/src/api/addons/" + m.skeleton.Name
// 删除配置页面文件
if gfile.Exists(targetWebPath) {
err = gfile.Remove(targetWebPath)
if err != nil {
g.Log().Warning(ctx, "删除前端配置文件失败:", err)
} else {
g.Log().Debug(ctx, "删除前端配置文件成功")
}
}
// 删除API文件
if gfile.Exists(targetApiPath) {
err = gfile.Remove(targetApiPath)
if err != nil {
g.Log().Warning(ctx, "删除API文件失败:", err)
} else {
g.Log().Debug(ctx, "删除API文件成功")
}
}
return
}

View File

@ -0,0 +1,11 @@
// Package model
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package model
// BasicConfig 基础配置
type BasicConfig struct {
Test string `json:"basicTest"`
}

View File

@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// Banner is the golang structure for table banner.
type Banner struct {
Id int `json:"id" orm:"id" description:""`
Name string `json:"name" orm:"name" description:"轮播图名称"`
Cover string `json:"cover" orm:"cover" description:"图片URL"`
Link string `json:"link" orm:"link" description:"跳转链接,小程序内用相对地址"`
Type int `json:"type" orm:"type" description:"类型默认不传"`
Status uint `json:"status" orm:"status" description:"1可用"`
Sort int `json:"sort" orm:"sort" description:"排序,数字越大越靠前"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
}

View File

@ -0,0 +1,98 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/addons/flashbanner/model/entity"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
)
// BannerCreateInp 创建轮播图
type BannerCreateInp struct {
Name string `json:"name" v:"required#轮播图名称不能为空" dc:"轮播图名称"`
Cover string `json:"cover" v:"required#轮播图封面不能为空" dc:"轮播图封面"`
Link string `json:"link" dc:"轮播图链接"`
Type int `json:"type" dc:"轮播图类型"`
Sort int `json:"sort" dc:"排序,数字越大越靠前"`
}
func (in *BannerCreateInp) Filter(ctx context.Context) (err error) {
return
}
type BannerCreateModel struct{}
// BannerEditInp 修改/新增轮播图
type BannerEditInp struct {
entity.Banner
}
func (in *BannerEditInp) Filter(ctx context.Context) (err error) {
if in.Name == "" {
err = gerror.New("轮播图名称不能为空")
return
}
if in.Cover == "" {
err = gerror.New("轮播图封面不能为空")
return
}
return
}
type BannerEditModel struct{}
// BannerDeleteInp 删除轮播图
type BannerDeleteInp struct {
Id interface{} `json:"id" v:"required#轮播图ID不能为空" dc:"轮播图ID"`
}
func (in *BannerDeleteInp) Filter(ctx context.Context) (err error) {
return
}
type BannerDeleteModel struct{}
// BannerViewInp 获取指定轮播图信息
type BannerViewInp struct {
Id int64 `json:"id" v:"required#轮播图ID不能为空" dc:"轮播图ID"`
}
func (in *BannerViewInp) Filter(ctx context.Context) (err error) {
return
}
type BannerViewModel struct {
entity.Banner
}
// BannerListInp 获取轮播图列表
type BannerListInp struct {
form.PageReq
Name string `json:"name" dc:"轮播图名称"`
Type int `json:"type" dc:"轮播图类型"`
}
func (in *BannerListInp) Filter(ctx context.Context) (err error) {
return
}
type BannerListModel struct {
entity.Banner
}
// BannerStatusInp 更新轮播图状态
type BannerStatusInp struct {
Id int64 `json:"id" v:"required#轮播图ID不能为空" dc:"轮播图ID"`
Status int `json:"status" v:"required#状态不能为空" dc:"状态"`
}
func (in *BannerStatusInp) Filter(ctx context.Context) (err error) {
return
}
type BannerStatusModel struct{}

View File

@ -0,0 +1,24 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/model/input/sysin"
)
// UpdateConfigInp 更新指定配置
type UpdateConfigInp struct {
sysin.UpdateAddonsConfigInp
}
type GetConfigInp struct {
sysin.GetAddonsConfigInp
}
type GetConfigModel struct {
List g.Map `json:"list"`
}

View File

@ -0,0 +1,27 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package sysin
import (
"context"
"github.com/gogf/gf/v2/os/gtime"
)
// IndexTestInp 测试
type IndexTestInp struct {
Name string `json:"name" d:"HotGo" dc:"名称"`
}
func (in *IndexTestInp) Filter(ctx context.Context) (err error) {
return
}
type IndexTestModel struct {
Name string `json:"name" dc:"名称"`
Module string `json:"module" dc:"当前插件模块"`
Time *gtime.Time `json:"time" dc:"当前时间"`
}

View File

@ -0,0 +1,9 @@
// Package queues
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package queues
// 消息队列.
// 插件中的消息队列消费者可以统一在这里注册和处理

View File

@ -0,0 +1 @@
Hello这是创建插件 [轮播图管理] 时默认生成的一个静态目录文件用于测试当你看到这个提示时说明已经联调成功啦

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello@{.Data.name}!!</p></h1>
<h2><p>@{.Data.module}</p></h2>
<h2><p>服务器时间@{.Data.time}</p></h2>
</div>
</body>
<script>
</script>
</html>

View File

@ -0,0 +1,36 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/flashbanner/controller/admin/sys"
"hotgo/addons/flashbanner/controller/admin/banner"
"hotgo/addons/flashbanner/global"
"hotgo/addons/flashbanner/router/genrouter"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
// Admin 后台路由
func Admin(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
sys.Index,
)
group.Middleware(service.Middleware().AdminAuth)
group.Bind(
sys.Config,
banner.Banner,
)
})
// 注册生成路由
genrouter.Register(ctx, group)
}

View File

@ -0,0 +1,32 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/flashbanner/controller/api/banner"
"hotgo/addons/flashbanner/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
// Api 前台路由
func Api(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppApi, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
// 无需验证的路由
banner.BannerIndex,
)
group.Middleware(service.Middleware().ApiAuth)
group.Bind(
// 需要验证的路由
// ...
)
})
}

View File

@ -0,0 +1,34 @@
// Package genrouter
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package genrouter
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/flashbanner/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
var (
NoLoginRouter []interface{} // 无需登录
LoginRequiredRouter []interface{} // 需要登录
)
// Register 注册通过代码生成的后台路由
func Register(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
if len(NoLoginRouter) > 0 {
group.Bind(NoLoginRouter...)
}
group.Middleware(service.Middleware().AdminAuth)
if len(LoginRequiredRouter) > 0 {
group.Bind(LoginRequiredRouter...)
}
})
}

View File

@ -0,0 +1,25 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/flashbanner/controller/home"
"hotgo/addons/flashbanner/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
)
// Home 前台页面路由
func Home(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppHome, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
home.Index,
)
})
}

View File

@ -0,0 +1,39 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/flashbanner/controller/websocket"
"hotgo/addons/flashbanner/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
ws "hotgo/internal/websocket"
)
// WebSocket ws路由配置
func WebSocket(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppWebSocket, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
// 无需验证的路由
websocket.Index,
)
// ws连接中间件
group.Middleware(service.Middleware().WebSocketAuth)
group.Bind(
// 需要验证的路由
// ..
)
})
// 注册消息路由
ws.RegisterMsg(ws.EventHandlers{
// ...
})
}

View File

@ -0,0 +1,71 @@
// ================================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"hotgo/addons/flashbanner/model"
"hotgo/addons/flashbanner/model/input/sysin"
)
type (
ISysConfig interface {
GetBasic(ctx context.Context) (conf *model.BasicConfig, err error)
GetConfigByGroup(ctx context.Context, in *sysin.GetConfigInp) (res *sysin.GetConfigModel, err error)
UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateConfigInp) error
}
ISysIndex interface {
Test(ctx context.Context, in *sysin.IndexTestInp) (res *sysin.IndexTestModel, err error)
}
ISysBanner interface {
Create(ctx context.Context, in *sysin.BannerCreateInp) (err error)
Edit(ctx context.Context, in *sysin.BannerEditInp) (err error)
Delete(ctx context.Context, in *sysin.BannerDeleteInp) (err error)
View(ctx context.Context, in *sysin.BannerViewInp) (res *sysin.BannerViewModel, err error)
List(ctx context.Context, in *sysin.BannerListInp) (list []*sysin.BannerListModel, totalCount int, err error)
Status(ctx context.Context, in *sysin.BannerStatusInp) (err error)
GetMaxSort(ctx context.Context) (maxSort int, err error)
}
)
var (
localSysConfig ISysConfig
localSysIndex ISysIndex
localSysBanner ISysBanner
)
func SysConfig() ISysConfig {
if localSysConfig == nil {
panic("implement not found for interface ISysConfig, forgot register?")
}
return localSysConfig
}
func RegisterSysConfig(i ISysConfig) {
localSysConfig = i
}
func SysIndex() ISysIndex {
if localSysIndex == nil {
panic("implement not found for interface ISysIndex, forgot register?")
}
return localSysIndex
}
func RegisterSysIndex(i ISysIndex) {
localSysIndex = i
}
func SysBanner() ISysBanner {
if localSysBanner == nil {
panic("implement not found for interface ISysBanner, forgot register?")
}
return localSysBanner
}
func RegisterSysBanner(i ISysBanner) {
localSysBanner = i
}

View File

@ -0,0 +1,17 @@
import { http } from '@/utils/http/axios';
export function getConfig(params) {
return http.request({
url: '/flashbanner/config/get',
method: 'get',
params,
});
}
export function updateConfig(params) {
return http.request({
url: '/flashbanner/config/update',
method: 'post',
params,
});
}

View File

@ -0,0 +1,33 @@
import { http } from '@/utils/http/axios';
export function List(params) {
return http.request({
url: '/flashbanner/banner/list',
method: 'GET',
params,
});
}
export function Add(params) {
return http.request({
url: '/flashbanner/banner/create',
method: 'POST',
params,
});
}
export function Edit(params) {
return http.request({
url: '/flashbanner/banner/edit',
method: 'POST',
params,
});
}
export function Delete(params) {
return http.request({
url: '/flashbanner/banner/delete',
method: 'POST',
params,
});
}

View File

@ -0,0 +1,106 @@
<template>
<div>
<n-modal
v-model:show="showModal"
:on-after-leave="cancelForm"
:mask-closable="false"
:show-icon="false"
preset="dialog"
:title="formParams?.id > 0 ? '编辑 #' + formParams?.id : '添加'"
:style="{
width: dialogWidth,
}"
>
<n-form
:model="formParams"
:rules="rules"
ref="formRef"
label-placement="left"
:label-width="100"
class="py-8"
>
<n-grid x-gap="24" :cols="1">
<n-gi>
<n-form-item label="名称" path="name">
<n-input placeholder="请输入名称" v-model:value="formParams.name" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="上传图片" path="cover">
<UploadImage v-model:value="formParams.cover" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="链接地址" path="link">
<n-input placeholder="请输入链接地址" v-model:value="formParams.link" />
</n-form-item>
</n-gi>
</n-grid>
</n-form>
<template #action>
<n-space>
<n-button @click="cancelForm">取消</n-button>
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
</n-space>
</template>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import { h, reactive, ref, computed, defineExpose, defineEmits } from 'vue';
import { useDialog, useMessage } from 'naive-ui';
import UploadImage from '@/components/Upload/uploadImage.vue';
import { rules } from './model';
import { Edit, Add } from '@/api/addons/flashbanner/index';
import { adaModalWidth } from '@/utils/hotgo';
import { cloneDeep } from 'lodash-es';
const emit = defineEmits(['on-refresh']);
const defaultState = {
name: '',
cover: '',
link: '',
};
const message = useMessage();
const showModal = ref(false);
const formBtnLoading = ref(false);
const formRef = ref<any>();
const formParams = ref<any>(cloneDeep(defaultState));
const dialogWidth = computed(() => {
return adaModalWidth();
});
// 关闭表单
const cancelForm = () => {
showModal.value = false;
formParams.value = cloneDeep(defaultState);
};
// 新增或编辑
function confirmForm(e) {
e.preventDefault();
formBtnLoading.value = true;
const Request = formParams.value.id > 0 ? Edit : Add;
formRef.value.validate((errors) => {
if (!errors) {
Request(formParams.value).then((_res) => {
message.success('操作成功');
cancelForm();
emit('on-refresh');
});
} else {
message.error('请填写完整信息');
}
formBtnLoading.value = false;
});
}
defineExpose({ showModal, formParams });
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,83 @@
import { h, render } from 'vue';
import { FormSchema, useForm } from '@/components/Form';
import { NImage } from 'naive-ui';
import { fallbackSrc } from '@/utils/hotgo';
// **********查询表单********
const detailSchemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '图片名称',
defaultValue: null,
componentProps: {
placeholder: '请输入图片名称',
},
},
];
export const [register, {}] = useForm({
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
labelWidth: 80,
schemas: detailSchemas,
});
// *********表格**********
export const defaultColumns = [
{
title: 'ID',
key: 'id',
width: 100,
},
{
title: '图片名称',
key: 'name',
},
{
title: '图片',
key: 'cover',
render(row) {
if (row.cover !== '') {
return h(NImage, {
width: 40,
height: 40,
src: row.cover,
fallbackSrc: fallbackSrc(),
style: {
width: '40px',
height: '40px',
'max-width': '100%',
'max-height': '100%',
},
});
} else {
return '暂无图片'
}
},
},
{
title: '链接地址',
key: 'link',
render(row) {
return h('a', { href: row.link, target: '_blank' }, row.link);
}
},
{
title: '创建时间',
key: 'createdAt',
width: 180,
},
];
// *********编辑表单规则***********
export const rules = {
name: {
required: true,
trigger: ['blur', 'input'],
message: '请输入图片名称',
},
cover: {
required: true,
trigger: ['blur', 'input'],
message: '请上传图片',
},
};

View File

@ -0,0 +1,74 @@
<template>
<div>
<n-spin :show="show" description="请稍候...">
<n-form :label-width="80" :model="formValue" :rules="rules" ref="formRef">
<n-form-item label="测试参数" path="basicTest">
<n-input v-model:value="formValue.basicTest" placeholder="请输入测试参数" />
<template #feedback>
这是一个测试参数每个插件都可以有独立的配置项可以按需添加</template
>
</n-form-item>
<div>
<n-space>
<n-button type="primary" @click="formSubmit">保存更新</n-button>
</n-space>
</div>
</n-form>
</n-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { useMessage } from 'naive-ui';
import { getConfig, updateConfig } from '@/api/addons/flashbanner/config';
const group = ref('basic');
const show = ref(false);
const rules = {
basicTest: {
required: true,
message: '请输入测试参数',
trigger: 'blur',
},
};
const formRef: any = ref(null);
const message = useMessage();
const formValue = ref({
basicTest: 'HotGo',
});
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
} else {
message.error('验证失败请填写完整信息');
}
});
}
onMounted(() => {
load();
});
function load() {
show.value = true;
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
formValue.value = res.list;
})
.finally(() => {
show.value = false;
});
});
}
</script>

View File

@ -0,0 +1,82 @@
<template>
<div>
<n-grid cols="24 300:1 600:24" :x-gap="12">
<n-grid-item span="6">
<n-card :bordered="false" size="small" class="proCard">
<n-thing
class="thing-cell"
v-for="item in typeTabList"
:key="item.key"
:class="{ 'thing-cell-on': type === item.key }"
@click="switchType(item)"
>
<template #header>{{ item.name }}</template>
<template #description>{{ item.desc }}</template>
</n-thing>
</n-card>
</n-grid-item>
<n-grid-item span="18">
<n-card :bordered="false" size="small" :title="typeTitle" class="proCard">
<BasicSetting v-if="type === 1" />
</n-card>
</n-grid-item>
</n-grid>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import BasicSetting from './BasicSetting.vue';
const typeTabList = [
{
name: '基本设置',
desc: '系统常规设置',
key: 1,
},
];
export default defineComponent({
components: {
BasicSetting,
},
setup() {
const state = reactive({
type: 1,
typeTitle: '基本设置',
});
function switchType(e) {
state.type = e.key;
state.typeTitle = e.name;
}
return {
...toRefs(state),
switchType,
typeTabList,
};
},
});
</script>
<style lang="less" scoped>
.thing-cell {
margin: 0 -16px 10px;
padding: 5px 16px;
&:hover {
background: #f3f3f3;
cursor: pointer;
}
}
.thing-cell-on {
background: #f0faff;
color: #2d8cf0;
::v-deep(.n-thing-main .n-thing-header .n-thing-header__title) {
color: #2d8cf0;
}
&:hover {
background: #f0faff;
}
}
</style>

View File

@ -0,0 +1,151 @@
<template>
<div>
<n-card :bordered="false" class="proCard" title="轮播图管理">
<BasicForm
@register="register"
@submit="handleQuery"
@reset="resetForm"
@keyup.enter="handleQuery"
ref="searchFormRef"
>
<template #statusSlot="{ model, field }">
<n-input v-model:value="model[field]" />
</template>
</BasicForm>
<BasicTable
:openChecked="true"
:columns="columns"
:actionColumn="actionColumn"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="tableRef"
:scroll-x="scrollX"
:resizeHeightOffset="-10000"
>
<template #tableTitle>
<n-button
type="primary"
@click="handleAdd"
class="min-left-space"
v-if="hasPermission(['/member/edit'])"
>
<template #icon>
<n-icon>
<PlusOutlined />
</n-icon>
</template>
添加
</n-button>
</template>
</BasicTable>
<BasicEdit ref="editRef" @on-refresh="onRefresh" />
</n-card>
</div>
</template>
<script lang="ts" setup>
import { h, onMounted, ref, computed, watch, reactive } from 'vue';
import { register, defaultColumns } from './components/model';
import BasicEdit from './components/Edit.vue';
import { BasicForm } from '@/components/Form/index';
import { BasicTable, TableAction } from '@/components/Table';
import { usePermission } from '@/hooks/web/usePermission';
import { List, Delete } from '@/api/addons/flashbanner/index';
import { PlusOutlined } from '@vicons/antd';
import { useDialog, useMessage } from 'naive-ui';
import { cloneDeep } from 'lodash-es';
import { adaTableScrollX } from '@/utils/hotgo';
interface TableActionState {
reload: () => void;
}
const { hasPermission } = usePermission();
const dialog = useDialog();
const message = useMessage();
const formParams = ref({});
const tableRef = ref<TableActionState>();
const editRef = ref();
const columns = ref(defaultColumns);
const actionColumn = reactive({
width: 200,
title: '操作',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: ['/member/delete'],
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: ['/member/delete'],
},
],
});
},
});
const scrollX = computed(() => {
return adaTableScrollX(defaultColumns, actionColumn.width);
});
const loadDataTable = async (res) => {
return await List({ ...formParams.value, ...res });
};
// 刷新table
const onRefresh = () => {
tableRef.value?.reload();
};
// 重置查询框
const resetForm = () => {
formParams.value = {};
onRefresh();
};
// 查询
const handleQuery = (e: any) => {
formParams.value = { ...e };
onRefresh();
};
// 添加
const handleAdd = () => {
if (editRef.value) {
editRef.value.showModal = true;
}
};
// 编辑
const handleEdit = (record: Recordable) => {
if (editRef.value) {
editRef.value.showModal = true;
editRef.value.formParams = cloneDeep(record);
}
}
// 删除
const handleDelete = (record: Recordable) => {
dialog.warning({
title: '警告',
content: '你确定要删除',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
Delete({ id: record.id }).then((_res) => {
message.success('操作成功');
onRefresh();
});
},
});
}
</script>
<style lang="less"></style>

View File

@ -0,0 +1,167 @@
-- 删除表如果存在
DROP TABLE IF EXISTS hg_banner;
-- 创建表
CREATE TABLE hg_banner (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
cover VARCHAR(255) DEFAULT NULL,
link VARCHAR(255) DEFAULT NULL,
type INTEGER DEFAULT 0,
status SMALLINT DEFAULT 1,
sort INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NULL,
updated_at TIMESTAMP DEFAULT NULL
);
-- 添加表注释
COMMENT ON TABLE hg_banner IS '轮播图表';
-- 添加列注释
COMMENT ON COLUMN hg_banner.name IS '轮播图名称';
COMMENT ON COLUMN hg_banner.cover IS '图片URL';
COMMENT ON COLUMN hg_banner.link IS '跳转链接小程序内用相对地址';
COMMENT ON COLUMN hg_banner.type IS '类型默认不传';
COMMENT ON COLUMN hg_banner.status IS '1可用,2不可用';
COMMENT ON COLUMN hg_banner.sort IS '排序数字越大越靠前';
-- 添加 updated_at 字段的更新触发器模拟 MySQL ON UPDATE CURRENT_TIMESTAMP
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_updated_at
BEFORE UPDATE ON hg_banner
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 添加菜单项
-- 先查找或创建"内容管理"父菜单如果不存在
INSERT INTO hg_admin_menu (pid, level, tree, title, name, path, icon, type, redirect, permissions, permission_name, component, always_show, active_menu, is_root, is_frame, frame_src, keep_alive, hidden, affix, sort, remark, status, updated_at, created_at)
SELECT 0, 1, '', '内容管理', 'content', '/content', 'BookOutlined', 1, '/content', '', '', 'LAYOUT', 2, '', 1, 2, '', 2, 2, 2, 12, '内容管理模块', 1, NOW(), NOW()
WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name = 'content');
-- 获取内容管理菜单ID并添加轮播图管理菜单
WITH content_parent AS (
SELECT id FROM hg_admin_menu WHERE name = 'content' LIMIT 1
),
inserted_menu AS (
INSERT INTO hg_admin_menu (pid, level, tree, title, name, path, icon, type, redirect, permissions, permission_name, component, always_show, active_menu, is_root, is_frame, frame_src, keep_alive, hidden, affix, sort, remark, status, updated_at, created_at)
SELECT
cp.id,
2,
'tr_' || cp.id::text || ' ',
'轮播图管理',
'flashbanner',
'/flashbanner',
'',
2,
'',
'/flashbanner/banner/list',
'',
'/addons/flashbanner/index',
2,
'',
2,
2,
'',
1,
2,
2,
10,
'轮播图管理模块',
1,
NOW(),
NOW()
FROM content_parent cp
RETURNING id
)
-- 添加子菜单和按钮
INSERT INTO hg_admin_menu (pid, level, tree, title, name, path, icon, type, redirect, permissions, permission_name, component, always_show, active_menu, is_root, is_frame, frame_src, keep_alive, hidden, affix, sort, remark, status, updated_at, created_at)
SELECT
im.id,
3,
'tr_' || (SELECT id FROM content_parent)::text || ' tr_' || im.id::text || ' ',
'新增轮播',
'addbanner',
'',
'',
3,
'',
'/flashbanner/banner/create',
'',
'',
2,
'',
2,
2,
'',
2,
2,
2,
10,
'新增轮播图权限',
1,
NOW(),
NOW()
FROM inserted_menu im
UNION ALL
SELECT
im.id,
3,
'tr_' || (SELECT id FROM content_parent)::text || ' tr_' || im.id::text || ' ',
'轮播编辑',
'editbanner',
'',
'',
3,
'',
'/flashbanner/banner/update',
'',
'',
2,
'',
2,
2,
'',
2,
2,
2,
10,
'编辑轮播图权限',
1,
NOW(),
NOW()
FROM inserted_menu im
UNION ALL
SELECT
im.id,
3,
'tr_' || (SELECT id FROM content_parent)::text || ' tr_' || im.id::text || ' ',
'删除轮播',
'delbanner',
'',
'',
3,
'',
'/flashbanner/banner/delete',
'',
'',
2,
'',
2,
2,
'',
2,
2,
2,
10,
'删除轮播图权限',
1,
NOW(),
NOW()
FROM inserted_menu im;

View File

@ -0,0 +1,33 @@
DROP TABLE IF EXISTS `hg_banner`;
CREATE TABLE `hg_banner` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL COMMENT '轮播图名称',
`cover` varchar(255) DEFAULT NULL COMMENT '图片URL',
`link` varchar(255) DEFAULT NULL COMMENT '跳转链接小程序内用相对地址',
`type` int(11) DEFAULT 0 COMMENT '类型默认不传',
`status` tinyint(1) DEFAULT 1 COMMENT '1可用,2不可用',
`sort` int(11) DEFAULT 0 COMMENT '排序数字越大越靠前',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轮播图表';
-- 添加菜单项
-- 先查找或创建"内容管理"父菜单如果不存在
INSERT IGNORE INTO `hg_admin_menu`(`pid`, `level`, `tree`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `sort`, `remark`, `status`, `updated_at`, `created_at`) VALUES
(0, 1, '', '内容管理', 'content', '/content', 'BookOutlined', 1, '/content', '', '', 'LAYOUT', 2, '', 1, 2, '', 2, 2, 2, 12, '内容管理模块', 1, NOW(), NOW());
-- 获取内容管理菜单ID并添加轮播图管理菜单
SET @content_parent_id = (SELECT id FROM `hg_admin_menu` WHERE `name` = 'content' LIMIT 1);
INSERT INTO `hg_admin_menu`(`pid`, `level`, `tree`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `sort`, `remark`, `status`, `updated_at`, `created_at`) VALUES
(@content_parent_id, 2, CONCAT('tr_', @content_parent_id, ' '), '轮播图管理', 'flashbanner', '/flashbanner', '', 2, '', '/flashbanner/banner/list', '', '/addons/flashbanner/index', 2, '', 2, 2, '', 1, 2, 2, 10, '轮播图管理模块', 1, NOW(), NOW());
-- 添加子菜单和按钮
SET @flashbanner_menu_id = LAST_INSERT_ID();
INSERT INTO `hg_admin_menu`(`pid`, `level`, `tree`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `sort`, `remark`, `status`, `updated_at`, `created_at`) VALUES
(@flashbanner_menu_id, 3, CONCAT('tr_', @content_parent_id, ' tr_', @flashbanner_menu_id, ' '), '新增轮播', 'addbanner', '', '', 3, '', '/flashbanner/banner/create', '', '', 2, '', 2, 2, '', 2, 2, 2, 10, '新增轮播图权限', 1, NOW(), NOW()),
(@flashbanner_menu_id, 3, CONCAT('tr_', @content_parent_id, ' tr_', @flashbanner_menu_id, ' '), '轮播编辑', 'editbanner', '', '', 3, '', '/flashbanner/banner/update', '', '', 2, '', 2, 2, '', 2, 2, 2, 10, '编辑轮播图权限', 1, NOW(), NOW()),
(@flashbanner_menu_id, 3, CONCAT('tr_', @content_parent_id, ' tr_', @flashbanner_menu_id, ' '), '删除轮播', 'delbanner', '', '', 3, '', '/flashbanner/banner/delete', '', '', 2, '', 2, 2, '', 2, 2, 2, 10, '删除轮播图权限', 1, NOW(), NOW());

View File

@ -0,0 +1,9 @@
-- 删除表
DROP TABLE IF EXISTS hg_banner;
-- 删除 flashbanner 相关菜单
-- 先删除子菜单权限按钮
DELETE FROM hg_admin_menu WHERE name IN ('addbanner', 'editbanner', 'delbanner');
-- 删除主菜单
DELETE FROM hg_admin_menu WHERE name = 'flashbanner';

View File

@ -0,0 +1,9 @@
-- 删除表
DROP TABLE IF EXISTS `hg_banner`;
-- 删除 flashbanner 相关菜单
-- 先删除子菜单权限按钮
DELETE FROM `hg_admin_menu` WHERE `name` IN ('addbanner', 'editbanner', 'delbanner');
-- 删除主菜单
DELETE FROM `hg_admin_menu` WHERE `name` = 'flashbanner';

View File

@ -0,0 +1,2 @@
# 为了在容器映射sql所以需要独立出来
# 兼容数据库和前端文件的迁移

View File

@ -0,0 +1,66 @@
package migrations
import (
"context"
"io"
"os"
"strings"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/container/gvar"
)
func DoSqlContent(ctx context.Context, sqlPath string) (string, error) {
open, err := os.Open(sqlPath)
if err != nil {
return "fail", err
}
defer open.Close()
sqlContent, err := io.ReadAll(open)
if err != nil {
return "fail", err
}
// 首先尝试整个执行
_, err = g.DB().Exec(ctx, string(sqlContent))
if err != nil {
g.Log().Error(ctx, "整个执行SQL失败尝试分句执行:", err)
// 整个执行失败,尝试按分号分割执行
sqls := strings.Split(string(sqlContent), ";")
for _, sql := range sqls {
sql = strings.TrimSpace(sql)
if sql != "" {
_, err := g.DB().Exec(ctx, sql)
if err != nil {
g.Log().Error(ctx, "执行SQL失败:", err, sql)
return "fail", err
}
}
}
}
return "success", nil
}
func GetDbLink(ctx context.Context) *gvar.Var {
link := g.Cfg().MustGet(ctx, "database.default")
//读写分离
if !link.IsSlice() {
return g.Cfg().MustGet(ctx, "database.default.link")
}
for _, v := range link.Array() {
// 只获取主库
val := v.(map[string]interface{})
if val["role"] == "master" {
return gvar.New(val["link"])
}
}
return gvar.New("database.default.0.link")
}
func GetDbType(ctx context.Context) string {
var (
link = GetDbLink(ctx)
)
config := strings.SplitN(link.String(), ":", 2)
return config[0]
}

View File

@ -0,0 +1,8 @@
// Package modules
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package modules
import _ "hotgo/addons/flashbanner"

View File

@ -8,14 +8,15 @@ package addons
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/consts" "hotgo/internal/consts"
"hotgo/internal/model" "hotgo/internal/model"
"hotgo/utility/validate" "hotgo/utility/validate"
"strconv" "strconv"
"strings" "strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
) )
type BuildOption struct { type BuildOption struct {
@ -27,12 +28,14 @@ type BuildOption struct {
// Build 构建新插件 // Build 构建新插件
func Build(ctx context.Context, option *BuildOption) (err error) { func Build(ctx context.Context, option *BuildOption) (err error) {
var ( var (
resourcePath = GetResourcePath(ctx) resourcePath = GetResourcePath(ctx)
buildPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name buildPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name
modulesPath = "./" + consts.AddonsDir + "/modules/" + option.Skeleton.Name + ".go" modulesPath = "./" + consts.AddonsDir + "/modules/" + option.Skeleton.Name + ".go"
webApiPath = gstr.Replace(option.Config.WebApiPath, "{$name}", option.Skeleton.Name) webApiPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name + "/web/src/api/addons/" + option.Skeleton.Name
webViewsPath = gstr.Replace(option.Config.WebViewsPath, "{$name}", option.Skeleton.Name) webViewsPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name + "/web/src/views/addons/" + option.Skeleton.Name
replaces = map[string]string{ webApiPathBase = gstr.Replace(option.Config.WebApiPath, "{$name}", option.Skeleton.Name)
webViewsPathBase = gstr.Replace(option.Config.WebViewsPath, "{$name}", option.Skeleton.Name)
replaces = map[string]string{
"@{.label}": option.Skeleton.Label, "@{.label}": option.Skeleton.Label,
"@{.name}": option.Skeleton.Name, "@{.name}": option.Skeleton.Name,
"@{.group}": strconv.Itoa(option.Skeleton.Group), "@{.group}": strconv.Itoa(option.Skeleton.Group),
@ -94,16 +97,36 @@ func Build(ctx context.Context, option *BuildOption) (err error) {
if err = gfile.PutContents(webApiPath+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil { if err = gfile.PutContents(webApiPath+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil {
return return
} }
if err = gfile.PutContents(webApiPathBase+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil {
// web插件配置主页面
if err = gfile.PutContents(webViewsPath+"/config/BasicSetting.vue", gstr.ReplaceByMap(webConfigBasicSetting, replaces)); err != nil {
return return
} }
// web插件基础配置页面 // web插件配置主页面 - 插件
if err = gfile.PutContents(webViewsPath+"/config/BasicSetting.vue", gstr.ReplaceByMap(webConfigBasicSetting, replaces)); err != nil {
return
}
// web插件基础配置页面 - 插件
if err = gfile.PutContents(webViewsPath+"/config/system.vue", gstr.ReplaceByMap(webConfigSystem, replaces)); err != nil { if err = gfile.PutContents(webViewsPath+"/config/system.vue", gstr.ReplaceByMap(webConfigSystem, replaces)); err != nil {
return return
} }
// web插件配置主页面
if err = gfile.PutContents(webViewsPathBase+"/config/BasicSetting.vue", gstr.ReplaceByMap(webConfigBasicSetting, replaces)); err != nil {
return
}
// web插件基础配置页面
if err = gfile.PutContents(webViewsPathBase+"/config/system.vue", gstr.ReplaceByMap(webConfigSystem, replaces)); err != nil {
return
}
// 创建迁移文件
sqlPath := gfile.Pwd() + gfile.Separator + "addons/migrations/" + option.Skeleton.Name + "/install.sql"
sqlPathUn := gfile.Pwd() + gfile.Separator + "addons/migrations/" + option.Skeleton.Name + "/uninstall.sql"
if err = gfile.PutContents(sqlPath, ""); err != nil {
return
}
if err = gfile.PutContents(sqlPathUn, ""); err != nil {
return
}
// 创建静态目录 // 创建静态目录
if validate.InSlice(option.Extend, consts.AddonsExtendResourcePublic) { if validate.InSlice(option.Extend, consts.AddonsExtendResourcePublic) {

View File

@ -37,3 +37,14 @@ import _ "hotgo/addons/@{.name}"
gf gen service -s=addons/@{.name}/logic -d=addons/@{.name}/service gf gen service -s=addons/@{.name}/logic -d=addons/@{.name}/service
``` ```
```shell
# 插件迁移
cp .\internal\dao\internal\@{.name}* .\addons\@{.name}\dao\internal\
cp .\internal\model\entity\@{.name}* .\addons\@{.name}\model\entity\
```
### 插件开发
- 直接在后台新建插件
- 创建迁移脚本
- 开发工具->代码生成自动生成前后端模块

View File

@ -14,9 +14,15 @@ import (
_ "hotgo/addons/@{.name}/logic" _ "hotgo/addons/@{.name}/logic"
_ "hotgo/addons/@{.name}/queues" _ "hotgo/addons/@{.name}/queues"
"hotgo/addons/@{.name}/router" "hotgo/addons/@{.name}/router"
"hotgo/addons/migrations"
"hotgo/internal/library/addons" "hotgo/internal/library/addons"
"hotgo/internal/service" "hotgo/internal/service"
"sync" "sync"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
) )
type module struct { type module struct {
@ -77,9 +83,54 @@ func (m *module) GetSkeleton() *addons.Skeleton {
return m.skeleton return m.skeleton
} }
// Install 安装模块 // Install 安装模块
func (m *module) Install(ctx context.Context) (err error) { func (m *module) Install(ctx context.Context) (err error) {
// ... sqlExt := ".sql"
if migrations.GetDbType(ctx) == "pgsql" {
sqlExt = ".pg.sql"
}
// 执行数据库安装文件
sqlPath := gfile.Pwd() + gfile.Separator + "addons/migrations/@{.name}/uninstall"+sqlExt
result, err := migrations.DoSqlContent(ctx, sqlPath)
if err != nil {
g.Log().Error(ctx, "安装模块", m.skeleton.Label, "失败", err)
return
}
g.Log().Debug(ctx, "安装模块", m.skeleton.Label, "成功", result)
// 复制web目录下的文件到管理后台对应位置
// 插件的前端配置文件位于插件目录下的web子目录
sourceWebPath := gfile.Pwd() + gfile.Separator + "addons/@{.name}/web/src/views/addons/@{.name}"
targetWebPath := "../web/src/views/addons/@{.name}"
g.Log().Debug(ctx, "复制前端配置文件", "源路径:", sourceWebPath, "目标路径:", targetWebPath)
// 检查源路径是否存在
if gfile.Exists(sourceWebPath) {
err = gfile.CopyDir(sourceWebPath, targetWebPath)
if err != nil {
g.Log().Error(ctx, "复制前端配置文件失败:", err)
} else {
g.Log().Debug(ctx, "复制前端配置文件成功")
}
} else {
g.Log().Warning(ctx, "前端配置文件源路径不存在:", sourceWebPath)
}
// 复制API文件
sourceApiPath := gfile.Pwd() + gfile.Separator + "addons/@{.name}/web/src/api/addons/@{.name}"
targetApiPath := "../web/src/api/addons/@{.name}"
g.Log().Debug(ctx, "复制API文件", "源路径:", sourceApiPath, "目标路径:", targetApiPath)
if gfile.Exists(sourceApiPath) {
err = gfile.CopyDir(sourceApiPath, targetApiPath)
if err != nil {
g.Log().Error(ctx, "复制API文件失败:", err)
} else {
g.Log().Debug(ctx, "复制API文件成功")
}
} else {
g.Log().Warning(ctx, "API文件源路径不存在:", sourceApiPath)
}
return return
} }
@ -91,6 +142,17 @@ func (m *module) Upgrade(ctx context.Context) (err error) {
// UnInstall 卸载模块 // UnInstall 卸载模块
func (m *module) UnInstall(ctx context.Context) (err error) { func (m *module) UnInstall(ctx context.Context) (err error) {
// ... sqlExt := ".sql"
if migrations.GetDbType(ctx) == "pgsql" {
sqlExt = ".pg.sql"
}
// 移除数据库安装文件
sqlPath := gfile.Pwd() + gfile.Separator + "addons/migrations/@{.name}/uninstall" + sqlExt
result, err := migrations.DoSqlContent(ctx, sqlPath)
if err != nil {
g.Log().Error(ctx, "卸载模块", m.skeleton.Label, "失败", err)
return
}
g.Log().Debug(ctx, "卸载模块", m.skeleton.Label, "成功", result)
return return
} }