mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-10-08 11:06:39 +08:00
🎨 🔥 ✨ 🚑 集成前端代码的&兼容容器化部署插件的插件改造&提供了一个新的轮播图插件
This commit is contained in:
parent
edb673ee34
commit
f254132e18
45
server/addons/flashbanner/README.MD
Normal file
45
server/addons/flashbanner/README.MD
Normal 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"
|
||||
```
|
||||
|
||||
4、HotGo 后台进入 开发工具->插件管理->找到 轮播图管理 (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\
|
||||
```
|
65
server/addons/flashbanner/api/admin/banner/banner.go
Normal file
65
server/addons/flashbanner/api/admin/banner/banner.go
Normal 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{}
|
30
server/addons/flashbanner/api/admin/config/config.go
Normal file
30
server/addons/flashbanner/api/admin/config/config.go
Normal 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 {
|
||||
}
|
33
server/addons/flashbanner/api/admin/index/banner/banner.go
Normal file
33
server/addons/flashbanner/api/admin/index/banner/banner.go
Normal 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
|
||||
}
|
23
server/addons/flashbanner/api/admin/index/index.go
Normal file
23
server/addons/flashbanner/api/admin/index/index.go
Normal 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
|
||||
}
|
||||
|
||||
|
21
server/addons/flashbanner/api/api/index/index.go
Normal file
21
server/addons/flashbanner/api/api/index/index.go
Normal 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
|
||||
}
|
21
server/addons/flashbanner/api/home/index/index.go
Normal file
21
server/addons/flashbanner/api/home/index/index.go
Normal 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/>"`
|
||||
}
|
21
server/addons/flashbanner/api/websocket/index/index.go
Normal file
21
server/addons/flashbanner/api/websocket/index/index.go
Normal 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
|
||||
}
|
9
server/addons/flashbanner/consts/consts.go
Normal file
9
server/addons/flashbanner/consts/consts.go
Normal 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
|
||||
|
||||
// 常量枚举.
|
||||
// 插件中的常量枚举可以统一在这目录下
|
81
server/addons/flashbanner/controller/admin/banner/banner.go
Normal file
81
server/addons/flashbanner/controller/admin/banner/banner.go
Normal 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
|
||||
}
|
14
server/addons/flashbanner/controller/admin/sys/banner.go
Normal file
14
server/addons/flashbanner/controller/admin/sys/banner.go
Normal 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
|
||||
)
|
33
server/addons/flashbanner/controller/admin/sys/config.go
Normal file
33
server/addons/flashbanner/controller/admin/sys/config.go
Normal 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
|
||||
}
|
30
server/addons/flashbanner/controller/admin/sys/index.go
Normal file
30
server/addons/flashbanner/controller/admin/sys/index.go
Normal 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
|
||||
}
|
41
server/addons/flashbanner/controller/api/banner/banner.go
Normal file
41
server/addons/flashbanner/controller/api/banner/banner.go
Normal 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
|
||||
}
|
31
server/addons/flashbanner/controller/api/index.go
Normal file
31
server/addons/flashbanner/controller/api/index.go
Normal 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
|
||||
}
|
||||
|
34
server/addons/flashbanner/controller/home/index.go
Normal file
34
server/addons/flashbanner/controller/home/index.go
Normal 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
|
||||
}
|
30
server/addons/flashbanner/controller/websocket/index.go
Normal file
30
server/addons/flashbanner/controller/websocket/index.go
Normal 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
|
||||
}
|
9
server/addons/flashbanner/crons/crons.go
Normal file
9
server/addons/flashbanner/crons/crons.go
Normal 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
|
||||
|
||||
// 定时任务.
|
||||
// 插件中的定时任务可以统一在这里注册和处理
|
12
server/addons/flashbanner/dao/banner.go
Normal file
12
server/addons/flashbanner/dao/banner.go
Normal 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()
|
||||
)
|
89
server/addons/flashbanner/dao/internal/banner.go
Normal file
89
server/addons/flashbanner/dao/internal/banner.go
Normal 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)
|
||||
}
|
12
server/addons/flashbanner/global/global.go
Normal file
12
server/addons/flashbanner/global/global.go
Normal 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 // 插件架子
|
||||
)
|
22
server/addons/flashbanner/global/init.go
Normal file
22
server/addons/flashbanner/global/init.go
Normal 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
|
||||
}
|
0
server/addons/flashbanner/logic/.gitkeep
Normal file
0
server/addons/flashbanner/logic/.gitkeep
Normal file
9
server/addons/flashbanner/logic/logic.go
Normal file
9
server/addons/flashbanner/logic/logic.go
Normal file
@ -0,0 +1,9 @@
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package logic
|
||||
|
||||
import (
|
||||
_ "hotgo/addons/flashbanner/logic/sys"
|
||||
)
|
173
server/addons/flashbanner/logic/sys/banner.go
Normal file
173
server/addons/flashbanner/logic/sys/banner.go
Normal 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
|
||||
}
|
59
server/addons/flashbanner/logic/sys/config.go
Normal file
59
server/addons/flashbanner/logic/sys/config.go
Normal 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)
|
||||
}
|
35
server/addons/flashbanner/logic/sys/index.go
Normal file
35
server/addons/flashbanner/logic/sys/index.go
Normal 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
|
||||
}
|
176
server/addons/flashbanner/main.go
Normal file
176
server/addons/flashbanner/main.go
Normal 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
|
||||
}
|
11
server/addons/flashbanner/model/config.go
Normal file
11
server/addons/flashbanner/model/config.go
Normal 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"`
|
||||
}
|
22
server/addons/flashbanner/model/entity/banner.go
Normal file
22
server/addons/flashbanner/model/entity/banner.go
Normal 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:""`
|
||||
}
|
98
server/addons/flashbanner/model/input/sysin/banner.go
Normal file
98
server/addons/flashbanner/model/input/sysin/banner.go
Normal 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{}
|
24
server/addons/flashbanner/model/input/sysin/config.go
Normal file
24
server/addons/flashbanner/model/input/sysin/config.go
Normal 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"`
|
||||
}
|
27
server/addons/flashbanner/model/input/sysin/index.go
Normal file
27
server/addons/flashbanner/model/input/sysin/index.go
Normal 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:"当前时间"`
|
||||
}
|
9
server/addons/flashbanner/queues/queues.go
Normal file
9
server/addons/flashbanner/queues/queues.go
Normal 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
|
||||
|
||||
// 消息队列.
|
||||
// 插件中的消息队列消费者可以统一在这里注册和处理
|
0
server/addons/flashbanner/resource/public/.gitkeep
Normal file
0
server/addons/flashbanner/resource/public/.gitkeep
Normal file
1
server/addons/flashbanner/resource/public/default
Normal file
1
server/addons/flashbanner/resource/public/default
Normal file
@ -0,0 +1 @@
|
||||
Hello!这是创建插件 [轮播图管理] 时默认生成的一个静态目录文件,用于测试,当你看到这个提示时,说明已经联调成功啦!
|
30
server/addons/flashbanner/resource/template/home/index.html
Normal file
30
server/addons/flashbanner/resource/template/home/index.html
Normal 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>
|
36
server/addons/flashbanner/router/admin.go
Normal file
36
server/addons/flashbanner/router/admin.go
Normal 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)
|
||||
}
|
32
server/addons/flashbanner/router/api.go
Normal file
32
server/addons/flashbanner/router/api.go
Normal 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(
|
||||
// 需要验证的路由
|
||||
// ...
|
||||
)
|
||||
})
|
||||
}
|
34
server/addons/flashbanner/router/genrouter/init.go
Normal file
34
server/addons/flashbanner/router/genrouter/init.go
Normal 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...)
|
||||
}
|
||||
})
|
||||
}
|
25
server/addons/flashbanner/router/home.go
Normal file
25
server/addons/flashbanner/router/home.go
Normal 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,
|
||||
)
|
||||
})
|
||||
}
|
39
server/addons/flashbanner/router/websocket.go
Normal file
39
server/addons/flashbanner/router/websocket.go
Normal 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{
|
||||
// ...
|
||||
})
|
||||
}
|
0
server/addons/flashbanner/service/.gitkeep
Normal file
0
server/addons/flashbanner/service/.gitkeep
Normal file
71
server/addons/flashbanner/service/sys.go
Normal file
71
server/addons/flashbanner/service/sys.go
Normal 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
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
@ -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>
|
@ -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: '请上传图片',
|
||||
},
|
||||
};
|
@ -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>
|
@ -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>
|
@ -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>
|
167
server/addons/migrations/flashbanner/install.pg.sql
Normal file
167
server/addons/migrations/flashbanner/install.pg.sql
Normal 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;
|
33
server/addons/migrations/flashbanner/install.sql
Normal file
33
server/addons/migrations/flashbanner/install.sql
Normal 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());
|
9
server/addons/migrations/flashbanner/uninstall.pg.sql
Normal file
9
server/addons/migrations/flashbanner/uninstall.pg.sql
Normal 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';
|
9
server/addons/migrations/flashbanner/uninstall.sql
Normal file
9
server/addons/migrations/flashbanner/uninstall.sql
Normal 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';
|
2
server/addons/migrations/readme.md
Normal file
2
server/addons/migrations/readme.md
Normal file
@ -0,0 +1,2 @@
|
||||
# 为了在容器映射sql所以需要独立出来
|
||||
# 兼容数据库和前端文件的迁移
|
66
server/addons/migrations/sqlDo.go
Normal file
66
server/addons/migrations/sqlDo.go
Normal 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]
|
||||
}
|
8
server/addons/modules/flashbanner.go
Normal file
8
server/addons/modules/flashbanner.go
Normal 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"
|
@ -8,14 +8,15 @@ package addons
|
||||
import (
|
||||
"context"
|
||||
"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/model"
|
||||
"hotgo/utility/validate"
|
||||
"strconv"
|
||||
"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 {
|
||||
@ -27,12 +28,14 @@ type BuildOption struct {
|
||||
// Build 构建新插件
|
||||
func Build(ctx context.Context, option *BuildOption) (err error) {
|
||||
var (
|
||||
resourcePath = GetResourcePath(ctx)
|
||||
buildPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name
|
||||
modulesPath = "./" + consts.AddonsDir + "/modules/" + option.Skeleton.Name + ".go"
|
||||
webApiPath = gstr.Replace(option.Config.WebApiPath, "{$name}", option.Skeleton.Name)
|
||||
webViewsPath = gstr.Replace(option.Config.WebViewsPath, "{$name}", option.Skeleton.Name)
|
||||
replaces = map[string]string{
|
||||
resourcePath = GetResourcePath(ctx)
|
||||
buildPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name
|
||||
modulesPath = "./" + consts.AddonsDir + "/modules/" + option.Skeleton.Name + ".go"
|
||||
webApiPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name + "/web/src/api/addons/" + option.Skeleton.Name
|
||||
webViewsPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name + "/web/src/views/addons/" + option.Skeleton.Name
|
||||
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,
|
||||
"@{.name}": option.Skeleton.Name,
|
||||
"@{.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 {
|
||||
return
|
||||
}
|
||||
|
||||
// web插件配置主页面
|
||||
if err = gfile.PutContents(webViewsPath+"/config/BasicSetting.vue", gstr.ReplaceByMap(webConfigBasicSetting, replaces)); err != nil {
|
||||
if err = gfile.PutContents(webApiPathBase+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -37,3 +37,14 @@ import _ "hotgo/addons/@{.name}"
|
||||
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\
|
||||
```
|
||||
|
||||
### 插件开发
|
||||
- 直接在后台新建插件
|
||||
- 创建迁移脚本
|
||||
- 开发工具->代码生成:自动生成前后端模块
|
@ -14,9 +14,15 @@ import (
|
||||
_ "hotgo/addons/@{.name}/logic"
|
||||
_ "hotgo/addons/@{.name}/queues"
|
||||
"hotgo/addons/@{.name}/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 {
|
||||
@ -77,9 +83,54 @@ func (m *module) GetSkeleton() *addons.Skeleton {
|
||||
return m.skeleton
|
||||
}
|
||||
|
||||
|
||||
// Install 安装模块
|
||||
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
|
||||
}
|
||||
|
||||
@ -91,6 +142,17 @@ func (m *module) Upgrade(ctx context.Context) (err error) {
|
||||
|
||||
// UnInstall 卸载模块
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user