增加前台模块,添加实例html模板页面

This commit is contained in:
孟帅 2022-12-15 16:12:08 +08:00
parent 6b3333340f
commit a7658b9b8b
18 changed files with 594 additions and 86 deletions

1
server/.gitignore vendored
View File

@ -24,6 +24,7 @@ resource/ssl/server.key
temp/ temp/
main.exe main.exe
main.exe~ main.exe~
*.exe
*.log *.log
*.zip *.zip
.idea .idea

View File

@ -0,0 +1,16 @@
// Package base
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package base
import "github.com/gogf/gf/v2/frame/g"
type SiteIndexReq struct {
g.Meta `path:"/index" method:"get" summary:"首页" tags:"首页"`
}
type SiteIndexRes struct {
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
}

View File

@ -17,7 +17,7 @@ var (
serverCloseSignal chan struct{} serverCloseSignal chan struct{}
Main = &gcmd.Command{ Main = &gcmd.Command{
Description: ` Description: `
欢迎使用HotGo! 命令提示符
--------------------------------------------------------------------------------- ---------------------------------------------------------------------------------
启动服务 启动服务
>> HTTP服务 [go run main.go http] >> HTTP服务 [go run main.go http]
@ -34,8 +34,8 @@ var (
Name: "help", Name: "help",
Brief: "查看帮助", Brief: "查看帮助",
Description: ` Description: `
欢迎使用 HotGo github地址:https://github.com/bufanyun/hotgo
当前版本:v2.0.0 文档地址:文档正在书写中请耐心等一等
`, `,
} }

View File

@ -11,6 +11,8 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcmd"
baseApi "hotgo/api/home/base"
"hotgo/internal/controller/home/base"
"hotgo/internal/library/casbin" "hotgo/internal/library/casbin"
"hotgo/internal/model" "hotgo/internal/model"
"hotgo/internal/router" "hotgo/internal/router"
@ -55,7 +57,7 @@ var (
// 注册默认首页路由 // 注册默认首页路由
group.ALL("/", func(r *ghttp.Request) { group.ALL("/", func(r *ghttp.Request) {
r.Response.Write("hello hotGo!!") _, _ = base.Site.Index(r.Context(), &baseApi.SiteIndexReq{})
return return
}) })
@ -72,6 +74,8 @@ var (
// 注册websocket路由 // 注册websocket路由
router.WebSocket(ctx, group) router.WebSocket(ctx, group)
// 注册前台页面路由
router.Home(ctx, group)
}) })
// 启动定时任务 // 启动定时任务

View File

@ -0,0 +1,29 @@
// Package base
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package base
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"hotgo/api/home/base"
"hotgo/internal/consts"
"hotgo/internal/model"
"hotgo/internal/service"
)
// Site 基础
var Site = cSite{}
type cSite struct{}
func (a *cSite) Index(ctx context.Context, req *base.SiteIndexReq) (res *base.SiteIndexRes, err error) {
service.View().Render(ctx, model.View{Data: g.Map{
"name": "HotGo",
"version": consts.VersionApp,
}})
return
}

View File

@ -10,4 +10,5 @@ import (
_ "hotgo/internal/logic/hook" _ "hotgo/internal/logic/hook"
_ "hotgo/internal/logic/middleware" _ "hotgo/internal/logic/middleware"
_ "hotgo/internal/logic/sys" _ "hotgo/internal/logic/sys"
_ "hotgo/internal/logic/view"
) )

View File

@ -29,6 +29,12 @@ func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
err error err error
) )
// 模板页面响应
if "text/html" == r.Response.Header().Get("Content-Type") {
r.Middleware.Next()
return
}
if err := r.GetError(); err != nil { if err := r.GetError(); err != nil {
g.Log().Print(ctx, err) g.Log().Print(ctx, err)
// 记录到自定义错误日志文件 // 记录到自定义错误日志文件

View File

@ -0,0 +1,192 @@
// Package view
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package view
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/model"
"hotgo/internal/service"
)
type sView struct{}
func init() {
service.RegisterView(New())
}
func New() *sView {
return &sView{}
}
// GetBreadCrumb 前台系统-获取面包屑列表
func (s *sView) GetBreadCrumb(ctx context.Context, in *model.ViewGetBreadCrumbInput) []model.ViewBreadCrumb {
breadcrumb := []model.ViewBreadCrumb{
{Name: "首页", Url: "/"},
}
return breadcrumb
}
// GetTitle 前台系统-获取标题
func (s *sView) GetTitle(ctx context.Context, in *model.ViewGetTitleInput) string {
return "title"
}
// RenderTpl 渲染指定模板页面
func (s *sView) RenderTpl(ctx context.Context, tpl string, data ...model.View) {
var (
viewObj = model.View{}
viewData = make(g.Map)
request = g.RequestFromCtx(ctx)
)
if len(data) > 0 {
viewObj = data[0]
}
if viewObj.Title == "" {
viewObj.Title = g.Cfg().MustGet(ctx, `setting.title`).String()
} else {
viewObj.Title = viewObj.Title + ` - ` + g.Cfg().MustGet(ctx, `setting.title`).String()
}
if viewObj.Keywords == "" {
viewObj.Keywords = g.Cfg().MustGet(ctx, `setting.keywords`).String()
}
if viewObj.Description == "" {
viewObj.Description = g.Cfg().MustGet(ctx, `setting.description`).String()
}
if viewObj.IpcCode == "" {
viewObj.IpcCode = g.Cfg().MustGet(ctx, `setting.icpCode`).String()
}
if viewObj.GET == nil {
viewObj.GET = request.GetQueryMap()
}
// 去掉空数据
viewData = gconv.Map(viewObj)
for k, v := range viewData {
if g.IsEmpty(v) {
delete(viewData, k)
}
}
// 内置对象
viewData["BuildIn"] = &viewBuildIn{httpRequest: request}
// 渲染模板
_ = request.Response.WriteTpl(tpl, viewData)
}
// Render 渲染默认模板页面
func (s *sView) Render(ctx context.Context, data ...model.View) {
s.RenderTpl(ctx, g.Cfg().MustGet(ctx, "viewer.homeLayout").String(), data...)
}
// Render302 跳转中间页面
func (s *sView) Render302(ctx context.Context, data ...model.View) {
view := model.View{}
if len(data) > 0 {
view = data[0]
}
if view.Title == "" {
view.Title = "页面跳转中"
}
//view.MainTpl = s.getViewFolderName(ctx) + "/pages/302.html"
//s.Render(ctx, view)
s.RenderTpl(ctx, "default/pages/302.html", view)
}
// Render401 401页面
func (s *sView) Render401(ctx context.Context, data ...model.View) {
view := model.View{}
if len(data) > 0 {
view = data[0]
}
if view.Title == "" {
view.Title = "无访问权限"
}
s.RenderTpl(ctx, "default/pages/401.html", view)
}
// Render403 403页面
func (s *sView) Render403(ctx context.Context, data ...model.View) {
view := model.View{}
if len(data) > 0 {
view = data[0]
}
if view.Title == "" {
view.Title = "无访问权限"
}
s.RenderTpl(ctx, "default/pages/403.html", view)
}
// Render404 404页面
func (s *sView) Render404(ctx context.Context, data ...model.View) {
view := model.View{}
if len(data) > 0 {
view = data[0]
}
if view.Title == "" {
view.Title = "资源不存在"
}
s.RenderTpl(ctx, "default/pages/404.html", view)
}
// Render500 500页面
func (s *sView) Render500(ctx context.Context, data ...model.View) {
view := model.View{}
if len(data) > 0 {
view = data[0]
}
if view.Title == "" {
view.Title = "请求执行错误"
}
s.RenderTpl(ctx, "default/pages/500.html", view)
}
func (s *sView) Error(ctx context.Context, err error) {
view := model.View{
Title: "错误提示",
Error: err.Error(),
}
s.RenderTpl(ctx, "default/pages/500.html", view)
}
// 获取视图存储目录
func (s *sView) getViewFolderName(ctx context.Context) string {
return gstr.Split(g.Cfg().MustGet(ctx, "viewer.indexLayout").String(), "/")[0]
}
// 获取自动设置的MainTpl
func (s *sView) getDefaultMainTpl(ctx context.Context) string {
var (
viewFolderPrefix = s.getViewFolderName(ctx)
urlPathArray = gstr.SplitAndTrim(g.RequestFromCtx(ctx).URL.Path, "/")
mainTpl string
)
if len(urlPathArray) > 0 && urlPathArray[0] == viewFolderPrefix {
urlPathArray = urlPathArray[1:]
}
switch {
case len(urlPathArray) == 2:
// 如果2级路由为数字那么为模块的详情页面那么路由固定为/xxx/detail。
// 如果需要定制化内容模板请在具体路由方法中设置MainTpl。
if gstr.IsNumeric(urlPathArray[1]) {
urlPathArray[1] = "detail"
}
mainTpl = viewFolderPrefix + "/" + gfile.Join(urlPathArray[0], urlPathArray[1]) + ".html"
case len(urlPathArray) == 1:
mainTpl = viewFolderPrefix + "/" + urlPathArray[0] + "/index.html"
default:
// 默认首页内容
mainTpl = viewFolderPrefix + "/index/index.html"
}
return gstr.TrimLeft(mainTpl, "/")
}

View File

@ -0,0 +1,91 @@
// Package view
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package view
import (
"fmt"
"hotgo/internal/consts"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmode"
)
// 视图自定义方法管理对象
type viewBuildIn struct {
httpRequest *ghttp.Request
}
// Page 创建分页HTML内容
func (s *viewBuildIn) Page(total, size int) string {
page := s.httpRequest.GetPage(total, size)
page.LinkStyle = "page-link"
page.SpanStyle = "page-link"
page.PrevPageTag = "«"
page.NextPageTag = "»"
content := page.PrevPage() + page.PageBar() + page.NextPage()
content = gstr.ReplaceByMap(content, map[string]string{
"<span": "<li class=\"page-item disabled\"><span",
"/span>": "/span></li>",
"<a": "<li class=\"page-item\"><a",
"/a>": "/a></li>",
})
return content
}
// UrlPath 获取当前页面的Url Path.
func (s *viewBuildIn) UrlPath() string {
return s.httpRequest.URL.Path
}
// FormatTime 格式化时间
func (s *viewBuildIn) FormatTime(gt *gtime.Time) string {
if gt == nil {
return ""
}
n := gtime.Now().Timestamp()
t := gt.Timestamp()
var ys int64 = 31536000
var ds int64 = 86400
var hs int64 = 3600
var ms int64 = 60
var ss int64 = 1
var rs string
d := n - t
switch {
case d > ys:
rs = fmt.Sprintf("%d年前", int(d/ys))
case d > ds:
rs = fmt.Sprintf("%d天前", int(d/ds))
case d > hs:
rs = fmt.Sprintf("%d小时前", int(d/hs))
case d > ms:
rs = fmt.Sprintf("%d分钟前", int(d/ms))
case d > ss:
rs = fmt.Sprintf("%d秒前", int(d/ss))
default:
rs = "刚刚"
}
return rs
}
// Version 随机数 开发环境时间戳,线上为前端版本号
func (s *viewBuildIn) Version() string {
var rand string
if gmode.IsDevelop() {
rand = gconv.String(gtime.TimestampMilli())
} else {
rand = consts.VersionApp
}
return rand
}

View File

@ -0,0 +1,42 @@
// Package model
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package model
// View 视图渲染内容对象
type View struct {
Title string // 页面标题
Keywords string // 页面Keywords
Description string // 页面Description
IpcCode string // ICP备案号
Error string // 错误信息
MainTpl string // 自定义MainTpl展示模板文件
Redirect string // 引导页面跳转
ContentType string // 内容模型
BreadCrumb []ViewBreadCrumb // 面包屑
GET map[string]interface{} // GET参数
Data interface{} // 页面参数
}
// ViewBreadCrumb 视图面包屑结构
type ViewBreadCrumb struct {
Name string // 显示名称
Url string // 链接地址,当为空时表示被选中
}
// ViewGetBreadCrumbInput 获取面包屑请求
type ViewGetBreadCrumbInput struct {
ContentId uint // (可选)内容ID
ContentType string // (可选)内容类型
CategoryId uint // (可选)栏目ID
}
// ViewGetTitleInput 获取title请求
type ViewGetTitleInput struct {
ContentType string // (可选)内容类型
CategoryId uint // (可选)栏目ID
CurrentName string // (可选)当前名称
}

View File

@ -0,0 +1,26 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/internal/controller/home/base"
)
// Home 前台页面路由
func Home(ctx context.Context, group *ghttp.RouterGroup) {
routerPrefix, _ := g.Cfg().Get(ctx, "router.home.prefix", "/home")
group.Group(routerPrefix.String(), func(group *ghttp.RouterGroup) {
group.Bind(
base.Site, // 基础
)
})
}

View File

@ -92,13 +92,13 @@ type (
) )
var ( var (
localAdminMemberPost IAdminMemberPost
localAdminMenu IAdminMenu
localAdminNotice IAdminNotice localAdminNotice IAdminNotice
localAdminPost IAdminPost localAdminPost IAdminPost
localAdminRole IAdminRole localAdminRole IAdminRole
localAdminDept IAdminDept localAdminDept IAdminDept
localAdminMember IAdminMember localAdminMember IAdminMember
localAdminMemberPost IAdminMemberPost
localAdminMenu IAdminMenu
) )
func AdminMemberPost() IAdminMemberPost { func AdminMemberPost() IAdminMemberPost {

View File

@ -16,12 +16,6 @@ import (
) )
type ( type (
ISysDictType interface {
Tree(ctx context.Context) (list []g.Map, err error)
Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error
Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error)
Select(ctx context.Context, in sysin.DictTypeSelectInp) (list sysin.DictTypeSelectModel, err error)
}
ISysLog interface { ISysLog interface {
Export(ctx context.Context, in sysin.LogListInp) (err error) Export(ctx context.Context, in sysin.LogListInp) (err error)
RealWrite(ctx context.Context, commonLog entity.SysLog) error RealWrite(ctx context.Context, commonLog entity.SysLog) error
@ -32,6 +26,15 @@ type (
Delete(ctx context.Context, in sysin.LogDeleteInp) error Delete(ctx context.Context, in sysin.LogDeleteInp) error
List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int64, err error) List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int64, err error)
} }
ISysAttachment interface {
Delete(ctx context.Context, in sysin.AttachmentDeleteInp) error
Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error)
Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error)
MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (*sysin.AttachmentMaxSortModel, error)
View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error)
List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int64, err error)
Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (data *entity.SysAttachment, err error)
}
ISysBlacklist interface { ISysBlacklist interface {
Delete(ctx context.Context, in sysin.BlacklistDeleteInp) error Delete(ctx context.Context, in sysin.BlacklistDeleteInp) error
Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error) Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error)
@ -40,12 +43,6 @@ type (
View(ctx context.Context, in sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error) View(ctx context.Context, in sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error)
List(ctx context.Context, in sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int64, err error) List(ctx context.Context, in sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int64, err error)
} }
ISysConfig interface {
GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error)
GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error)
ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error)
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error
}
ISysCron interface { ISysCron interface {
StartCron(ctx context.Context) StartCron(ctx context.Context)
Delete(ctx context.Context, in sysin.CronDeleteInp) error Delete(ctx context.Context, in sysin.CronDeleteInp) error
@ -55,6 +52,23 @@ type (
View(ctx context.Context, in sysin.CronViewInp) (res *sysin.CronViewModel, err error) View(ctx context.Context, in sysin.CronViewInp) (res *sysin.CronViewModel, err error)
List(ctx context.Context, in sysin.CronListInp) (list []*sysin.CronListModel, totalCount int64, err error) List(ctx context.Context, in sysin.CronListInp) (list []*sysin.CronListModel, totalCount int64, err error)
} }
ISysDictData interface {
Delete(ctx context.Context, in sysin.DictDataDeleteInp) error
Edit(ctx context.Context, in sysin.DictDataEditInp) (err error)
List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int64, err error)
}
ISysDictType interface {
Tree(ctx context.Context) (list []g.Map, err error)
Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error
Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error)
Select(ctx context.Context, in sysin.DictTypeSelectInp) (list sysin.DictTypeSelectModel, err error)
}
ISysConfig interface {
GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error)
GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error)
ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error)
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error
}
ISysCronGroup interface { ISysCronGroup interface {
Delete(ctx context.Context, in sysin.CronGroupDeleteInp) error Delete(ctx context.Context, in sysin.CronGroupDeleteInp) error
Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error) Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error)
@ -64,20 +78,6 @@ type (
List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int64, err error) List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int64, err error)
Select(ctx context.Context, in sysin.CronGroupSelectInp) (list sysin.CronGroupSelectModel, err error) Select(ctx context.Context, in sysin.CronGroupSelectInp) (list sysin.CronGroupSelectModel, err error)
} }
ISysDictData interface {
Delete(ctx context.Context, in sysin.DictDataDeleteInp) error
Edit(ctx context.Context, in sysin.DictDataEditInp) (err error)
List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int64, err error)
}
ISysAttachment interface {
Delete(ctx context.Context, in sysin.AttachmentDeleteInp) error
Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error)
Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error)
MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (*sysin.AttachmentMaxSortModel, error)
View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error)
List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int64, err error)
Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (data *entity.SysAttachment, err error)
}
ISysProvinces interface { ISysProvinces interface {
Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error
Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error) Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error)
@ -89,50 +89,17 @@ type (
) )
var ( var (
localSysConfig ISysConfig localSysBlacklist ISysBlacklist
localSysCron ISysCron localSysCron ISysCron
localSysCronGroup ISysCronGroup
localSysDictData ISysDictData localSysDictData ISysDictData
localSysDictType ISysDictType localSysDictType ISysDictType
localSysLog ISysLog localSysLog ISysLog
localSysBlacklist ISysBlacklist
localSysProvinces ISysProvinces
localSysAttachment ISysAttachment localSysAttachment ISysAttachment
localSysCronGroup ISysCronGroup
localSysProvinces ISysProvinces
localSysConfig ISysConfig
) )
func SysAttachment() ISysAttachment {
if localSysAttachment == nil {
panic("implement not found for interface ISysAttachment, forgot register?")
}
return localSysAttachment
}
func RegisterSysAttachment(i ISysAttachment) {
localSysAttachment = i
}
func SysProvinces() ISysProvinces {
if localSysProvinces == nil {
panic("implement not found for interface ISysProvinces, forgot register?")
}
return localSysProvinces
}
func RegisterSysProvinces(i ISysProvinces) {
localSysProvinces = i
}
func SysBlacklist() ISysBlacklist {
if localSysBlacklist == nil {
panic("implement not found for interface ISysBlacklist, forgot register?")
}
return localSysBlacklist
}
func RegisterSysBlacklist(i ISysBlacklist) {
localSysBlacklist = i
}
func SysConfig() ISysConfig { func SysConfig() ISysConfig {
if localSysConfig == nil { if localSysConfig == nil {
panic("implement not found for interface ISysConfig, forgot register?") panic("implement not found for interface ISysConfig, forgot register?")
@ -144,17 +111,6 @@ func RegisterSysConfig(i ISysConfig) {
localSysConfig = i localSysConfig = i
} }
func SysCron() ISysCron {
if localSysCron == nil {
panic("implement not found for interface ISysCron, forgot register?")
}
return localSysCron
}
func RegisterSysCron(i ISysCron) {
localSysCron = i
}
func SysCronGroup() ISysCronGroup { func SysCronGroup() ISysCronGroup {
if localSysCronGroup == nil { if localSysCronGroup == nil {
panic("implement not found for interface ISysCronGroup, forgot register?") panic("implement not found for interface ISysCronGroup, forgot register?")
@ -166,6 +122,17 @@ func RegisterSysCronGroup(i ISysCronGroup) {
localSysCronGroup = i localSysCronGroup = i
} }
func SysProvinces() ISysProvinces {
if localSysProvinces == nil {
panic("implement not found for interface ISysProvinces, forgot register?")
}
return localSysProvinces
}
func RegisterSysProvinces(i ISysProvinces) {
localSysProvinces = i
}
func SysDictData() ISysDictData { func SysDictData() ISysDictData {
if localSysDictData == nil { if localSysDictData == nil {
panic("implement not found for interface ISysDictData, forgot register?") panic("implement not found for interface ISysDictData, forgot register?")
@ -198,3 +165,36 @@ func SysLog() ISysLog {
func RegisterSysLog(i ISysLog) { func RegisterSysLog(i ISysLog) {
localSysLog = i localSysLog = i
} }
func SysAttachment() ISysAttachment {
if localSysAttachment == nil {
panic("implement not found for interface ISysAttachment, forgot register?")
}
return localSysAttachment
}
func RegisterSysAttachment(i ISysAttachment) {
localSysAttachment = i
}
func SysBlacklist() ISysBlacklist {
if localSysBlacklist == nil {
panic("implement not found for interface ISysBlacklist, forgot register?")
}
return localSysBlacklist
}
func RegisterSysBlacklist(i ISysBlacklist) {
localSysBlacklist = i
}
func SysCron() ISysCron {
if localSysCron == nil {
panic("implement not found for interface ISysCron, forgot register?")
}
return localSysCron
}
func RegisterSysCron(i ISysCron) {
localSysCron = i
}

View File

@ -0,0 +1,41 @@
// ================================================================================
// 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/internal/model"
)
type (
IView interface {
GetBreadCrumb(ctx context.Context, in *model.ViewGetBreadCrumbInput) []model.ViewBreadCrumb
GetTitle(ctx context.Context, in *model.ViewGetTitleInput) string
RenderTpl(ctx context.Context, tpl string, data ...model.View)
Render(ctx context.Context, data ...model.View)
Render302(ctx context.Context, data ...model.View)
Render401(ctx context.Context, data ...model.View)
Render403(ctx context.Context, data ...model.View)
Render404(ctx context.Context, data ...model.View)
Render500(ctx context.Context, data ...model.View)
Error(ctx context.Context, err error)
}
)
var (
localView IView
)
func View() IView {
if localView == nil {
panic("implement not found for interface IView, forgot register?")
}
return localView
}
func RegisterView(i IView) {
localView = i
}

View File

@ -46,6 +46,7 @@ type Client struct {
Socket *websocket.Conn // 用户连接 Socket *websocket.Conn // 用户连接
Send chan *WResponse // 待发送的数据 Send chan *WResponse // 待发送的数据
SendClose bool // 发送是否关闭 SendClose bool // 发送是否关闭
closeSignal chan struct{} // 关闭信号
FirstTime uint64 // 首次连接时间 FirstTime uint64 // 首次连接时间
HeartbeatTime uint64 // 用户上次心跳时间 HeartbeatTime uint64 // 用户上次心跳时间
Tags garray.StrArray // 标签 Tags garray.StrArray // 标签
@ -63,6 +64,7 @@ func NewClient(r *ghttp.Request, socket *websocket.Conn, firstTime uint64) (clie
Socket: socket, Socket: socket,
Send: make(chan *WResponse, 100), Send: make(chan *WResponse, 100),
SendClose: false, SendClose: false,
closeSignal: make(chan struct{}, 1),
FirstTime: firstTime, FirstTime: firstTime,
HeartbeatTime: firstTime, HeartbeatTime: firstTime,
User: contexts.Get(r.Context()).User, User: contexts.Get(r.Context()).User,
@ -107,6 +109,9 @@ func (c *Client) write() {
}() }()
for { for {
select { select {
case <-c.closeSignal:
g.Log().Infof(ctxManager, "websocket client quit, user:%+v", c.User)
return
case message, ok := <-c.Send: case message, ok := <-c.Send:
if !ok { if !ok {
// 发送数据错误 关闭连接 // 发送数据错误 关闭连接
@ -159,12 +164,13 @@ func (c *Client) close() {
return return
} }
c.SendClose = true c.SendClose = true
if _, ok := <-c.Send; !ok { //if _, ok := <-c.Send; !ok {
g.Log().Warningf(ctxManager, "close of closed channel, client.id:%v", c.ID) // g.Log().Warningf(ctxManager, "close of closed channel, client.id:%v", c.ID)
} else { //} else {
// 关闭 chan // // 关闭 chan
close(c.Send) // close(c.Send)
} //}
c.closeSignal <- struct{}{}
} }
// Close 关闭指定客户端连接 // Close 关闭指定客户端连接

View File

@ -50,6 +50,18 @@ server:
pprofEnabled: true # 是否开启PProf性能调试特性默认为false pprofEnabled: true # 是否开启PProf性能调试特性默认为false
pprofPattern: "/pprof" # 开启PProf时有效表示PProf特性的页面访问路径对当前Server绑定的所有域名有效 pprofPattern: "/pprof" # 开启PProf时有效表示PProf特性的页面访问路径对当前Server绑定的所有域名有效
viewer:
paths: "resource/template"
defaultFile: "index.html"
delimiters: ["@{", "}"]
homeLayout: "home/index.html"
# 内容设置
setting:
title: "HotGo"
keywords: "中后台解决方案,gf框架,vue3"
description: "hotgo 是一个基于 goframe2vue3vite2TypeScriptuinapp 的中后台解决方案,它可以帮助你快速搭建企业级中后台项目,相信不管是从新技术使用还是其他方面,都能帮助到你,持续更新中。"
# 路由配置 # 路由配置
router: router:
@ -82,6 +94,12 @@ router:
# 不需要验证登录的路由地址 # 不需要验证登录的路由地址
exceptLogin: [ exceptLogin: [
] ]
# 前台页面
home:
# 前缀
prefix: "/home"
# 不需要验证登录的路由地址
exceptPath: [ ]
#JWT #JWT
jwt: jwt:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
<!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.version}</p></h2>
</div>
</body>
<script>
</script>
</html>