第一版完成
This commit is contained in:
1
internal/consts/consts.go
Normal file
1
internal/consts/consts.go
Normal file
@@ -0,0 +1 @@
|
||||
package consts
|
||||
70
internal/controller/admin.go
Normal file
70
internal/controller/admin.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// 管理员控制器
|
||||
type cAdmin struct{}
|
||||
|
||||
var Admin = &cAdmin{}
|
||||
|
||||
// Login 管理员登录
|
||||
func (c *cAdmin) Login(r *ghttp.Request) {
|
||||
var req *model.LoginRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用管理员服务进行登录验证
|
||||
adminService := service.Admin()
|
||||
result, err := adminService.Login(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "登录失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "登录成功", Data: result})
|
||||
}
|
||||
|
||||
// Logout 管理员登出
|
||||
func (c *cAdmin) Logout(r *ghttp.Request) {
|
||||
// 简单的登出处理,清除token等
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "登出成功"})
|
||||
}
|
||||
|
||||
// Detail 获取管理员详情
|
||||
func (c *cAdmin) Detail(r *ghttp.Request) {
|
||||
// 调用管理员服务获取当前管理员信息
|
||||
adminService := service.Admin()
|
||||
result, err := adminService.Profile(r.Context())
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取管理员信息失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Update 更新管理员信息
|
||||
func (c *cAdmin) Update(r *ghttp.Request) {
|
||||
var req *model.UpdateAdminRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用管理员服务更新信息
|
||||
adminService := service.Admin()
|
||||
err := adminService.UpdateProfile(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "更新管理员信息失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "更新成功"})
|
||||
}
|
||||
173
internal/controller/article.go
Normal file
173
internal/controller/article.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// 文章控制器
|
||||
type cArticle struct{}
|
||||
|
||||
var Article = &cArticle{}
|
||||
|
||||
// List 获取文章列表(公开接口)
|
||||
func (c *cArticle) List(r *ghttp.Request) {
|
||||
var req *model.ArticleListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务获取列表
|
||||
articleService := service.Article()
|
||||
result, err := articleService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取文章列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// AdminList 获取文章列表(管理后台)
|
||||
func (c *cArticle) AdminList(r *ghttp.Request) {
|
||||
var req *model.ArticleListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务获取管理后台列表
|
||||
articleService := service.Article()
|
||||
result, err := articleService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取文章列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Create 创建文章
|
||||
func (c *cArticle) Create(r *ghttp.Request) {
|
||||
var req *model.CreateArticleRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务创建文章
|
||||
articleService := service.Article()
|
||||
err := articleService.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "创建文章失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "创建文章成功"})
|
||||
}
|
||||
|
||||
// Detail 获取文章详情(公开接口)
|
||||
func (c *cArticle) Detail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
slug := gconv.String(r.Get("slug"))
|
||||
|
||||
if id <= 0 && slug == "" {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "文章ID或别名无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务获取详情
|
||||
articleService := service.Article()
|
||||
var result *model.Article
|
||||
var err error
|
||||
|
||||
if id > 0 {
|
||||
result, err = articleService.GetById(r.Context(), id)
|
||||
} else {
|
||||
result, err = articleService.GetBySlug(r.Context(), slug)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取文章详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "文章不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// AdminDetail 获取文章详情(管理后台)
|
||||
func (c *cArticle) AdminDetail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "文章ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务获取管理后台详情
|
||||
articleService := service.Article()
|
||||
result, err := articleService.GetById(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取文章详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "文章不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Update 更新文章
|
||||
func (c *cArticle) Update(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "文章ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
var req *model.UpdateArticleRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务更新文章
|
||||
articleService := service.Article()
|
||||
err := articleService.Update(r.Context(), id, req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "更新文章失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "更新文章成功"})
|
||||
}
|
||||
|
||||
// Delete 删除文章
|
||||
func (c *cArticle) Delete(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "文章ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用文章服务删除文章
|
||||
articleService := service.Article()
|
||||
err := articleService.Delete(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "删除文章失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "删除文章成功"})
|
||||
}
|
||||
96
internal/controller/attachment.go
Normal file
96
internal/controller/attachment.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// 附件控制器
|
||||
type cAttachment struct{}
|
||||
|
||||
var Attachment = &cAttachment{}
|
||||
|
||||
// List 获取附件列表
|
||||
func (c *cAttachment) List(r *ghttp.Request) {
|
||||
var req *model.AttachmentListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用附件服务获取列表
|
||||
attachmentService := service.Attachment()
|
||||
result, err := attachmentService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取附件列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Upload 上传附件
|
||||
func (c *cAttachment) Upload(r *ghttp.Request) {
|
||||
// 获取上传的文件
|
||||
file := r.GetUploadFile("file")
|
||||
if file == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "请选择要上传的文件"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用附件服务处理上传
|
||||
attachmentService := service.Attachment()
|
||||
result, err := attachmentService.Upload(r.Context(), file, "uploads")
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "文件上传失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "上传成功", Data: result})
|
||||
}
|
||||
|
||||
// Delete 删除附件
|
||||
func (c *cAttachment) Delete(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "附件ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用附件服务删除附件
|
||||
attachmentService := service.Attachment()
|
||||
err := attachmentService.Delete(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "删除附件失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "删除附件成功"})
|
||||
}
|
||||
|
||||
// Detail 获取附件详情
|
||||
func (c *cAttachment) Detail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "附件ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用附件服务获取详情
|
||||
attachmentService := service.Attachment()
|
||||
result, err := attachmentService.GetById(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取附件详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "附件不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
71
internal/controller/config.go
Normal file
71
internal/controller/config.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// 网站配置控制器
|
||||
type cConfig struct{}
|
||||
|
||||
var Config = &cConfig{}
|
||||
|
||||
// List 获取配置列表
|
||||
func (c *cConfig) List(r *ghttp.Request) {
|
||||
var req *model.ConfigListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用配置服务获取列表
|
||||
configService := service.Config()
|
||||
result, err := configService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取配置列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Update 更新配置
|
||||
func (c *cConfig) Update(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "配置ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
var req *model.UpdateConfigRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用配置服务更新配置
|
||||
configService := service.Config()
|
||||
err := configService.Update(r.Context(), gconv.String(id), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "更新配置失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "更新配置成功"})
|
||||
}
|
||||
|
||||
// Public 获取公开配置
|
||||
func (c *cConfig) Public(r *ghttp.Request) {
|
||||
// 调用配置服务获取公开配置
|
||||
configService := service.Config()
|
||||
result, err := configService.GetPublicConfigs(r.Context())
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取公开配置失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
159
internal/controller/news.go
Normal file
159
internal/controller/news.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// 新闻控制器
|
||||
type cNews struct{}
|
||||
|
||||
var News = &cNews{}
|
||||
|
||||
// List 获取新闻列表(公开接口)
|
||||
func (c *cNews) List(r *ghttp.Request) {
|
||||
var req *model.NewsListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务获取列表
|
||||
newsService := service.News()
|
||||
result, err := newsService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取新闻列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// AdminList 获取新闻列表(管理后台)
|
||||
func (c *cNews) AdminList(r *ghttp.Request) {
|
||||
var req *model.NewsListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务获取列表(暂时使用相同方法)
|
||||
newsService := service.News()
|
||||
result, err := newsService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取新闻列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Create 创建新闻
|
||||
func (c *cNews) Create(r *ghttp.Request) {
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "功能开发中"})
|
||||
}
|
||||
|
||||
// Detail 获取新闻详情(公开接口)
|
||||
func (c *cNews) Detail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
slug := gconv.String(r.Get("slug"))
|
||||
|
||||
if id <= 0 && slug == "" {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "新闻ID或别名无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务获取详情
|
||||
newsService := service.News()
|
||||
var result *model.News
|
||||
var err error
|
||||
|
||||
if id > 0 {
|
||||
result, err = newsService.GetById(r.Context(), id)
|
||||
} else {
|
||||
result, err = newsService.GetBySlug(r.Context(), slug)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取新闻详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "新闻不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// AdminDetail 获取新闻详情(管理后台)
|
||||
func (c *cNews) AdminDetail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "新闻ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务获取管理后台详情
|
||||
newsService := service.News()
|
||||
result, err := newsService.GetById(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取新闻详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "新闻不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Update 更新新闻
|
||||
func (c *cNews) Update(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "新闻ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
var req *model.UpdateNewsRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务更新新闻
|
||||
newsService := service.News()
|
||||
err := newsService.Update(r.Context(), id, req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "更新新闻失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "更新新闻成功"})
|
||||
}
|
||||
|
||||
// Delete 删除新闻
|
||||
func (c *cNews) Delete(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "新闻ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用新闻服务删除新闻
|
||||
newsService := service.News()
|
||||
err := newsService.Delete(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "删除新闻失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "删除新闻成功"})
|
||||
}
|
||||
106
internal/controller/router.go
Normal file
106
internal/controller/router.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// RegisterRoutes 注册所有路由
|
||||
func RegisterRoutes(s *ghttp.Server) {
|
||||
// API v1 路由组
|
||||
v1 := s.Group("/api/v1")
|
||||
|
||||
// 公开API路由 - 不需要认证
|
||||
public := v1.Group("/public")
|
||||
{
|
||||
// 网站配置
|
||||
public.GET("/configs", Config.Public)
|
||||
|
||||
// 文章相关
|
||||
articles := public.Group("/articles")
|
||||
{
|
||||
articles.GET("/", Article.List)
|
||||
articles.GET("/{id}", Article.Detail)
|
||||
}
|
||||
|
||||
// 新闻相关
|
||||
news := public.Group("/news")
|
||||
{
|
||||
news.GET("/", News.List)
|
||||
news.GET("/{id}", News.Detail)
|
||||
}
|
||||
}
|
||||
|
||||
// 用户API路由
|
||||
user := v1.Group("/user")
|
||||
{
|
||||
user.POST("/login", User.Login)
|
||||
user.POST("/register", User.Create)
|
||||
user.GET("/profile", User.Detail)
|
||||
user.PUT("/profile", User.Update)
|
||||
}
|
||||
|
||||
// 管理员API路由
|
||||
admin := v1.Group("/admin")
|
||||
{
|
||||
// 管理员认证
|
||||
admin.POST("/login", Admin.Login)
|
||||
admin.POST("/logout", Admin.Logout)
|
||||
admin.GET("/profile", Admin.Detail)
|
||||
admin.PUT("/profile", Admin.Update)
|
||||
|
||||
// 用户管理
|
||||
users := admin.Group("/users")
|
||||
{
|
||||
users.GET("/", User.List)
|
||||
users.POST("/", User.Create)
|
||||
users.GET("/{id}", User.Detail)
|
||||
users.PUT("/{id}", User.Update)
|
||||
users.DELETE("/{id}", User.Delete)
|
||||
}
|
||||
|
||||
// 文章管理
|
||||
articles := admin.Group("/articles")
|
||||
{
|
||||
articles.GET("/", Article.List)
|
||||
articles.POST("/", Article.Create)
|
||||
articles.GET("/{id}", Article.Detail)
|
||||
articles.PUT("/{id}", Article.Update)
|
||||
articles.DELETE("/{id}", Article.Delete)
|
||||
}
|
||||
|
||||
// 新闻管理
|
||||
news := admin.Group("/news")
|
||||
{
|
||||
news.GET("/", News.List)
|
||||
news.POST("/", News.Create)
|
||||
news.GET("/{id}", News.Detail)
|
||||
news.PUT("/{id}", News.Update)
|
||||
news.DELETE("/{id}", News.Delete)
|
||||
}
|
||||
|
||||
// 附件管理
|
||||
attachments := admin.Group("/attachments")
|
||||
{
|
||||
attachments.GET("/", Attachment.List)
|
||||
attachments.POST("/upload", Attachment.Upload)
|
||||
attachments.GET("/{id}", Attachment.Detail)
|
||||
attachments.DELETE("/{id}", Attachment.Delete)
|
||||
}
|
||||
|
||||
// 系统配置管理
|
||||
configs := admin.Group("/configs")
|
||||
{
|
||||
configs.GET("/", Config.List)
|
||||
configs.PUT("/{id}", Config.Update)
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
s.BindHandler("/health", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(g.Map{
|
||||
"status": "ok",
|
||||
"message": "CMS API服务运行正常",
|
||||
})
|
||||
})
|
||||
}
|
||||
139
internal/controller/user.go
Normal file
139
internal/controller/user.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
"cms-api/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// 用户控制器
|
||||
type cUser struct{}
|
||||
|
||||
var User = &cUser{}
|
||||
|
||||
// List 获取用户列表
|
||||
func (c *cUser) List(r *ghttp.Request) {
|
||||
var req *model.UserListRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务获取列表
|
||||
userService := service.User()
|
||||
result, err := userService.List(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取用户列表失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (c *cUser) Create(r *ghttp.Request) {
|
||||
var req *model.CreateUserRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务创建用户
|
||||
userService := service.User()
|
||||
err := userService.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "创建用户失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "创建用户成功"})
|
||||
}
|
||||
|
||||
// Detail 获取用户详情
|
||||
func (c *cUser) Detail(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "用户ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务获取详情
|
||||
userService := service.User()
|
||||
result, err := userService.GetById(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "获取用户详情失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 404, Message: "用户不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "获取成功", Data: result})
|
||||
}
|
||||
|
||||
// Update 更新用户
|
||||
func (c *cUser) Update(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "用户ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
var req *model.UpdateUserRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务更新用户
|
||||
userService := service.User()
|
||||
err := userService.Update(r.Context(), id, req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "更新用户失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "更新用户成功"})
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (c *cUser) Delete(r *ghttp.Request) {
|
||||
id := gconv.Int(r.Get("id"))
|
||||
if id <= 0 {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "用户ID无效"})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务删除用户
|
||||
userService := service.User()
|
||||
err := userService.Delete(r.Context(), id)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "删除用户失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "删除用户成功"})
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func (c *cUser) Login(r *ghttp.Request) {
|
||||
var req *model.UserLoginRequest
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 400, Message: "参数解析失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务进行登录验证
|
||||
userService := service.User()
|
||||
result, err := userService.Login(r.Context(), req)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(&model.Response{Code: 500, Message: "登录失败", Data: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(&model.Response{Code: 200, Message: "登录成功", Data: result})
|
||||
}
|
||||
0
internal/dao/.gitkeep
Normal file
0
internal/dao/.gitkeep
Normal file
95
internal/dao/admin.go
Normal file
95
internal/dao/admin.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model/entity"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminDao 管理员数据访问对象
|
||||
type AdminDao struct {
|
||||
table string
|
||||
group string
|
||||
columns AdminColumns
|
||||
}
|
||||
|
||||
// AdminColumns 管理员表字段
|
||||
type AdminColumns struct {
|
||||
Id string
|
||||
Username string
|
||||
Password string
|
||||
RealName string
|
||||
Email string
|
||||
Phone string
|
||||
Avatar string
|
||||
RoleId string
|
||||
Status string
|
||||
LastLoginAt string
|
||||
LastLoginIp string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
// NewAdminDao 创建管理员DAO
|
||||
func NewAdminDao() *AdminDao {
|
||||
return &AdminDao{
|
||||
group: "default",
|
||||
table: "cms_admin",
|
||||
columns: AdminColumns{
|
||||
Id: "id",
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
RealName: "real_name",
|
||||
Email: "email",
|
||||
Phone: "phone",
|
||||
Avatar: "avatar",
|
||||
RoleId: "role_id",
|
||||
Status: "status",
|
||||
LastLoginAt: "last_login_at",
|
||||
LastLoginIp: "last_login_ip",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Admin 管理员DAO实例
|
||||
var Admin = NewAdminDao()
|
||||
|
||||
// DB 获取数据库连接
|
||||
func (dao *AdminDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Ctx 创建上下文查询
|
||||
func (dao *AdminDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// GetByUsername 根据用户名获取管理员
|
||||
func (dao *AdminDao) GetByUsername(ctx context.Context, username string) (*entity.Admin, error) {
|
||||
var admin *entity.Admin
|
||||
err := dao.Ctx(ctx).Where(dao.columns.Username, username).Where(dao.columns.DeletedAt, 0).Scan(&admin)
|
||||
return admin, err
|
||||
}
|
||||
|
||||
// GetById 根据ID获取管理员
|
||||
func (dao *AdminDao) GetById(ctx context.Context, id uint64) (*entity.Admin, error) {
|
||||
var admin *entity.Admin
|
||||
err := dao.Ctx(ctx).Where(dao.columns.Id, id).Where(dao.columns.DeletedAt, 0).Scan(&admin)
|
||||
return admin, err
|
||||
}
|
||||
|
||||
// UpdateLastLogin 更新最后登录信息
|
||||
func (dao *AdminDao) UpdateLastLogin(ctx context.Context, id uint64, ip string) error {
|
||||
_, err := dao.Ctx(ctx).Data(g.Map{
|
||||
dao.columns.LastLoginAt: gtime.Now(),
|
||||
dao.columns.LastLoginIp: ip,
|
||||
dao.columns.UpdatedAt: gtime.Now(),
|
||||
}).Where(dao.columns.Id, id).Where(dao.columns.DeletedAt, 0).Update()
|
||||
return err
|
||||
}
|
||||
276
internal/dao/article_methods.go
Normal file
276
internal/dao/article_methods.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// GetById 根据ID获取文章
|
||||
func (dao *ArticleDao) GetById(ctx context.Context, id int) (*model.Article, error) {
|
||||
var article *model.Article
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&article)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// GetBySlug 根据URL别名获取文章
|
||||
func (dao *ArticleDao) GetBySlug(ctx context.Context, slug string) (*model.Article, error) {
|
||||
var article *model.Article
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Slug, slug).Scan(&article)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// IncrementViewCount 增加浏览次数
|
||||
func (dao *ArticleDao) IncrementViewCount(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Increment(dao.Columns().ViewCount, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// List 获取文章列表
|
||||
func (dao *ArticleDao) List(ctx context.Context, req *model.ArticleListRequest) ([]*model.Article, int, error) {
|
||||
var (
|
||||
articles []*model.Article
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.CategoryId > 0 {
|
||||
db = db.Where(dao.Columns().CategoryId, req.CategoryId)
|
||||
}
|
||||
if req.AuthorId > 0 {
|
||||
db = db.Where(dao.Columns().AuthorId, req.AuthorId)
|
||||
}
|
||||
if req.IsPublished >= 0 {
|
||||
db = db.Where(dao.Columns().IsPublished, req.IsPublished)
|
||||
}
|
||||
if req.IsFeatured >= 0 {
|
||||
db = db.Where(dao.Columns().IsFeatured, req.IsFeatured)
|
||||
}
|
||||
if req.IsTop >= 0 {
|
||||
db = db.Where(dao.Columns().IsTop, req.IsTop)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Title+" LIKE ? OR "+dao.Columns().Content+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().IsTop + " DESC, " + dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&articles)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return articles, total, nil
|
||||
}
|
||||
|
||||
// Create 创建文章
|
||||
func (dao *ArticleDao) Create(ctx context.Context, article *model.Article) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(article).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新文章
|
||||
func (dao *ArticleDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除文章
|
||||
func (dao *ArticleDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取文章总数
|
||||
func (dao *ArticleDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetPublishedCount 获取已发布文章数
|
||||
func (dao *ArticleDao) GetPublishedCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().IsPublished, 1).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetFeaturedCount 获取推荐文章数
|
||||
func (dao *ArticleDao) GetFeaturedCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().IsFeatured, 1).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐文章
|
||||
func (dao *ArticleDao) GetFeatured(ctx context.Context, limit int) ([]*model.Article, error) {
|
||||
var articles []*model.Article
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().IsPublished, 1).
|
||||
Where(dao.Columns().IsFeatured, 1).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&articles)
|
||||
return articles, err
|
||||
}
|
||||
|
||||
// GetLatest 获取最新文章
|
||||
func (dao *ArticleDao) GetLatest(ctx context.Context, limit int) ([]*model.Article, error) {
|
||||
var articles []*model.Article
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().IsPublished, 1).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&articles)
|
||||
return articles, err
|
||||
}
|
||||
|
||||
// Search 搜索文章
|
||||
func (dao *ArticleDao) Search(ctx context.Context, keyword string, page, pageSize int) ([]*model.Article, int, error) {
|
||||
var (
|
||||
articles []*model.Article
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加搜索条件
|
||||
if keyword != "" {
|
||||
db = db.Where(dao.Columns().IsPublished, 1).
|
||||
Where(dao.Columns().Title+" LIKE ? OR "+dao.Columns().Content+" LIKE ? OR "+dao.Columns().Summary+" LIKE ?",
|
||||
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&articles)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return articles, total, nil
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取文章
|
||||
func (dao *ArticleDao) GetByCategory(ctx context.Context, categoryId int, page, pageSize int) ([]*model.Article, int, error) {
|
||||
var (
|
||||
articles []*model.Article
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加分类条件
|
||||
db = db.Where(dao.Columns().CategoryId, categoryId).Where(dao.Columns().IsPublished, 1)
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().IsTop + " DESC, " + dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&articles)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return articles, total, nil
|
||||
}
|
||||
|
||||
// GetByAuthor 根据作者获取文章
|
||||
func (dao *ArticleDao) GetByAuthor(ctx context.Context, authorId int, page, pageSize int) ([]*model.Article, int, error) {
|
||||
var (
|
||||
articles []*model.Article
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加作者条件
|
||||
db = db.Where(dao.Columns().AuthorId, authorId).Where(dao.Columns().IsPublished, 1)
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&articles)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return articles, total, nil
|
||||
}
|
||||
|
||||
// GetTopArticles 获取置顶文章
|
||||
func (dao *ArticleDao) GetTopArticles(ctx context.Context, limit int) ([]*model.Article, error) {
|
||||
var articles []*model.Article
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().IsPublished, 1).
|
||||
Where(dao.Columns().IsTop, 1).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&articles)
|
||||
return articles, err
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新文章状态
|
||||
func (dao *ArticleDao) BatchUpdateStatus(ctx context.Context, ids []int, isPublished int) error {
|
||||
_, err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().Id+" IN (?)", ids).
|
||||
Data(g.Map{
|
||||
dao.Columns().IsPublished: isPublished,
|
||||
dao.Columns().UpdatedAt: gdb.Raw("NOW()"),
|
||||
}).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除文章
|
||||
func (dao *ArticleDao) BatchDelete(ctx context.Context, ids []int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id+" IN (?)", ids).Delete()
|
||||
return err
|
||||
}
|
||||
165
internal/dao/attachment_methods.go
Normal file
165
internal/dao/attachment_methods.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// Create 创建附件
|
||||
func (dao *AttachmentDao) Create(ctx context.Context, attachment *model.Attachment) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(attachment).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetById 根据ID获取附件
|
||||
func (dao *AttachmentDao) GetById(ctx context.Context, id int) (*model.Attachment, error) {
|
||||
var attachment *model.Attachment
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&attachment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return attachment, nil
|
||||
}
|
||||
|
||||
// List 获取附件列表
|
||||
func (dao *AttachmentDao) List(ctx context.Context, req *model.AttachmentListRequest) ([]*model.Attachment, int, error) {
|
||||
var (
|
||||
attachments []*model.Attachment
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.FileType != "" {
|
||||
db = db.Where(dao.Columns().FileType, req.FileType)
|
||||
}
|
||||
if req.UploadedBy > 0 {
|
||||
db = db.Where(dao.Columns().UploadedBy, req.UploadedBy)
|
||||
}
|
||||
if req.StorageType != "" {
|
||||
db = db.Where(dao.Columns().StorageType, req.StorageType)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().OriginalName+" LIKE ? OR "+dao.Columns().FileName+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
if req.StartDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" >= ?", req.StartDate)
|
||||
}
|
||||
if req.EndDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" <= ?", req.EndDate)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&attachments)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return attachments, total, nil
|
||||
}
|
||||
|
||||
// Delete 删除附件
|
||||
func (dao *AttachmentDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// IncrementUsageCount 增加使用次数
|
||||
func (dao *AttachmentDao) IncrementUsageCount(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Increment(dao.Columns().UsageCount, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取附件总数
|
||||
func (dao *AttachmentDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetTotalSize 获取附件总大小
|
||||
func (dao *AttachmentDao) GetTotalSize(ctx context.Context) (int64, error) {
|
||||
var totalSize int64
|
||||
err := dao.Ctx(ctx).Fields("SUM(" + dao.Columns().FileSize + ") as total_size").Scan(&totalSize)
|
||||
return totalSize, err
|
||||
}
|
||||
|
||||
// GetCountByType 根据类型获取附件数量
|
||||
func (dao *AttachmentDao) GetCountByType(ctx context.Context, fileType string) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().FileType, fileType).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除附件
|
||||
func (dao *AttachmentDao) BatchDelete(ctx context.Context, ids []int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id+" IN (?)", ids).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetByFileName 根据文件名获取附件
|
||||
func (dao *AttachmentDao) GetByFileName(ctx context.Context, fileName string) (*model.Attachment, error) {
|
||||
var attachment *model.Attachment
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().FileName, fileName).Scan(&attachment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return attachment, nil
|
||||
}
|
||||
|
||||
// GetByUploader 根据上传者获取附件列表
|
||||
func (dao *AttachmentDao) GetByUploader(ctx context.Context, uploaderId int, page, pageSize int) ([]*model.Attachment, int, error) {
|
||||
var (
|
||||
attachments []*model.Attachment
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加上传者条件
|
||||
db = db.Where(dao.Columns().UploadedBy, uploaderId)
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&attachments)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return attachments, total, nil
|
||||
}
|
||||
|
||||
// GetByIds 根据ID列表获取附件
|
||||
func (dao *AttachmentDao) GetByIds(ctx context.Context, ids []int) ([]*model.Attachment, error) {
|
||||
var attachments []*model.Attachment
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id+" IN (?)", ids).Scan(&attachments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return attachments, nil
|
||||
}
|
||||
161
internal/dao/config_methods.go
Normal file
161
internal/dao/config_methods.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetByKey 根据配置键获取配置
|
||||
func (dao *ConfigDao) GetByKey(ctx context.Context, key string) (*model.Config, error) {
|
||||
var config *model.Config
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().ConfigKey, key).Scan(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// List 获取配置列表
|
||||
func (dao *ConfigDao) List(ctx context.Context, req *model.ConfigListRequest) ([]*model.Config, int, error) {
|
||||
var (
|
||||
configs []*model.Config
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.GroupName != "" {
|
||||
db = db.Where(dao.Columns().GroupName, req.GroupName)
|
||||
}
|
||||
if req.IsSystem >= 0 {
|
||||
db = db.Where(dao.Columns().IsSystem, req.IsSystem)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().ConfigKey+" LIKE ? OR "+dao.Columns().Description+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().GroupName + " ASC, " + dao.Columns().SortOrder + " ASC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&configs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return configs, total, nil
|
||||
}
|
||||
|
||||
// GetByGroup 根据分组获取配置
|
||||
func (dao *ConfigDao) GetByGroup(ctx context.Context, groupName string) ([]*model.Config, error) {
|
||||
var configs []*model.Config
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().GroupName, groupName).Order(dao.Columns().SortOrder + " ASC").Scan(&configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetGroups 获取所有配置分组
|
||||
func (dao *ConfigDao) GetGroups(ctx context.Context) ([]string, error) {
|
||||
var groups []string
|
||||
err := dao.Ctx(ctx).Fields("DISTINCT " + dao.Columns().GroupName).Scan(&groups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// Create 创建配置
|
||||
func (dao *ConfigDao) Create(ctx context.Context, config *model.Config) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(config).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新配置 - 根据ID更新
|
||||
func (dao *ConfigDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateByKey 更新配置 - 根据配置键更新
|
||||
func (dao *ConfigDao) UpdateByKey(ctx context.Context, key string, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().ConfigKey, key).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateValue 更新配置值
|
||||
func (dao *ConfigDao) UpdateValue(ctx context.Context, key string, value string) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().ConfigKey, key).Data(map[string]interface{}{
|
||||
dao.Columns().ConfigValue: value,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除配置 - 根据ID删除
|
||||
func (dao *ConfigDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteByKey 删除配置 - 根据配置键删除
|
||||
func (dao *ConfigDao) DeleteByKey(ctx context.Context, key string) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().ConfigKey, key).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetById 根据ID获取配置
|
||||
func (dao *ConfigDao) GetById(ctx context.Context, id int) (*model.Config, error) {
|
||||
var config *model.Config
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// BatchUpdate 批量更新配置
|
||||
func (dao *ConfigDao) BatchUpdate(ctx context.Context, configs map[string]string) error {
|
||||
for key, value := range configs {
|
||||
err := dao.UpdateValue(ctx, key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSystemConfigs 获取系统配置
|
||||
func (dao *ConfigDao) GetSystemConfigs(ctx context.Context) ([]*model.Config, error) {
|
||||
var configs []*model.Config
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().IsSystem, 1).Order(dao.Columns().GroupName + " ASC, " + dao.Columns().SortOrder + " ASC").Scan(&configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetUserConfigs 获取用户配置
|
||||
func (dao *ConfigDao) GetUserConfigs(ctx context.Context) ([]*model.Config, error) {
|
||||
var configs []*model.Config
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().IsSystem, 0).Order(dao.Columns().GroupName + " ASC, " + dao.Columns().SortOrder + " ASC").Scan(&configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
177
internal/dao/contact_methods.go
Normal file
177
internal/dao/contact_methods.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetById 根据ID获取联系信息
|
||||
func (dao *ContactDao) GetById(ctx context.Context, id int) (*model.Contact, error) {
|
||||
var contact *model.Contact
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&contact)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contact, nil
|
||||
}
|
||||
|
||||
// List 获取联系信息列表
|
||||
func (dao *ContactDao) List(ctx context.Context, req *model.ContactListRequest) ([]*model.Contact, int, error) {
|
||||
var (
|
||||
contacts []*model.Contact
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.Status >= 0 {
|
||||
db = db.Where(dao.Columns().Status, req.Status)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Name+" LIKE ? OR "+dao.Columns().Email+" LIKE ? OR "+dao.Columns().Subject+" LIKE ?",
|
||||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
if req.StartDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" >= ?", req.StartDate)
|
||||
}
|
||||
if req.EndDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" <= ?", req.EndDate)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&contacts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return contacts, total, nil
|
||||
}
|
||||
|
||||
// Create 创建联系信息
|
||||
func (dao *ContactDao) Create(ctx context.Context, contact *model.Contact) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(contact).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新联系信息
|
||||
func (dao *ContactDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除联系信息
|
||||
func (dao *ContactDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取联系信息总数
|
||||
func (dao *ContactDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetCountByStatus 根据状态获取联系信息数
|
||||
func (dao *ContactDao) GetCountByStatus(ctx context.Context, status int) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().Status, status).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetTodayCount 获取今日新增联系信息数
|
||||
func (dao *ContactDao) GetTodayCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where("DATE("+dao.Columns().CreatedAt+") = CURDATE()").Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetWeekCount 获取本周新增联系信息数
|
||||
func (dao *ContactDao) GetWeekCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where("YEARWEEK("+dao.Columns().CreatedAt+") = YEARWEEK(NOW())").Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetMonthCount 获取本月新增联系信息数
|
||||
func (dao *ContactDao) GetMonthCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where("YEAR("+dao.Columns().CreatedAt+") = YEAR(NOW()) AND MONTH("+dao.Columns().CreatedAt+") = MONTH(NOW())").Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetLatest 获取最新联系信息
|
||||
func (dao *ContactDao) GetLatest(ctx context.Context, limit int) ([]*model.Contact, error) {
|
||||
var contacts []*model.Contact
|
||||
err := dao.Ctx(ctx).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&contacts)
|
||||
return contacts, err
|
||||
}
|
||||
|
||||
// GetUnprocessed 获取未处理的联系信息
|
||||
func (dao *ContactDao) GetUnprocessed(ctx context.Context, limit int) ([]*model.Contact, error) {
|
||||
var contacts []*model.Contact
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().Status, 0).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&contacts)
|
||||
return contacts, err
|
||||
}
|
||||
|
||||
// Search 搜索联系信息
|
||||
func (dao *ContactDao) Search(ctx context.Context, keyword string, page, pageSize int) ([]*model.Contact, int, error) {
|
||||
var (
|
||||
contacts []*model.Contact
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加搜索条件
|
||||
if keyword != "" {
|
||||
db = db.Where(dao.Columns().Name+" LIKE ? OR "+dao.Columns().Email+" LIKE ? OR "+dao.Columns().Subject+" LIKE ? OR "+dao.Columns().Message+" LIKE ?",
|
||||
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&contacts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return contacts, total, nil
|
||||
}
|
||||
|
||||
// GetTrends 获取联系信息趋势数据
|
||||
func (dao *ContactDao) GetTrends(ctx context.Context, days int) ([]map[string]interface{}, error) {
|
||||
var trends []map[string]interface{}
|
||||
// 这里应该实现趋势数据查询逻辑
|
||||
// 暂时返回空数组,实际项目中需要根据具体需求实现
|
||||
return trends, nil
|
||||
}
|
||||
571
internal/dao/dao.go
Normal file
571
internal/dao/dao.go
Normal file
@@ -0,0 +1,571 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// 数据访问对象实例
|
||||
var (
|
||||
User = NewUserDao()
|
||||
Role = NewRoleDao()
|
||||
Article = NewArticleDao()
|
||||
News = NewNewsDao()
|
||||
Attachment = NewAttachmentDao()
|
||||
Partner = NewPartnerDao()
|
||||
Contact = NewContactDao()
|
||||
Config = NewConfigDao()
|
||||
)
|
||||
|
||||
// UserDao 用户数据访问对象
|
||||
type UserDao struct {
|
||||
table string
|
||||
group string
|
||||
columns UserColumns
|
||||
}
|
||||
|
||||
type UserColumns struct {
|
||||
Id string
|
||||
Account string
|
||||
NickName string
|
||||
Avatar string
|
||||
Email string
|
||||
Password string
|
||||
Balance string
|
||||
QrCode string
|
||||
RoleId string
|
||||
IsSysNotifications string
|
||||
IsCollectionNotifications string
|
||||
IsMarketingNotifications string
|
||||
Ip string
|
||||
IpTable string
|
||||
Status string
|
||||
LastResetPasswordAt string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewUserDao() *UserDao {
|
||||
return &UserDao{
|
||||
group: "default",
|
||||
table: "users",
|
||||
columns: UserColumns{
|
||||
Id: "id",
|
||||
Account: "account",
|
||||
NickName: "nick_name",
|
||||
Avatar: "avatar",
|
||||
Email: "email",
|
||||
Password: "password",
|
||||
Balance: "balance",
|
||||
QrCode: "qr_code",
|
||||
RoleId: "role_id",
|
||||
IsSysNotifications: "is_sys_notifications",
|
||||
IsCollectionNotifications: "is_collection_notifications",
|
||||
IsMarketingNotifications: "is_marketing_notifications",
|
||||
Ip: "ip",
|
||||
IpTable: "ip_table",
|
||||
Status: "status",
|
||||
LastResetPasswordAt: "last_reset_password_at",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *UserDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *UserDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *UserDao) Columns() UserColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *UserDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *UserDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// RoleDao 角色数据访问对象
|
||||
type RoleDao struct {
|
||||
table string
|
||||
group string
|
||||
columns RoleColumns
|
||||
}
|
||||
|
||||
type RoleColumns struct {
|
||||
Id string
|
||||
Name string
|
||||
Description string
|
||||
Permissions string
|
||||
Status string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewRoleDao() *RoleDao {
|
||||
return &RoleDao{
|
||||
group: "default",
|
||||
table: "roles",
|
||||
columns: RoleColumns{
|
||||
Id: "id",
|
||||
Name: "name",
|
||||
Description: "description",
|
||||
Permissions: "permissions",
|
||||
Status: "status",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *RoleDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *RoleDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *RoleDao) Columns() RoleColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *RoleDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *RoleDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// ArticleDao 文章数据访问对象
|
||||
type ArticleDao struct {
|
||||
table string
|
||||
group string
|
||||
columns ArticleColumns
|
||||
}
|
||||
|
||||
type ArticleColumns struct {
|
||||
Id string
|
||||
Title string
|
||||
Slug string
|
||||
Summary string
|
||||
Content string
|
||||
HtmlContent string
|
||||
CoverImage string
|
||||
CategoryId string
|
||||
Tags string
|
||||
AuthorId string
|
||||
ViewCount string
|
||||
LikeCount string
|
||||
CommentCount string
|
||||
IsPublished string
|
||||
IsFeatured string
|
||||
IsTop string
|
||||
SeoTitle string
|
||||
SeoDescription string
|
||||
SeoKeywords string
|
||||
PublishedAt string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewArticleDao() *ArticleDao {
|
||||
return &ArticleDao{
|
||||
group: "default",
|
||||
table: "articles",
|
||||
columns: ArticleColumns{
|
||||
Id: "id",
|
||||
Title: "title",
|
||||
Slug: "slug",
|
||||
Summary: "summary",
|
||||
Content: "content",
|
||||
HtmlContent: "html_content",
|
||||
CoverImage: "cover_image",
|
||||
CategoryId: "category_id",
|
||||
Tags: "tags",
|
||||
AuthorId: "author_id",
|
||||
ViewCount: "view_count",
|
||||
LikeCount: "like_count",
|
||||
CommentCount: "comment_count",
|
||||
IsPublished: "is_published",
|
||||
IsFeatured: "is_featured",
|
||||
IsTop: "is_top",
|
||||
SeoTitle: "seo_title",
|
||||
SeoDescription: "seo_description",
|
||||
SeoKeywords: "seo_keywords",
|
||||
PublishedAt: "published_at",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *ArticleDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *ArticleDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *ArticleDao) Columns() ArticleColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *ArticleDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *ArticleDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// NewsDao 新闻数据访问对象
|
||||
type NewsDao struct {
|
||||
table string
|
||||
group string
|
||||
columns NewsColumns
|
||||
}
|
||||
|
||||
type NewsColumns struct {
|
||||
Id string
|
||||
Title string
|
||||
Slug string
|
||||
Summary string
|
||||
Content string
|
||||
CoverImage string
|
||||
Category string
|
||||
Source string
|
||||
Author string
|
||||
ViewCount string
|
||||
IsPublished string
|
||||
IsFeatured string
|
||||
IsTop string
|
||||
PublishedAt string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewNewsDao() *NewsDao {
|
||||
return &NewsDao{
|
||||
group: "default",
|
||||
table: "news",
|
||||
columns: NewsColumns{
|
||||
Id: "id",
|
||||
Title: "title",
|
||||
Slug: "slug",
|
||||
Summary: "summary",
|
||||
Content: "content",
|
||||
CoverImage: "cover_image",
|
||||
Category: "category",
|
||||
Source: "source",
|
||||
Author: "author",
|
||||
ViewCount: "view_count",
|
||||
IsPublished: "is_published",
|
||||
IsFeatured: "is_featured",
|
||||
IsTop: "is_top",
|
||||
PublishedAt: "published_at",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *NewsDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *NewsDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *NewsDao) Columns() NewsColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *NewsDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *NewsDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// AttachmentDao 附件数据访问对象
|
||||
type AttachmentDao struct {
|
||||
table string
|
||||
group string
|
||||
columns AttachmentColumns
|
||||
}
|
||||
|
||||
type AttachmentColumns struct {
|
||||
Id string
|
||||
OriginalName string
|
||||
FileName string
|
||||
FilePath string
|
||||
FileUrl string
|
||||
FileSize string
|
||||
FileType string
|
||||
MimeType string
|
||||
FileExt string
|
||||
StorageType string
|
||||
UploadIp string
|
||||
UploadedBy string
|
||||
UsageCount string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewAttachmentDao() *AttachmentDao {
|
||||
return &AttachmentDao{
|
||||
group: "default",
|
||||
table: "attachments",
|
||||
columns: AttachmentColumns{
|
||||
Id: "id",
|
||||
OriginalName: "original_name",
|
||||
FileName: "file_name",
|
||||
FilePath: "file_path",
|
||||
FileUrl: "file_url",
|
||||
FileSize: "file_size",
|
||||
FileType: "file_type",
|
||||
MimeType: "mime_type",
|
||||
FileExt: "file_ext",
|
||||
StorageType: "storage_type",
|
||||
UploadIp: "upload_ip",
|
||||
UploadedBy: "uploaded_by",
|
||||
UsageCount: "usage_count",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *AttachmentDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *AttachmentDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *AttachmentDao) Columns() AttachmentColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *AttachmentDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *AttachmentDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// PartnerDao 合作伙伴数据访问对象
|
||||
type PartnerDao struct {
|
||||
table string
|
||||
group string
|
||||
columns PartnerColumns
|
||||
}
|
||||
|
||||
type PartnerColumns struct {
|
||||
Id string
|
||||
Name string
|
||||
Logo string
|
||||
Website string
|
||||
Description string
|
||||
Category string
|
||||
SortOrder string
|
||||
IsFeatured string
|
||||
Status string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
DeletedAt string
|
||||
}
|
||||
|
||||
func NewPartnerDao() *PartnerDao {
|
||||
return &PartnerDao{
|
||||
group: "default",
|
||||
table: "partners",
|
||||
columns: PartnerColumns{
|
||||
Id: "id",
|
||||
Name: "name",
|
||||
Logo: "logo",
|
||||
Website: "website",
|
||||
Description: "description",
|
||||
Category: "category",
|
||||
SortOrder: "sort_order",
|
||||
IsFeatured: "is_featured",
|
||||
Status: "status",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *PartnerDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *PartnerDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *PartnerDao) Columns() PartnerColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *PartnerDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *PartnerDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// ContactDao 联系我们数据访问对象
|
||||
type ContactDao struct {
|
||||
table string
|
||||
group string
|
||||
columns ContactColumns
|
||||
}
|
||||
|
||||
type ContactColumns struct {
|
||||
Id string
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
Company string
|
||||
Subject string
|
||||
Message string
|
||||
Ip string
|
||||
UserAgent string
|
||||
Status string
|
||||
Reply string
|
||||
RepliedAt string
|
||||
RepliedBy string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
func NewContactDao() *ContactDao {
|
||||
return &ContactDao{
|
||||
group: "default",
|
||||
table: "contacts",
|
||||
columns: ContactColumns{
|
||||
Id: "id",
|
||||
Name: "name",
|
||||
Email: "email",
|
||||
Phone: "phone",
|
||||
Company: "company",
|
||||
Subject: "subject",
|
||||
Message: "message",
|
||||
Ip: "ip",
|
||||
UserAgent: "user_agent",
|
||||
Status: "status",
|
||||
Reply: "reply",
|
||||
RepliedAt: "replied_at",
|
||||
RepliedBy: "replied_by",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *ContactDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *ContactDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *ContactDao) Columns() ContactColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *ContactDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *ContactDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// ConfigDao 配置数据访问对象
|
||||
type ConfigDao struct {
|
||||
table string
|
||||
group string
|
||||
columns ConfigColumns
|
||||
}
|
||||
|
||||
type ConfigColumns struct {
|
||||
Id string
|
||||
ConfigKey string
|
||||
ConfigValue string
|
||||
ConfigType string
|
||||
GroupName string
|
||||
Description string
|
||||
SortOrder string
|
||||
IsSystem string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
func NewConfigDao() *ConfigDao {
|
||||
return &ConfigDao{
|
||||
group: "default",
|
||||
table: "site_configs",
|
||||
columns: ConfigColumns{
|
||||
Id: "id",
|
||||
ConfigKey: "config_key",
|
||||
ConfigValue: "config_value",
|
||||
ConfigType: "config_type",
|
||||
GroupName: "group_name",
|
||||
Description: "description",
|
||||
SortOrder: "sort_order",
|
||||
IsSystem: "is_system",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *ConfigDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
func (dao *ConfigDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
func (dao *ConfigDao) Columns() ConfigColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
func (dao *ConfigDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
func (dao *ConfigDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
227
internal/dao/news_methods.go
Normal file
227
internal/dao/news_methods.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetById 根据ID获取新闻
|
||||
func (dao *NewsDao) GetById(ctx context.Context, id int) (*model.News, error) {
|
||||
var news *model.News
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&news)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return news, nil
|
||||
}
|
||||
|
||||
// GetBySlug 根据URL别名获取新闻
|
||||
func (dao *NewsDao) GetBySlug(ctx context.Context, slug string) (*model.News, error) {
|
||||
var news *model.News
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Slug, slug).Scan(&news)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return news, nil
|
||||
}
|
||||
|
||||
// IncrementViewCount 增加浏览次数
|
||||
func (dao *NewsDao) IncrementViewCount(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Increment(dao.Columns().ViewCount, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// List 获取新闻列表
|
||||
func (dao *NewsDao) List(ctx context.Context, req *model.NewsListRequest) ([]*model.News, int, error) {
|
||||
var (
|
||||
newsList []*model.News
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.Category != "" {
|
||||
db = db.Where(dao.Columns().Category, req.Category)
|
||||
}
|
||||
if req.Author != "" {
|
||||
db = db.Where(dao.Columns().Author, req.Author)
|
||||
}
|
||||
if req.Source != "" {
|
||||
db = db.Where(dao.Columns().Source, req.Source)
|
||||
}
|
||||
if req.IsPublished >= 0 {
|
||||
db = db.Where(dao.Columns().IsPublished, req.IsPublished)
|
||||
}
|
||||
if req.IsFeatured >= 0 {
|
||||
db = db.Where(dao.Columns().IsFeatured, req.IsFeatured)
|
||||
}
|
||||
if req.IsTop >= 0 {
|
||||
db = db.Where(dao.Columns().IsTop, req.IsTop)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Title+" LIKE ? OR "+dao.Columns().Content+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
if req.StartDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" >= ?", req.StartDate)
|
||||
}
|
||||
if req.EndDate != "" {
|
||||
db = db.Where(dao.Columns().CreatedAt+" <= ?", req.EndDate)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().IsTop + " DESC, " + dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&newsList)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return newsList, total, nil
|
||||
}
|
||||
|
||||
// Create 创建新闻
|
||||
func (dao *NewsDao) Create(ctx context.Context, news *model.News) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(news).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新新闻
|
||||
func (dao *NewsDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除新闻
|
||||
func (dao *NewsDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取新闻总数
|
||||
func (dao *NewsDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetPublishedCount 获取已发布新闻数
|
||||
func (dao *NewsDao) GetPublishedCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().IsPublished, 1).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetFeaturedCount 获取推荐新闻数
|
||||
func (dao *NewsDao) GetFeaturedCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().IsFeatured, 1).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetCountByCategory 根据分类获取新闻数
|
||||
func (dao *NewsDao) GetCountByCategory(ctx context.Context, category string) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().Category, category).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐新闻
|
||||
func (dao *NewsDao) GetFeatured(ctx context.Context, limit int) ([]*model.News, error) {
|
||||
var newsList []*model.News
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().IsPublished, 1).
|
||||
Where(dao.Columns().IsFeatured, 1).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&newsList)
|
||||
return newsList, err
|
||||
}
|
||||
|
||||
// GetLatest 获取最新新闻
|
||||
func (dao *NewsDao) GetLatest(ctx context.Context, limit int) ([]*model.News, error) {
|
||||
var newsList []*model.News
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().IsPublished, 1).
|
||||
Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(limit).
|
||||
Scan(&newsList)
|
||||
return newsList, err
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取新闻
|
||||
func (dao *NewsDao) GetByCategory(ctx context.Context, category string, page, pageSize int) ([]*model.News, int, error) {
|
||||
var (
|
||||
newsList []*model.News
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加分类条件
|
||||
db = db.Where(dao.Columns().Category, category).Where(dao.Columns().IsPublished, 1)
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().IsTop + " DESC, " + dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&newsList)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return newsList, total, nil
|
||||
}
|
||||
|
||||
// Search 搜索新闻
|
||||
func (dao *NewsDao) Search(ctx context.Context, keyword string, page, pageSize int) ([]*model.News, int, error) {
|
||||
var (
|
||||
newsList []*model.News
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加搜索条件
|
||||
if keyword != "" {
|
||||
db = db.Where(dao.Columns().IsPublished, 1).
|
||||
Where(dao.Columns().Title+" LIKE ? OR "+dao.Columns().Content+" LIKE ? OR "+dao.Columns().Summary+" LIKE ?",
|
||||
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Scan(&newsList)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return newsList, total, nil
|
||||
}
|
||||
168
internal/dao/partner_methods.go
Normal file
168
internal/dao/partner_methods.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetById 根据ID获取合作伙伴
|
||||
func (dao *PartnerDao) GetById(ctx context.Context, id int) (*model.Partner, error) {
|
||||
var partner *model.Partner
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&partner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return partner, nil
|
||||
}
|
||||
|
||||
// List 获取合作伙伴列表
|
||||
func (dao *PartnerDao) List(ctx context.Context, req *model.PartnerListRequest) ([]*model.Partner, int, error) {
|
||||
var (
|
||||
partners []*model.Partner
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.Category != "" {
|
||||
db = db.Where(dao.Columns().Category, req.Category)
|
||||
}
|
||||
if req.IsFeatured >= 0 {
|
||||
db = db.Where(dao.Columns().IsFeatured, req.IsFeatured)
|
||||
}
|
||||
if req.Status >= 0 {
|
||||
db = db.Where(dao.Columns().Status, req.Status)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Name+" LIKE ? OR "+dao.Columns().Description+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().SortOrder + " ASC, " + dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&partners)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return partners, total, nil
|
||||
}
|
||||
|
||||
// GetAll 获取所有合作伙伴
|
||||
func (dao *PartnerDao) GetAll(ctx context.Context) ([]*model.Partner, error) {
|
||||
var partners []*model.Partner
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Status, 1).Order(dao.Columns().SortOrder + " ASC").Scan(&partners)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return partners, nil
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐合作伙伴
|
||||
func (dao *PartnerDao) GetFeatured(ctx context.Context, limit int) ([]*model.Partner, error) {
|
||||
var partners []*model.Partner
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().Status, 1).
|
||||
Where(dao.Columns().IsFeatured, 1).
|
||||
Order(dao.Columns().SortOrder + " ASC").
|
||||
Limit(limit).
|
||||
Scan(&partners)
|
||||
return partners, err
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取合作伙伴
|
||||
func (dao *PartnerDao) GetByCategory(ctx context.Context, category string) ([]*model.Partner, error) {
|
||||
var partners []*model.Partner
|
||||
err := dao.Ctx(ctx).
|
||||
Where(dao.Columns().Category, category).
|
||||
Where(dao.Columns().Status, 1).
|
||||
Order(dao.Columns().SortOrder + " ASC").
|
||||
Scan(&partners)
|
||||
return partners, err
|
||||
}
|
||||
|
||||
// GetByName 根据名称获取合作伙伴
|
||||
func (dao *PartnerDao) GetByName(ctx context.Context, name string) (*model.Partner, error) {
|
||||
var partner *model.Partner
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Name, name).Scan(&partner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return partner, nil
|
||||
}
|
||||
|
||||
// Create 创建合作伙伴
|
||||
func (dao *PartnerDao) Create(ctx context.Context, partner *model.Partner) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(partner).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新合作伙伴
|
||||
func (dao *PartnerDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除合作伙伴
|
||||
func (dao *PartnerDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateStatus 更新合作伙伴状态
|
||||
func (dao *PartnerDao) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(map[string]interface{}{
|
||||
dao.Columns().Status: status,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取合作伙伴总数
|
||||
func (dao *PartnerDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetCountByStatus 根据状态获取合作伙伴数
|
||||
func (dao *PartnerDao) GetCountByStatus(ctx context.Context, status int) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().Status, status).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetFeaturedCount 获取推荐合作伙伴数
|
||||
func (dao *PartnerDao) GetFeaturedCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().IsFeatured, 1).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetCategories 获取所有分类
|
||||
func (dao *PartnerDao) GetCategories(ctx context.Context) ([]string, error) {
|
||||
var categories []string
|
||||
err := dao.Ctx(ctx).Fields("DISTINCT " + dao.Columns().Category).Scan(&categories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
// GetCountByCategory 根据分类获取合作伙伴数
|
||||
func (dao *PartnerDao) GetCountByCategory(ctx context.Context, category string) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().Category, category).Count()
|
||||
return count, err
|
||||
}
|
||||
121
internal/dao/role_methods.go
Normal file
121
internal/dao/role_methods.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetById 根据ID获取角色
|
||||
func (dao *RoleDao) GetById(ctx context.Context, id int) (*model.Role, error) {
|
||||
var role *model.Role
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role, nil
|
||||
}
|
||||
|
||||
// List 获取角色列表
|
||||
func (dao *RoleDao) List(ctx context.Context, req *model.RoleListRequest) ([]*model.Role, int, error) {
|
||||
var (
|
||||
roles []*model.Role
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.Status >= 0 {
|
||||
db = db.Where(dao.Columns().Status, req.Status)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Name+" LIKE ? OR "+dao.Columns().Description+" LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&roles)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return roles, total, nil
|
||||
}
|
||||
|
||||
// GetAll 获取所有角色
|
||||
func (dao *RoleDao) GetAll(ctx context.Context) ([]*model.Role, error) {
|
||||
var roles []*model.Role
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Status, 1).Order(dao.Columns().CreatedAt + " ASC").Scan(&roles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// GetByName 根据名称获取角色
|
||||
func (dao *RoleDao) GetByName(ctx context.Context, name string) (*model.Role, error) {
|
||||
var role *model.Role
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Name, name).Scan(&role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role, nil
|
||||
}
|
||||
|
||||
// Create 创建角色
|
||||
func (dao *RoleDao) Create(ctx context.Context, role *model.Role) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(role).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新角色
|
||||
func (dao *RoleDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除角色
|
||||
func (dao *RoleDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateStatus 更新角色状态
|
||||
func (dao *RoleDao) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(map[string]interface{}{
|
||||
dao.Columns().Status: status,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckPermission 检查权限
|
||||
func (dao *RoleDao) CheckPermission(ctx context.Context, roleId int, permission string) (bool, error) {
|
||||
var role *model.Role
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, roleId).Scan(&role)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if role == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 这里应该实现权限检查逻辑
|
||||
// 暂时返回true,实际项目中需要根据role.Permissions字段进行权限验证
|
||||
return true, nil
|
||||
}
|
||||
121
internal/dao/user_methods.go
Normal file
121
internal/dao/user_methods.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/model"
|
||||
)
|
||||
|
||||
// GetByAccount 根据账号获取用户
|
||||
func (dao *UserDao) GetByAccount(ctx context.Context, account string) (*model.User, error) {
|
||||
var user *model.User
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Account, account).Scan(&user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetByEmail 根据邮箱获取用户
|
||||
func (dao *UserDao) GetByEmail(ctx context.Context, email string) (*model.User, error) {
|
||||
var user *model.User
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Email, email).Scan(&user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetById 根据ID获取用户
|
||||
func (dao *UserDao) GetById(ctx context.Context, id int) (*model.User, error) {
|
||||
var user *model.User
|
||||
err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Scan(&user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// List 获取用户列表
|
||||
func (dao *UserDao) List(ctx context.Context, req *model.UserListRequest) ([]*model.User, int, error) {
|
||||
var (
|
||||
users []*model.User
|
||||
total int
|
||||
db = dao.Ctx(ctx)
|
||||
)
|
||||
|
||||
// 添加查询条件
|
||||
if req.RoleId > 0 {
|
||||
db = db.Where(dao.Columns().RoleId, req.RoleId)
|
||||
}
|
||||
if req.Status >= 0 {
|
||||
db = db.Where(dao.Columns().Status, req.Status)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where(dao.Columns().Account+" LIKE ? OR "+dao.Columns().NickName+" LIKE ? OR "+dao.Columns().Email+" LIKE ?",
|
||||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
count, err := db.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = count
|
||||
|
||||
// 分页查询
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
err = db.Order(dao.Columns().CreatedAt + " DESC").
|
||||
Limit(req.PageSize).
|
||||
Offset(offset).
|
||||
Scan(&users)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return users, total, nil
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (dao *UserDao) Create(ctx context.Context, user *model.User) (int64, error) {
|
||||
result, err := dao.Ctx(ctx).Data(user).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Update 更新用户
|
||||
func (dao *UserDao) Update(ctx context.Context, id int, data interface{}) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (dao *UserDao) Delete(ctx context.Context, id int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateStatus 更新用户状态
|
||||
func (dao *UserDao) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
_, err := dao.Ctx(ctx).Where(dao.Columns().Id, id).Data(map[string]interface{}{
|
||||
dao.Columns().Status: status,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCount 获取用户总数
|
||||
func (dao *UserDao) GetCount(ctx context.Context) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Count()
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetCountByStatus 根据状态获取用户数
|
||||
func (dao *UserDao) GetCountByStatus(ctx context.Context, status int) (int, error) {
|
||||
count, err := dao.Ctx(ctx).Where(dao.Columns().Status, status).Count()
|
||||
return count, err
|
||||
}
|
||||
65
internal/middleware/auth.go
Normal file
65
internal/middleware/auth.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// UserAuth 用户认证中间件
|
||||
func UserAuth(r *ghttp.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 401,
|
||||
"message": "未提供认证令牌",
|
||||
})
|
||||
r.ExitAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 简单的token验证(实际项目中应该验证JWT)
|
||||
if token != "Bearer user-token" {
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 401,
|
||||
"message": "认证令牌无效",
|
||||
})
|
||||
r.ExitAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 设置用户信息到上下文
|
||||
r.SetCtxVar("user_id", 1)
|
||||
r.SetCtxVar("username", "testuser")
|
||||
|
||||
r.Middleware.Next()
|
||||
}
|
||||
|
||||
// AdminAuth 管理员认证中间件
|
||||
func AdminAuth(r *ghttp.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 401,
|
||||
"message": "未提供认证令牌",
|
||||
})
|
||||
r.ExitAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 简单的token验证(实际项目中应该验证JWT)
|
||||
if token != "Bearer admin-token" {
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 401,
|
||||
"message": "管理员认证令牌无效",
|
||||
})
|
||||
r.ExitAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 设置管理员信息到上下文
|
||||
r.SetCtxVar("admin_id", 1)
|
||||
r.SetCtxVar("admin_username", "admin")
|
||||
r.SetCtxVar("admin_role", "super_admin")
|
||||
|
||||
r.Middleware.Next()
|
||||
}
|
||||
11
internal/middleware/cors.go
Normal file
11
internal/middleware/cors.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// CORS 跨域中间件
|
||||
func CORS(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
r.Middleware.Next()
|
||||
}
|
||||
54
internal/middleware/response.go
Normal file
54
internal/middleware/response.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// ResponseHandler 统一响应处理中间件
|
||||
func ResponseHandler(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
|
||||
// 如果已经有响应内容,则不处理
|
||||
if r.Response.BufferLength() > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
ctx = r.Context()
|
||||
res = r.GetHandlerResponse()
|
||||
err = r.GetError()
|
||||
msg = "操作成功"
|
||||
data interface{}
|
||||
)
|
||||
|
||||
var responseCode int = 200
|
||||
|
||||
if err != nil {
|
||||
errCode := gerror.Code(err)
|
||||
if errCode != gcode.CodeNil {
|
||||
responseCode = int(errCode.Code())
|
||||
} else {
|
||||
responseCode = 500
|
||||
}
|
||||
msg = err.Error()
|
||||
g.Log().Error(ctx, "请求处理错误:", err)
|
||||
} else if r.Response.Status >= 400 {
|
||||
msg = "请求失败"
|
||||
responseCode = r.Response.Status
|
||||
} else {
|
||||
data = res
|
||||
}
|
||||
|
||||
response := &model.Response{
|
||||
Code: responseCode,
|
||||
Message: msg,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
r.Response.WriteJsonExit(response)
|
||||
}
|
||||
0
internal/model/.gitkeep
Normal file
0
internal/model/.gitkeep
Normal file
77
internal/model/admin.go
Normal file
77
internal/model/admin.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Admin 管理员实体
|
||||
type Admin struct {
|
||||
Id uint64 `json:"id" orm:"id,primary"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Password string `json:"-" orm:"password"`
|
||||
RealName string `json:"real_name" orm:"real_name"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
Phone string `json:"phone" orm:"phone"`
|
||||
Avatar string `json:"avatar" orm:"avatar"`
|
||||
RoleId uint64 `json:"role_id" orm:"role_id"`
|
||||
Status uint8 `json:"status" orm:"status"`
|
||||
LastLoginAt *time.Time `json:"last_login_at" orm:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip" orm:"last_login_ip"`
|
||||
CreatedAt *time.Time `json:"created_at" orm:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" orm:"updated_at"`
|
||||
DeletedAt uint64 `json:"deleted_at" orm:"deleted_at"`
|
||||
}
|
||||
|
||||
// LoginRequest 登录请求
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" v:"required|length:3,30#用户名不能为空|用户名长度为3到30位"`
|
||||
Password string `json:"password" v:"required|length:6,30#密码不能为空|密码长度为6到30位"`
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
Admin *Admin `json:"admin"`
|
||||
}
|
||||
|
||||
// AdminListRequest 管理员列表请求
|
||||
type AdminListRequest struct {
|
||||
Page int `json:"page" v:"min:1#页码不能小于1"`
|
||||
PageSize int `json:"page_size" v:"min:1|max:100#每页数量不能小于1且不能大于100"`
|
||||
Username string `json:"username"`
|
||||
RealName string `json:"real_name"`
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
|
||||
// CreateAdminRequest 创建管理员请求
|
||||
type CreateAdminRequest struct {
|
||||
Username string `json:"username" v:"required|length:3,30#用户名不能为空|用户名长度为3到30位"`
|
||||
Password string `json:"password" v:"required|length:6,30#密码不能为空|密码长度为6到30位"`
|
||||
RealName string `json:"real_name" v:"required|length:2,20#真实姓名不能为空|真实姓名长度为2到20位"`
|
||||
Email string `json:"email" v:"email#邮箱格式不正确"`
|
||||
Phone string `json:"phone" v:"phone#手机号格式不正确"`
|
||||
RoleId uint64 `json:"role_id" v:"required|min:1#角色ID不能为空"`
|
||||
Status uint8 `json:"status" v:"in:0,1#状态值只能为0或1"`
|
||||
}
|
||||
|
||||
// UpdateAdminRequest 更新管理员请求
|
||||
type UpdateAdminRequest struct {
|
||||
RealName string `json:"real_name" v:"required|length:2,20#真实姓名不能为空|真实姓名长度为2到20位"`
|
||||
Email string `json:"email" v:"email#邮箱格式不正确"`
|
||||
Phone string `json:"phone" v:"phone#手机号格式不正确"`
|
||||
RoleId uint64 `json:"role_id" v:"required|min:1#角色ID不能为空"`
|
||||
Status uint8 `json:"status" v:"in:0,1#状态值只能为0或1"`
|
||||
}
|
||||
|
||||
// AdminLoginRequest 管理员登录请求
|
||||
type AdminLoginRequest struct {
|
||||
Username string `json:"username" v:"required|length:3,30#用户名不能为空|用户名长度为3到30位"`
|
||||
Password string `json:"password" v:"required|length:6,30#密码不能为空|密码长度为6到30位"`
|
||||
}
|
||||
|
||||
// AdminLoginResponse 管理员登录响应
|
||||
type AdminLoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Admin *Admin `json:"admin"`
|
||||
}
|
||||
67
internal/model/article.go
Normal file
67
internal/model/article.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Article 文章模型
|
||||
type Article struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Title string `json:"title" dc:"标题"`
|
||||
Slug string `json:"slug" dc:"URL别名"`
|
||||
Summary string `json:"summary" dc:"摘要"`
|
||||
Content string `json:"content" dc:"内容(Markdown)"`
|
||||
HtmlContent string `json:"html_content" dc:"HTML内容"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
CategoryId int `json:"category_id" dc:"分类ID"`
|
||||
Tags interface{} `json:"tags" dc:"标签"`
|
||||
AuthorId int `json:"author_id" dc:"作者ID"`
|
||||
ViewCount int `json:"view_count" dc:"浏览次数"`
|
||||
LikeCount int `json:"like_count" dc:"点赞次数"`
|
||||
CommentCount int `json:"comment_count" dc:"评论次数"`
|
||||
IsPublished int `json:"is_published" dc:"是否发布 0:草稿 1:已发布"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐 0:否 1:是"`
|
||||
IsTop int `json:"is_top" dc:"是否置顶 0:否 1:是"`
|
||||
SeoTitle string `json:"seo_title" dc:"SEO标题"`
|
||||
SeoDescription string `json:"seo_description" dc:"SEO描述"`
|
||||
SeoKeywords string `json:"seo_keywords" dc:"SEO关键词"`
|
||||
PublishedAt *gtime.Time `json:"published_at" dc:"发布时间"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// CreateArticleRequest 创建文章请求
|
||||
type CreateArticleRequest struct {
|
||||
Title string `json:"title" v:"required|length:1,255#标题不能为空|标题长度不能超过255字符" dc:"标题"`
|
||||
Slug string `json:"slug" v:"length:0,255#URL别名长度不能超过255字符" dc:"URL别名"`
|
||||
Summary string `json:"summary" dc:"摘要"`
|
||||
Content string `json:"content" v:"required#内容不能为空" dc:"内容(Markdown)"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
CategoryId int `json:"category_id" dc:"分类ID"`
|
||||
Tags []string `json:"tags" dc:"标签"`
|
||||
AuthorId int `json:"author_id" dc:"作者ID"`
|
||||
IsPublished int `json:"is_published" dc:"是否发布"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐"`
|
||||
IsTop int `json:"is_top" dc:"是否置顶"`
|
||||
SeoTitle string `json:"seo_title" dc:"SEO标题"`
|
||||
SeoDescription string `json:"seo_description" dc:"SEO描述"`
|
||||
SeoKeywords string `json:"seo_keywords" dc:"SEO关键词"`
|
||||
}
|
||||
|
||||
// UpdateArticleRequest 更新文章请求
|
||||
type UpdateArticleRequest struct {
|
||||
Title string `json:"title" v:"required|length:1,255#标题不能为空|标题长度不能超过255字符" dc:"标题"`
|
||||
Slug string `json:"slug" v:"required|length:1,255#URL别名不能为空|URL别名长度不能超过255字符" dc:"URL别名"`
|
||||
Summary string `json:"summary" v:"required#摘要不能为空" dc:"摘要"`
|
||||
Content string `json:"content" v:"required#内容不能为空" dc:"内容"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
CategoryId int `json:"category_id" dc:"分类ID"`
|
||||
Tags []string `json:"tags" dc:"标签"`
|
||||
IsPublished int `json:"is_published" v:"in:0,1#发布状态只能为0或1" dc:"是否发布"`
|
||||
IsFeatured int `json:"is_featured" v:"in:0,1#推荐状态只能为0或1" dc:"是否推荐"`
|
||||
IsTop int `json:"is_top" v:"in:0,1#置顶状态只能为0或1" dc:"是否置顶"`
|
||||
SeoTitle string `json:"seo_title" dc:"SEO标题"`
|
||||
SeoDescription string `json:"seo_description" dc:"SEO描述"`
|
||||
SeoKeywords string `json:"seo_keywords" dc:"SEO关键词"`
|
||||
}
|
||||
40
internal/model/attachment.go
Normal file
40
internal/model/attachment.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Attachment 附件模型
|
||||
type Attachment struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
OriginalName string `json:"original_name" dc:"原始文件名"`
|
||||
FileName string `json:"file_name" dc:"存储文件名"`
|
||||
FilePath string `json:"file_path" dc:"文件路径"`
|
||||
FileUrl string `json:"file_url" dc:"文件URL"`
|
||||
FileSize int64 `json:"file_size" dc:"文件大小(字节)"`
|
||||
FileType string `json:"file_type" dc:"文件类型"`
|
||||
MimeType string `json:"mime_type" dc:"MIME类型"`
|
||||
FileExt string `json:"file_ext" dc:"文件扩展名"`
|
||||
StorageType string `json:"storage_type" dc:"存储类型 local:本地 oss:阿里云OSS"`
|
||||
UploadIp string `json:"upload_ip" dc:"上传IP"`
|
||||
UploadedBy int `json:"uploaded_by" dc:"上传者ID"`
|
||||
UsageCount int `json:"usage_count" dc:"使用次数"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// UploadRequest 上传请求
|
||||
type UploadRequest struct {
|
||||
FileType string `json:"file_type" dc:"文件类型"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
}
|
||||
|
||||
// UploadResponse 上传响应
|
||||
type UploadResponse struct {
|
||||
Id int `json:"id" dc:"附件ID"`
|
||||
FileName string `json:"file_name" dc:"文件名"`
|
||||
FileUrl string `json:"file_url" dc:"文件URL"`
|
||||
FileSize int64 `json:"file_size" dc:"文件大小"`
|
||||
FileType string `json:"file_type" dc:"文件类型"`
|
||||
}
|
||||
82
internal/model/common.go
Normal file
82
internal/model/common.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package model
|
||||
|
||||
// 通用响应结构
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// 分页响应结构
|
||||
type PageResponse struct {
|
||||
List interface{} `json:"list"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// 通用列表请求
|
||||
type ListRequest struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
PageSize int `json:"page_size" form:"page_size"`
|
||||
Keyword string `json:"keyword" form:"keyword"`
|
||||
Status int `json:"status" form:"status"`
|
||||
}
|
||||
|
||||
|
||||
// 文章列表请求
|
||||
type ArticleListRequest struct {
|
||||
ListRequest
|
||||
CategoryId int `json:"category_id" form:"category_id"`
|
||||
AuthorId int `json:"author_id" form:"author_id"`
|
||||
IsPublished int `json:"is_published" form:"is_published"`
|
||||
IsFeatured int `json:"is_featured" form:"is_featured"`
|
||||
IsTop int `json:"is_top" form:"is_top"`
|
||||
StartDate string `json:"start_date" form:"start_date"`
|
||||
EndDate string `json:"end_date" form:"end_date"`
|
||||
}
|
||||
|
||||
// 附件列表请求
|
||||
type AttachmentListRequest struct {
|
||||
ListRequest
|
||||
FileType string `json:"file_type" form:"file_type"`
|
||||
UploadedBy int `json:"uploaded_by" form:"uploaded_by"`
|
||||
StartDate string `json:"start_date" form:"start_date"`
|
||||
EndDate string `json:"end_date" form:"end_date"`
|
||||
StorageType string `json:"storage_type" form:"storage_type"`
|
||||
}
|
||||
|
||||
// 新闻列表请求
|
||||
type NewsListRequest struct {
|
||||
ListRequest
|
||||
Category string `json:"category" form:"category"`
|
||||
Author string `json:"author" form:"author"`
|
||||
Source string `json:"source" form:"source"`
|
||||
IsPublished int `json:"is_published" form:"is_published"`
|
||||
IsFeatured int `json:"is_featured" form:"is_featured"`
|
||||
IsTop int `json:"is_top" form:"is_top"`
|
||||
StartDate string `json:"start_date" form:"start_date"`
|
||||
EndDate string `json:"end_date" form:"end_date"`
|
||||
}
|
||||
|
||||
// 合作伙伴列表请求
|
||||
type PartnerListRequest struct {
|
||||
ListRequest
|
||||
Category string `json:"category" form:"category"`
|
||||
IsFeatured int `json:"is_featured" form:"is_featured"`
|
||||
}
|
||||
|
||||
// 联系我们列表请求
|
||||
type ContactListRequest struct {
|
||||
ListRequest
|
||||
StartDate string `json:"start_date" form:"start_date"`
|
||||
EndDate string `json:"end_date" form:"end_date"`
|
||||
}
|
||||
|
||||
// 配置列表请求
|
||||
type ConfigListRequest struct {
|
||||
ListRequest
|
||||
GroupName string `json:"group_name" form:"group_name"`
|
||||
IsSystem int `json:"is_system" form:"is_system"`
|
||||
}
|
||||
48
internal/model/config.go
Normal file
48
internal/model/config.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Config 配置模型
|
||||
type Config struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
ConfigKey string `json:"config_key" dc:"配置键"`
|
||||
ConfigValue string `json:"config_value" dc:"配置值"`
|
||||
ConfigType string `json:"config_type" dc:"配置类型 string:字符串 int:整数 bool:布尔 json:JSON"`
|
||||
GroupName string `json:"group_name" dc:"分组名称"`
|
||||
Description string `json:"description" dc:"配置描述"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsSystem int `json:"is_system" dc:"是否系统配置 0:否 1:是"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// SiteConfig 站点配置别名
|
||||
type SiteConfig = Config
|
||||
|
||||
// CreateConfigRequest 创建配置请求
|
||||
type CreateConfigRequest struct {
|
||||
ConfigKey string `json:"config_key" v:"required|length:1,100#配置键不能为空|配置键长度不能超过100字符" dc:"配置键"`
|
||||
ConfigValue string `json:"config_value" dc:"配置值"`
|
||||
ConfigType string `json:"config_type" dc:"配置类型"`
|
||||
GroupName string `json:"group_name" dc:"分组名称"`
|
||||
Description string `json:"description" dc:"配置描述"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsSystem int `json:"is_system" dc:"是否系统配置"`
|
||||
}
|
||||
|
||||
// UpdateConfigRequest 更新配置请求
|
||||
type UpdateConfigRequest struct {
|
||||
ConfigValue string `json:"config_value" dc:"配置值"`
|
||||
ConfigType string `json:"config_type" dc:"配置类型"`
|
||||
GroupName string `json:"group_name" dc:"分组名称"`
|
||||
Description string `json:"description" dc:"配置描述"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsSystem int `json:"is_system" dc:"是否系统配置"`
|
||||
}
|
||||
|
||||
// BatchUpdateConfigRequest 批量更新配置请求
|
||||
type BatchUpdateConfigRequest struct {
|
||||
Configs map[string]string `json:"configs" v:"required#配置不能为空" dc:"配置键值对"`
|
||||
}
|
||||
45
internal/model/contact.go
Normal file
45
internal/model/contact.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Contact 联系我们模型
|
||||
type Contact struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Name string `json:"name" dc:"姓名"`
|
||||
Email string `json:"email" dc:"邮箱"`
|
||||
Phone string `json:"phone" dc:"电话"`
|
||||
Company string `json:"company" dc:"公司"`
|
||||
Subject string `json:"subject" dc:"主题"`
|
||||
Message string `json:"message" dc:"留言内容"`
|
||||
Ip string `json:"ip" dc:"IP地址"`
|
||||
UserAgent string `json:"user_agent" dc:"用户代理"`
|
||||
Status int `json:"status" dc:"状态 0:未处理 1:已处理 2:已回复"`
|
||||
Reply string `json:"reply" dc:"回复内容"`
|
||||
RepliedAt *gtime.Time `json:"replied_at" dc:"回复时间"`
|
||||
RepliedBy int `json:"replied_by" dc:"回复人ID"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// CreateContactRequest 创建联系信息请求
|
||||
type CreateContactRequest struct {
|
||||
Name string `json:"name" v:"required|length:1,50#姓名不能为空|姓名长度不能超过50字符" dc:"姓名"`
|
||||
Email string `json:"email" v:"required|email#邮箱不能为空|邮箱格式不正确" dc:"邮箱"`
|
||||
Phone string `json:"phone" dc:"电话"`
|
||||
Company string `json:"company" dc:"公司"`
|
||||
Subject string `json:"subject" v:"required|length:1,200#主题不能为空|主题长度不能超过200字符" dc:"主题"`
|
||||
Message string `json:"message" v:"required|length:1,2000#留言内容不能为空|留言内容长度不能超过2000字符" dc:"留言内容"`
|
||||
}
|
||||
|
||||
// ReplyContactRequest 回复联系信息请求
|
||||
type ReplyContactRequest struct {
|
||||
Reply string `json:"reply" v:"required|length:1,2000#回复内容不能为空|回复内容长度不能超过2000字符" dc:"回复内容"`
|
||||
}
|
||||
|
||||
// ContactTrend 联系信息趋势数据
|
||||
type ContactTrend struct {
|
||||
Date string `json:"date" dc:"日期"`
|
||||
Count int `json:"count" dc:"数量"`
|
||||
}
|
||||
0
internal/model/do/.gitkeep
Normal file
0
internal/model/do/.gitkeep
Normal file
0
internal/model/entity/.gitkeep
Normal file
0
internal/model/entity/.gitkeep
Normal file
23
internal/model/entity/admin.go
Normal file
23
internal/model/entity/admin.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Admin 管理员实体
|
||||
type Admin struct {
|
||||
Id uint64 `json:"id" orm:"id,primary"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Password string `json:"-" orm:"password"`
|
||||
RealName string `json:"real_name" orm:"real_name"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
Phone string `json:"phone" orm:"phone"`
|
||||
Avatar string `json:"avatar" orm:"avatar"`
|
||||
RoleId uint64 `json:"role_id" orm:"role_id"`
|
||||
Status uint8 `json:"status" orm:"status"`
|
||||
LastLoginAt *time.Time `json:"last_login_at" orm:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip" orm:"last_login_ip"`
|
||||
CreatedAt *time.Time `json:"created_at" orm:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" orm:"updated_at"`
|
||||
DeletedAt uint64 `json:"deleted_at" orm:"deleted_at"`
|
||||
}
|
||||
56
internal/model/news.go
Normal file
56
internal/model/news.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// News 新闻模型
|
||||
type News struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Title string `json:"title" dc:"标题"`
|
||||
Slug string `json:"slug" dc:"URL别名"`
|
||||
Summary string `json:"summary" dc:"摘要"`
|
||||
Content string `json:"content" dc:"内容"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
Source string `json:"source" dc:"来源"`
|
||||
Author string `json:"author" dc:"作者"`
|
||||
ViewCount int `json:"view_count" dc:"浏览次数"`
|
||||
IsPublished int `json:"is_published" dc:"是否发布 0:草稿 1:已发布"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐 0:否 1:是"`
|
||||
IsTop int `json:"is_top" dc:"是否置顶 0:否 1:是"`
|
||||
PublishedAt *gtime.Time `json:"published_at" dc:"发布时间"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// CreateNewsRequest 创建新闻请求
|
||||
type CreateNewsRequest struct {
|
||||
Title string `json:"title" v:"required|length:1,255#标题不能为空|标题长度不能超过255字符" dc:"标题"`
|
||||
Slug string `json:"slug" v:"length:0,255#URL别名长度不能超过255字符" dc:"URL别名"`
|
||||
Summary string `json:"summary" dc:"摘要"`
|
||||
Content string `json:"content" v:"required#内容不能为空" dc:"内容"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
Source string `json:"source" dc:"来源"`
|
||||
Author string `json:"author" dc:"作者"`
|
||||
IsPublished int `json:"is_published" dc:"是否发布"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐"`
|
||||
IsTop int `json:"is_top" dc:"是否置顶"`
|
||||
}
|
||||
|
||||
// UpdateNewsRequest 更新新闻请求
|
||||
type UpdateNewsRequest struct {
|
||||
Title string `json:"title" v:"required|length:1,255#标题不能为空|标题长度不能超过255字符" dc:"标题"`
|
||||
Slug string `json:"slug" v:"length:0,255#URL别名长度不能超过255字符" dc:"URL别名"`
|
||||
Summary string `json:"summary" dc:"摘要"`
|
||||
Content string `json:"content" v:"required#内容不能为空" dc:"内容"`
|
||||
CoverImage string `json:"cover_image" dc:"封面图片"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
Source string `json:"source" dc:"来源"`
|
||||
Author string `json:"author" dc:"作者"`
|
||||
IsPublished int `json:"is_published" dc:"是否发布"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐"`
|
||||
IsTop int `json:"is_top" dc:"是否置顶"`
|
||||
}
|
||||
45
internal/model/partner.go
Normal file
45
internal/model/partner.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Partner 合作伙伴模型
|
||||
type Partner struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Name string `json:"name" dc:"合作伙伴名称"`
|
||||
Logo string `json:"logo" dc:"Logo图片"`
|
||||
Website string `json:"website" dc:"官网地址"`
|
||||
Description string `json:"description" dc:"描述"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐 0:否 1:是"`
|
||||
Status int `json:"status" dc:"状态 0:禁用 1:启用"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// CreatePartnerRequest 创建合作伙伴请求
|
||||
type CreatePartnerRequest struct {
|
||||
Name string `json:"name" v:"required|length:1,100#合作伙伴名称不能为空|名称长度不能超过100字符" dc:"合作伙伴名称"`
|
||||
Logo string `json:"logo" dc:"Logo图片"`
|
||||
Website string `json:"website" dc:"官网地址"`
|
||||
Description string `json:"description" dc:"描述"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
}
|
||||
|
||||
// UpdatePartnerRequest 更新合作伙伴请求
|
||||
type UpdatePartnerRequest struct {
|
||||
Name string `json:"name" v:"required|length:1,100#合作伙伴名称不能为空|名称长度不能超过100字符" dc:"合作伙伴名称"`
|
||||
Logo string `json:"logo" dc:"Logo图片"`
|
||||
Website string `json:"website" dc:"官网地址"`
|
||||
Description string `json:"description" dc:"描述"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
SortOrder int `json:"sort_order" dc:"排序"`
|
||||
IsFeatured int `json:"is_featured" dc:"是否推荐"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
}
|
||||
17
internal/model/permission.go
Normal file
17
internal/model/permission.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
// Permission 权限模型
|
||||
type Permission struct {
|
||||
Key string `json:"key" dc:"权限键"`
|
||||
Name string `json:"name" dc:"权限名称"`
|
||||
Description string `json:"description" dc:"权限描述"`
|
||||
Group string `json:"group" dc:"权限分组"`
|
||||
}
|
||||
|
||||
// PermissionGroup 权限分组模型
|
||||
type PermissionGroup struct {
|
||||
Key string `json:"key" dc:"分组键"`
|
||||
Name string `json:"name" dc:"分组名称"`
|
||||
Description string `json:"description" dc:"分组描述"`
|
||||
Permissions []Permission `json:"permissions" dc:"权限列表"`
|
||||
}
|
||||
41
internal/model/role.go
Normal file
41
internal/model/role.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Role 角色模型
|
||||
type Role struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Name string `json:"name" dc:"角色名称"`
|
||||
Description string `json:"description" dc:"角色描述"`
|
||||
Permissions interface{} `json:"permissions" dc:"权限列表"`
|
||||
Status int `json:"status" dc:"状态 0:禁用 1:启用"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// RoleListRequest 角色列表请求
|
||||
type RoleListRequest struct {
|
||||
Page int `json:"page" dc:"页码"`
|
||||
PageSize int `json:"page_size" dc:"每页数量"`
|
||||
Keyword string `json:"keyword" dc:"关键词"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
}
|
||||
|
||||
// CreateRoleRequest 创建角色请求
|
||||
type CreateRoleRequest struct {
|
||||
Name string `json:"name" v:"required|length:2,32#角色名称不能为空|角色名称长度为2-32位" dc:"角色名称"`
|
||||
Description string `json:"description" dc:"角色描述"`
|
||||
Permissions []string `json:"permissions" dc:"权限列表"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
}
|
||||
|
||||
// UpdateRoleRequest 更新角色请求
|
||||
type UpdateRoleRequest struct {
|
||||
Name string `json:"name" v:"required|length:2,32#角色名称不能为空|角色名称长度为2-32位" dc:"角色名称"`
|
||||
Description string `json:"description" dc:"角色描述"`
|
||||
Permissions []string `json:"permissions" dc:"权限列表"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
}
|
||||
83
internal/model/user.go
Normal file
83
internal/model/user.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// User 用户模型
|
||||
type User struct {
|
||||
Id int `json:"id" dc:"主键"`
|
||||
Account string `json:"account" dc:"账号"`
|
||||
NickName string `json:"nick_name" dc:"昵称"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Email string `json:"email" dc:"邮箱"`
|
||||
Password string `json:"-" dc:"登录密码"`
|
||||
Balance float64 `json:"balance" dc:"余额"`
|
||||
QrCode string `json:"qr_code" dc:"微信二维码地址"`
|
||||
RoleId int `json:"role_id" dc:"角色"`
|
||||
IsSysNotifications int `json:"is_sys_notifications" dc:"是否接受邮件系统通知"`
|
||||
IsCollectionNotifications int `json:"is_collection_notifications" dc:"是否接受收藏的项目更新信息"`
|
||||
IsMarketingNotifications int `json:"is_marketing_notifications" dc:"是否接受营销信息"`
|
||||
Ip string `json:"ip" dc:"本次登录ip"`
|
||||
IpTable interface{} `json:"ip_table" dc:"常用登录IP地址列表"`
|
||||
Status int `json:"status" dc:"状态 0:正常 1:冻结 2:封号 3:注销"`
|
||||
LastResetPasswordAt int `json:"last_reset_password_at" dc:"最后一次修改密码的时间"`
|
||||
CreatedAt *gtime.Time `json:"created_at" dc:"注册时间"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" dc:"编辑时间"`
|
||||
DeletedAt int `json:"deleted_at" dc:"删除时间"`
|
||||
}
|
||||
|
||||
// UserListRequest 用户列表请求
|
||||
type UserListRequest struct {
|
||||
Page int `json:"page" dc:"页码"`
|
||||
PageSize int `json:"page_size" dc:"每页数量"`
|
||||
Keyword string `json:"keyword" dc:"关键词"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
RoleId int `json:"role_id" dc:"角色ID"`
|
||||
}
|
||||
|
||||
// CreateUserRequest 创建用户请求
|
||||
type CreateUserRequest struct {
|
||||
Account string `json:"account" v:"required|length:3,32#账号不能为空|账号长度为3-32位" dc:"账号"`
|
||||
NickName string `json:"nick_name" v:"required|length:2,64#昵称不能为空|昵称长度为2-64位" dc:"昵称"`
|
||||
Email string `json:"email" v:"required|email#邮箱不能为空|邮箱格式不正确" dc:"邮箱"`
|
||||
Password string `json:"password" v:"required|length:6,32#密码不能为空|密码长度为6-32位" dc:"密码"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Balance float64 `json:"balance" dc:"余额"`
|
||||
RoleId int `json:"role_id" dc:"角色ID"`
|
||||
IsSysNotifications int `json:"is_sys_notifications" dc:"是否接受邮件系统通知"`
|
||||
IsCollectionNotifications int `json:"is_collection_notifications" dc:"是否接受收藏的项目更新信息"`
|
||||
IsMarketingNotifications int `json:"is_marketing_notifications" dc:"是否接受营销信息"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest 更新用户请求
|
||||
type UpdateUserRequest struct {
|
||||
NickName string `json:"nick_name" v:"required|length:2,64#昵称不能为空|昵称长度为2-64位" dc:"昵称"`
|
||||
Email string `json:"email" v:"required|email#邮箱不能为空|邮箱格式不正确" dc:"邮箱"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Balance float64 `json:"balance" dc:"余额"`
|
||||
RoleId int `json:"role_id" dc:"角色ID"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
IsSysNotifications int `json:"is_sys_notifications" dc:"是否接受邮件系统通知"`
|
||||
IsCollectionNotifications int `json:"is_collection_notifications" dc:"是否接受收藏的项目更新信息"`
|
||||
IsMarketingNotifications int `json:"is_marketing_notifications" dc:"是否接受营销信息"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" v:"required#原密码不能为空" dc:"原密码"`
|
||||
NewPassword string `json:"new_password" v:"required|length:6,32#新密码不能为空|新密码长度为6-32位" dc:"新密码"`
|
||||
}
|
||||
|
||||
// UserLoginRequest 用户登录请求
|
||||
type UserLoginRequest struct {
|
||||
Account string `json:"account" v:"required#账号不能为空" dc:"账号"`
|
||||
Password string `json:"password" v:"required#密码不能为空" dc:"密码"`
|
||||
}
|
||||
|
||||
// UserLoginResponse 用户登录响应
|
||||
type UserLoginResponse struct {
|
||||
Token string `json:"token" dc:"访问令牌"`
|
||||
ExpiresIn int `json:"expires_in" dc:"过期时间(秒)"`
|
||||
User *User `json:"user" dc:"用户信息"`
|
||||
}
|
||||
0
internal/service/.gitkeep
Normal file
0
internal/service/.gitkeep
Normal file
251
internal/service/admin.go
Normal file
251
internal/service/admin.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAdmin struct{}
|
||||
|
||||
func Admin() *sAdmin {
|
||||
return &sAdmin{}
|
||||
}
|
||||
|
||||
// Login 管理员登录
|
||||
func (s *sAdmin) Login(ctx context.Context, req *model.LoginRequest) (*model.LoginResponse, error) {
|
||||
// 查询管理员
|
||||
admin := &model.Admin{}
|
||||
err := dao.Admin.Ctx(ctx).Where("username", req.Username).Where("deleted_at", 0).Scan(admin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if admin.Id == 0 {
|
||||
return nil, gerror.New("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if !s.VerifyPassword(req.Password, admin.Password) {
|
||||
return nil, gerror.New("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 检查状态
|
||||
if admin.Status != 1 {
|
||||
return nil, gerror.New("账号已被禁用")
|
||||
}
|
||||
|
||||
// 更新登录信息
|
||||
_, err = dao.Admin.Ctx(ctx).Where("id", admin.Id).Update(g.Map{
|
||||
"last_login_at": gtime.Now().Unix(),
|
||||
"last_login_ip": g.RequestFromCtx(ctx).GetClientIp(),
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "更新登录信息失败:", err)
|
||||
}
|
||||
|
||||
// 生成token
|
||||
token, err := Auth.GenerateToken(ctx, uint64(admin.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 清除密码字段
|
||||
admin.Password = ""
|
||||
|
||||
return &model.LoginResponse{
|
||||
Token: token,
|
||||
Admin: admin,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetById 根据ID获取管理员信息
|
||||
func (s *sAdmin) GetById(ctx context.Context, id int) (*model.Admin, error) {
|
||||
admin := &model.Admin{}
|
||||
err := dao.Admin.Ctx(ctx).Where("id", id).Where("deleted_at", 0).Scan(admin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if admin.Id == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// 清除密码字段
|
||||
admin.Password = ""
|
||||
return admin, nil
|
||||
}
|
||||
|
||||
// List 获取管理员列表
|
||||
func (s *sAdmin) List(ctx context.Context, req *model.ListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
keyword = req.Keyword
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
query := dao.Admin.Ctx(ctx).Where("deleted_at", 0)
|
||||
|
||||
// 关键词搜索
|
||||
if keyword != "" {
|
||||
query = query.WhereOr("username LIKE ?", "%"+keyword+"%").
|
||||
WhereOr("real_name LIKE ?", "%"+keyword+"%").
|
||||
WhereOr("email LIKE ?", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if req.Status >= 0 {
|
||||
query = query.Where("status", req.Status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
total, err := query.Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
var admins []*model.Admin
|
||||
err = query.Page(page, pageSize).OrderDesc("id").Scan(&admins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 清除密码字段
|
||||
for _, admin := range admins {
|
||||
admin.Password = ""
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: admins,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建管理员
|
||||
func (s *sAdmin) Create(ctx context.Context, req *model.CreateAdminRequest) error {
|
||||
// 检查用户名是否存在
|
||||
count, err := dao.Admin.Ctx(ctx).Where("username", req.Username).Where("deleted_at", 0).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("用户名已存在")
|
||||
}
|
||||
|
||||
// 检查邮箱是否存在
|
||||
count, err = dao.Admin.Ctx(ctx).Where("email", req.Email).Where("deleted_at", 0).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("邮箱已存在")
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
hashedPassword := s.HashPassword(req.Password)
|
||||
|
||||
// 创建管理员
|
||||
_, err = dao.Admin.Ctx(ctx).Insert(g.Map{
|
||||
"username": req.Username,
|
||||
"password": hashedPassword,
|
||||
"real_name": req.RealName,
|
||||
"email": req.Email,
|
||||
"phone": req.Phone,
|
||||
"role_id": req.RoleId,
|
||||
"status": 1,
|
||||
"created_at": gtime.Now(),
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新管理员
|
||||
func (s *sAdmin) Update(ctx context.Context, id int, req *model.UpdateAdminRequest) error {
|
||||
// 检查管理员是否存在
|
||||
admin, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if admin == nil {
|
||||
return gerror.New("管理员不存在")
|
||||
}
|
||||
|
||||
// 检查邮箱是否被其他管理员使用
|
||||
count, err := dao.Admin.Ctx(ctx).Where("email", req.Email).Where("id !=", id).Where("deleted_at", 0).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("邮箱已被使用")
|
||||
}
|
||||
|
||||
// 更新管理员
|
||||
_, err = dao.Admin.Ctx(ctx).Where("id", id).Update(g.Map{
|
||||
"real_name": req.RealName,
|
||||
"email": req.Email,
|
||||
"phone": req.Phone,
|
||||
"role_id": req.RoleId,
|
||||
"status": req.Status,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除管理员
|
||||
func (s *sAdmin) Delete(ctx context.Context, id int) error {
|
||||
// 检查管理员是否存在
|
||||
admin, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if admin == nil {
|
||||
return gerror.New("管理员不存在")
|
||||
}
|
||||
|
||||
// 软删除
|
||||
_, err = dao.Admin.Ctx(ctx).Where("id", id).Update(g.Map{
|
||||
"deleted_at": gtime.Now().Unix(),
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// HashPassword 加密密码
|
||||
func (s *sAdmin) HashPassword(password string) string {
|
||||
return gmd5.MustEncrypt(password + "cms_salt_2024")
|
||||
}
|
||||
|
||||
// VerifyPassword 验证密码
|
||||
func (s *sAdmin) VerifyPassword(password, hashedPassword string) bool {
|
||||
return gmd5.MustEncrypt(password+"cms_salt_2024") == hashedPassword
|
||||
}
|
||||
|
||||
// Profile 获取当前管理员信息
|
||||
func (s *sAdmin) Profile(ctx context.Context) (*model.Admin, error) {
|
||||
adminId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id"))
|
||||
return s.GetById(ctx, adminId)
|
||||
}
|
||||
|
||||
// UpdateProfile 更新当前管理员信息
|
||||
func (s *sAdmin) UpdateProfile(ctx context.Context, req *model.UpdateAdminRequest) error {
|
||||
adminId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id"))
|
||||
return s.Update(ctx, adminId, req)
|
||||
}
|
||||
355
internal/service/article.go
Normal file
355
internal/service/article.go
Normal file
@@ -0,0 +1,355 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sArticle struct{}
|
||||
|
||||
func Article() *sArticle {
|
||||
return &sArticle{}
|
||||
}
|
||||
|
||||
// GetById 根据ID获取文章信息
|
||||
func (s *sArticle) GetById(ctx context.Context, id int) (*model.Article, error) {
|
||||
article, err := dao.Article.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if article != nil {
|
||||
// 增加浏览次数
|
||||
go func() {
|
||||
dao.Article.IncrementViewCount(context.Background(), id)
|
||||
}()
|
||||
}
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// GetBySlug 根据URL别名获取文章
|
||||
func (s *sArticle) GetBySlug(ctx context.Context, slug string) (*model.Article, error) {
|
||||
article, err := dao.Article.GetBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if article != nil {
|
||||
// 增加浏览次数
|
||||
go func() {
|
||||
dao.Article.IncrementViewCount(context.Background(), article.Id)
|
||||
}()
|
||||
}
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// List 获取文章列表
|
||||
func (s *sArticle) List(ctx context.Context, req *model.ArticleListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
articles, total, err := dao.Article.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: articles,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建文章
|
||||
func (s *sArticle) Create(ctx context.Context, req *model.CreateArticleRequest) error {
|
||||
// 检查URL别名是否存在
|
||||
if req.Slug != "" {
|
||||
existArticle, err := dao.Article.GetBySlug(ctx, req.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existArticle != nil {
|
||||
return gerror.New("URL别名已存在")
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供slug,则根据标题生成
|
||||
req.Slug = s.generateSlug(req.Title)
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
authorId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id"))
|
||||
if authorId == 0 {
|
||||
authorId = gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("user_id"))
|
||||
}
|
||||
|
||||
// 处理HTML内容(如果是Markdown,需要转换)
|
||||
htmlContent := req.Content
|
||||
if req.Content != "" {
|
||||
// 这里可以添加Markdown到HTML的转换逻辑
|
||||
htmlContent = s.markdownToHTML(req.Content)
|
||||
}
|
||||
|
||||
// 创建文章
|
||||
article := &model.Article{
|
||||
Title: req.Title,
|
||||
Slug: req.Slug,
|
||||
Summary: req.Summary,
|
||||
Content: req.Content,
|
||||
HtmlContent: htmlContent,
|
||||
CoverImage: req.CoverImage,
|
||||
CategoryId: req.CategoryId,
|
||||
Tags: req.Tags,
|
||||
AuthorId: authorId,
|
||||
IsPublished: req.IsPublished,
|
||||
IsFeatured: req.IsFeatured,
|
||||
IsTop: req.IsTop,
|
||||
SeoTitle: req.SeoTitle,
|
||||
SeoDescription: req.SeoDescription,
|
||||
SeoKeywords: req.SeoKeywords,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
if req.IsPublished == 1 {
|
||||
article.PublishedAt = gtime.Now()
|
||||
}
|
||||
|
||||
_, err := dao.Article.Create(ctx, article)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新文章
|
||||
func (s *sArticle) Update(ctx context.Context, id int, req *model.UpdateArticleRequest) error {
|
||||
// 检查文章是否存在
|
||||
article, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if article == nil {
|
||||
return gerror.New("文章不存在")
|
||||
}
|
||||
|
||||
// 检查URL别名是否被其他文章使用
|
||||
if req.Slug != "" && req.Slug != article.Slug {
|
||||
existArticle, err := dao.Article.GetBySlug(ctx, req.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existArticle != nil && existArticle.Id != id {
|
||||
return gerror.New("URL别名已被使用")
|
||||
}
|
||||
}
|
||||
|
||||
// 处理HTML内容
|
||||
htmlContent := req.Content
|
||||
if req.Content != "" {
|
||||
htmlContent = s.markdownToHTML(req.Content)
|
||||
}
|
||||
|
||||
// 更新文章
|
||||
updateData := g.Map{
|
||||
"title": req.Title,
|
||||
"slug": req.Slug,
|
||||
"summary": req.Summary,
|
||||
"content": req.Content,
|
||||
"html_content": htmlContent,
|
||||
"cover_image": req.CoverImage,
|
||||
"category_id": req.CategoryId,
|
||||
"tags": req.Tags,
|
||||
"is_published": req.IsPublished,
|
||||
"is_featured": req.IsFeatured,
|
||||
"is_top": req.IsTop,
|
||||
"seo_title": req.SeoTitle,
|
||||
"seo_description": req.SeoDescription,
|
||||
"seo_keywords": req.SeoKeywords,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
// 如果从草稿变为发布状态,设置发布时间
|
||||
if req.IsPublished == 1 && article.IsPublished == 0 {
|
||||
updateData["published_at"] = gtime.Now()
|
||||
}
|
||||
|
||||
return dao.Article.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除文章
|
||||
func (s *sArticle) Delete(ctx context.Context, id int) error {
|
||||
// 检查文章是否存在
|
||||
article, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if article == nil {
|
||||
return gerror.New("文章不存在")
|
||||
}
|
||||
|
||||
return dao.Article.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新文章状态
|
||||
func (s *sArticle) UpdateStatus(ctx context.Context, id int, isPublished int) error {
|
||||
// 检查文章是否存在
|
||||
article, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if article == nil {
|
||||
return gerror.New("文章不存在")
|
||||
}
|
||||
|
||||
updateData := g.Map{
|
||||
"is_published": isPublished,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
// 如果是发布状态,设置发布时间
|
||||
if isPublished == 1 && article.IsPublished == 0 {
|
||||
updateData["published_at"] = gtime.Now()
|
||||
}
|
||||
|
||||
return dao.Article.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// SetFeatured 设置推荐状态
|
||||
func (s *sArticle) SetFeatured(ctx context.Context, id int, isFeatured int) error {
|
||||
// 检查文章是否存在
|
||||
article, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if article == nil {
|
||||
return gerror.New("文章不存在")
|
||||
}
|
||||
|
||||
return dao.Article.Update(ctx, id, g.Map{
|
||||
"is_featured": isFeatured,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// SetTop 设置置顶状态
|
||||
func (s *sArticle) SetTop(ctx context.Context, id int, isTop int) error {
|
||||
// 检查文章是否存在
|
||||
article, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if article == nil {
|
||||
return gerror.New("文章不存在")
|
||||
}
|
||||
|
||||
return dao.Article.Update(ctx, id, g.Map{
|
||||
"is_top": isTop,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats 获取文章统计信息
|
||||
func (s *sArticle) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总文章数
|
||||
totalCount, err := dao.Article.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取已发布文章数
|
||||
publishedCount, err := dao.Article.GetPublishedCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取草稿数
|
||||
draftCount := totalCount - publishedCount
|
||||
|
||||
// 获取推荐文章数
|
||||
featuredCount, err := dao.Article.GetFeaturedCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"published_count": publishedCount,
|
||||
"draft_count": draftCount,
|
||||
"featured_count": featuredCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐文章
|
||||
func (s *sArticle) GetFeatured(ctx context.Context, limit int) ([]*model.Article, error) {
|
||||
if limit <= 0 {
|
||||
limit = 5
|
||||
}
|
||||
return dao.Article.GetFeatured(ctx, limit)
|
||||
}
|
||||
|
||||
// GetLatest 获取最新文章
|
||||
func (s *sArticle) GetLatest(ctx context.Context, limit int) ([]*model.Article, error) {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
return dao.Article.GetLatest(ctx, limit)
|
||||
}
|
||||
|
||||
// generateSlug 生成URL别名
|
||||
func (s *sArticle) generateSlug(title string) string {
|
||||
// 简单的slug生成逻辑,实际项目中可能需要更复杂的处理
|
||||
slug := strings.ToLower(title)
|
||||
slug = strings.ReplaceAll(slug, " ", "-")
|
||||
slug = strings.ReplaceAll(slug, " ", "-") // 全角空格
|
||||
// 移除特殊字符,只保留字母、数字、中文和连字符
|
||||
// 这里简化处理,实际项目中可能需要更完善的slug生成逻辑
|
||||
return slug
|
||||
}
|
||||
|
||||
// markdownToHTML 将Markdown转换为HTML
|
||||
func (s *sArticle) markdownToHTML(markdown string) string {
|
||||
// 这里应该使用Markdown解析库,如github.com/russross/blackfriday
|
||||
// 暂时直接返回原内容,实际项目中需要实现Markdown解析
|
||||
return markdown
|
||||
}
|
||||
|
||||
// Search 搜索文章
|
||||
func (s *sArticle) Search(ctx context.Context, keyword string, page, pageSize int) (*model.PageResponse, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
articles, total, err := dao.Article.Search(ctx, keyword, page, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: articles,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
353
internal/service/attachment.go
Normal file
353
internal/service/attachment.go
Normal file
@@ -0,0 +1,353 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
type sAttachment struct{}
|
||||
|
||||
func Attachment() *sAttachment {
|
||||
return &sAttachment{}
|
||||
}
|
||||
|
||||
// Upload 上传文件
|
||||
func (s *sAttachment) Upload(ctx context.Context, file *ghttp.UploadFile, uploadType string) (*model.Attachment, error) {
|
||||
// 检查文件大小
|
||||
maxSize := int64(10 * 1024 * 1024) // 10MB
|
||||
if file.Size > maxSize {
|
||||
return nil, gerror.New("文件大小不能超过10MB")
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
if !s.isAllowedFileType(file.Filename) {
|
||||
return nil, gerror.New("不支持的文件类型")
|
||||
}
|
||||
|
||||
// 生成文件名和路径
|
||||
fileName, filePath, fileUrl := s.generateFilePath(file.Filename, uploadType)
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(filePath)
|
||||
if !gfile.Exists(dir) {
|
||||
if err := gfile.Mkdir(dir); err != nil {
|
||||
return nil, gerror.New("创建目录失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
if _, err := file.Save(filePath, true); err != nil {
|
||||
return nil, gerror.New("保存文件失败: " + err.Error())
|
||||
}
|
||||
|
||||
// 获取上传者ID
|
||||
uploadedBy := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id"))
|
||||
if uploadedBy == 0 {
|
||||
uploadedBy = gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("user_id"))
|
||||
}
|
||||
|
||||
// 获取客户端IP
|
||||
uploadIp := g.RequestFromCtx(ctx).GetClientIp()
|
||||
|
||||
// 创建附件记录
|
||||
attachment := &model.Attachment{
|
||||
OriginalName: file.Filename,
|
||||
FileName: fileName,
|
||||
FilePath: filePath,
|
||||
FileUrl: fileUrl,
|
||||
FileSize: file.Size,
|
||||
FileType: s.getFileType(file.Filename),
|
||||
MimeType: s.getMimeType(file.Filename),
|
||||
FileExt: s.getFileExt(file.Filename),
|
||||
StorageType: "local",
|
||||
UploadIp: uploadIp,
|
||||
UploadedBy: uploadedBy,
|
||||
UsageCount: 0,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
id, err := dao.Attachment.Create(ctx, attachment)
|
||||
if err != nil {
|
||||
// 如果数据库保存失败,删除已上传的文件
|
||||
os.Remove(filePath)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attachment.Id = int(id)
|
||||
return attachment, nil
|
||||
}
|
||||
|
||||
// GetById 根据ID获取附件信息
|
||||
func (s *sAttachment) GetById(ctx context.Context, id int) (*model.Attachment, error) {
|
||||
return dao.Attachment.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// List 获取附件列表
|
||||
func (s *sAttachment) List(ctx context.Context, req *model.AttachmentListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
attachments, total, err := dao.Attachment.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: attachments,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete 删除附件
|
||||
func (s *sAttachment) Delete(ctx context.Context, id int) error {
|
||||
// 检查附件是否存在
|
||||
attachment, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if attachment == nil {
|
||||
return gerror.New("附件不存在")
|
||||
}
|
||||
|
||||
// 删除物理文件
|
||||
if gfile.Exists(attachment.FilePath) {
|
||||
if err := os.Remove(attachment.FilePath); err != nil {
|
||||
g.Log().Error(ctx, "删除物理文件失败:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除数据库记录
|
||||
return dao.Attachment.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// UpdateUsageCount 更新使用次数
|
||||
func (s *sAttachment) UpdateUsageCount(ctx context.Context, id int) error {
|
||||
return dao.Attachment.IncrementUsageCount(ctx, id)
|
||||
}
|
||||
|
||||
// GetStats 获取附件统计信息
|
||||
func (s *sAttachment) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总附件数
|
||||
totalCount, err := dao.Attachment.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取总文件大小
|
||||
totalSize, err := dao.Attachment.GetTotalSize(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取各类型文件数量
|
||||
imageCount, err := dao.Attachment.GetCountByType(ctx, "image")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
documentCount, err := dao.Attachment.GetCountByType(ctx, "document")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
videoCount, err := dao.Attachment.GetCountByType(ctx, "video")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"total_size": totalSize,
|
||||
"image_count": imageCount,
|
||||
"document_count": documentCount,
|
||||
"video_count": videoCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isAllowedFileType 检查文件类型是否允许
|
||||
func (s *sAttachment) isAllowedFileType(filename string) bool {
|
||||
allowedExts := []string{
|
||||
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", // 图片
|
||||
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", // 文档
|
||||
".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv", // 视频
|
||||
".mp3", ".wav", ".flac", ".aac", // 音频
|
||||
".zip", ".rar", ".7z", ".tar", ".gz", // 压缩包
|
||||
}
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
for _, allowedExt := range allowedExts {
|
||||
if ext == allowedExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// generateFilePath 生成文件路径
|
||||
func (s *sAttachment) generateFilePath(originalName, uploadType string) (string, string, string) {
|
||||
// 获取文件扩展名
|
||||
ext := filepath.Ext(originalName)
|
||||
|
||||
// 生成唯一文件名
|
||||
hash := gmd5.MustEncrypt(fmt.Sprintf("%s_%d_%s", originalName, time.Now().UnixNano(), grand.S(8)))
|
||||
fileName := hash + ext
|
||||
|
||||
// 根据日期创建目录结构
|
||||
now := time.Now()
|
||||
dateDir := fmt.Sprintf("%d/%02d/%02d", now.Year(), now.Month(), now.Day())
|
||||
|
||||
// 根据上传类型创建子目录
|
||||
if uploadType == "" {
|
||||
uploadType = "general"
|
||||
}
|
||||
|
||||
// 构建完整路径
|
||||
relativePath := fmt.Sprintf("uploads/%s/%s/%s", uploadType, dateDir, fileName)
|
||||
fullPath := filepath.Join("storage", relativePath)
|
||||
fileUrl := "/" + strings.ReplaceAll(relativePath, "\\", "/")
|
||||
|
||||
return fileName, fullPath, fileUrl
|
||||
}
|
||||
|
||||
// getFileType 获取文件类型
|
||||
func (s *sAttachment) getFileType(filename string) string {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
|
||||
imageExts := []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"}
|
||||
documentExts := []string{".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt"}
|
||||
videoExts := []string{".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv"}
|
||||
audioExts := []string{".mp3", ".wav", ".flac", ".aac"}
|
||||
archiveExts := []string{".zip", ".rar", ".7z", ".tar", ".gz"}
|
||||
|
||||
for _, imageExt := range imageExts {
|
||||
if ext == imageExt {
|
||||
return "image"
|
||||
}
|
||||
}
|
||||
|
||||
for _, docExt := range documentExts {
|
||||
if ext == docExt {
|
||||
return "document"
|
||||
}
|
||||
}
|
||||
|
||||
for _, videoExt := range videoExts {
|
||||
if ext == videoExt {
|
||||
return "video"
|
||||
}
|
||||
}
|
||||
|
||||
for _, audioExt := range audioExts {
|
||||
if ext == audioExt {
|
||||
return "audio"
|
||||
}
|
||||
}
|
||||
|
||||
for _, archiveExt := range archiveExts {
|
||||
if ext == archiveExt {
|
||||
return "archive"
|
||||
}
|
||||
}
|
||||
|
||||
return "other"
|
||||
}
|
||||
|
||||
// getMimeType 获取MIME类型
|
||||
func (s *sAttachment) getMimeType(filename string) string {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
|
||||
mimeTypes := map[string]string{
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".png": "image/png",
|
||||
".gif": "image/gif",
|
||||
".bmp": "image/bmp",
|
||||
".webp": "image/webp",
|
||||
".pdf": "application/pdf",
|
||||
".doc": "application/msword",
|
||||
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".xls": "application/vnd.ms-excel",
|
||||
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
".ppt": "application/vnd.ms-powerpoint",
|
||||
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
".txt": "text/plain",
|
||||
".mp4": "video/mp4",
|
||||
".avi": "video/x-msvideo",
|
||||
".mov": "video/quicktime",
|
||||
".wmv": "video/x-ms-wmv",
|
||||
".flv": "video/x-flv",
|
||||
".mkv": "video/x-matroska",
|
||||
".mp3": "audio/mpeg",
|
||||
".wav": "audio/wav",
|
||||
".flac": "audio/flac",
|
||||
".aac": "audio/aac",
|
||||
".zip": "application/zip",
|
||||
".rar": "application/x-rar-compressed",
|
||||
".7z": "application/x-7z-compressed",
|
||||
".tar": "application/x-tar",
|
||||
".gz": "application/gzip",
|
||||
}
|
||||
|
||||
if mimeType, exists := mimeTypes[ext]; exists {
|
||||
return mimeType
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
// getFileExt 获取文件扩展名
|
||||
func (s *sAttachment) getFileExt(filename string) string {
|
||||
ext := filepath.Ext(filename)
|
||||
if len(ext) > 0 {
|
||||
return ext[1:] // 去掉点号
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除附件
|
||||
func (s *sAttachment) BatchDelete(ctx context.Context, ids []int) error {
|
||||
for _, id := range ids {
|
||||
if err := s.Delete(ctx, id); err != nil {
|
||||
g.Log().Error(ctx, "批量删除附件失败, ID:", id, "错误:", err)
|
||||
// 继续删除其他文件,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByIds 根据ID列表获取附件
|
||||
func (s *sAttachment) GetByIds(ctx context.Context, ids []int) ([]*model.Attachment, error) {
|
||||
return dao.Attachment.GetByIds(ctx, ids)
|
||||
}
|
||||
113
internal/service/auth.go
Normal file
113
internal/service/auth.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type sAuth struct{}
|
||||
|
||||
var Auth = sAuth{}
|
||||
|
||||
// Claims JWT声明
|
||||
type Claims struct {
|
||||
UserId uint64 `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// GenerateToken 生成JWT Token
|
||||
func (s *sAuth) GenerateToken(ctx context.Context, userId uint64) (string, error) {
|
||||
// 获取JWT配置
|
||||
jwtConfig := g.Cfg().MustGet(ctx, "jwt")
|
||||
signingKey := jwtConfig.Map()["signingKey"].(string)
|
||||
expire := gconv.Int64(jwtConfig.Map()["expire"])
|
||||
|
||||
// 创建声明
|
||||
claims := Claims{
|
||||
UserId: userId,
|
||||
Username: "", // 将在后续获取
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "cms-api",
|
||||
Subject: gconv.String(userId),
|
||||
},
|
||||
}
|
||||
|
||||
// 创建token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// 签名token
|
||||
tokenString, err := token.SignedString([]byte(signingKey))
|
||||
if err != nil {
|
||||
return "", gerror.New("生成Token失败")
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// ParseToken 解析JWT Token
|
||||
func (s *sAuth) ParseToken(ctx context.Context, tokenString string) (*Claims, error) {
|
||||
// 获取JWT配置
|
||||
jwtConfig := g.Cfg().MustGet(ctx, "jwt")
|
||||
signingKey := jwtConfig.Map()["signingKey"].(string)
|
||||
|
||||
// 解析token
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(signingKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, gerror.New("Token解析失败")
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, gerror.New("Token无效")
|
||||
}
|
||||
|
||||
// ValidateToken 验证Token
|
||||
func (s *sAuth) ValidateToken(ctx context.Context, tokenString string) (uint64, error) {
|
||||
claims, err := s.ParseToken(ctx, tokenString)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 检查管理员是否存在且状态正常
|
||||
admin, err := Admin().GetById(ctx, int(claims.UserId))
|
||||
if err != nil {
|
||||
return 0, gerror.New("管理员不存在")
|
||||
}
|
||||
|
||||
if admin == nil || admin.Status != 1 {
|
||||
return 0, gerror.New("管理员已被禁用")
|
||||
}
|
||||
|
||||
return claims.UserId, nil
|
||||
}
|
||||
|
||||
// RefreshToken 刷新Token
|
||||
func (s *sAuth) RefreshToken(ctx context.Context, tokenString string) (string, error) {
|
||||
claims, err := s.ParseToken(ctx, tokenString)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 生成新的Token
|
||||
return s.GenerateToken(ctx, claims.UserId)
|
||||
}
|
||||
|
||||
// GetTokenExpire 获取Token过期时间
|
||||
func (s *sAuth) GetTokenExpire(ctx context.Context) int {
|
||||
jwtConfig := g.Cfg().MustGet(ctx, "jwt")
|
||||
return gconv.Int(jwtConfig.Map()["expire"])
|
||||
}
|
||||
413
internal/service/config.go
Normal file
413
internal/service/config.go
Normal file
@@ -0,0 +1,413 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sConfig struct{}
|
||||
|
||||
func Config() *sConfig {
|
||||
return &sConfig{}
|
||||
}
|
||||
|
||||
// GetByKey 根据配置键获取配置
|
||||
func (s *sConfig) GetByKey(ctx context.Context, key string) (*model.SiteConfig, error) {
|
||||
return dao.Config.GetByKey(ctx, key)
|
||||
}
|
||||
|
||||
// GetValue 获取配置值
|
||||
func (s *sConfig) GetValue(ctx context.Context, key string, defaultValue ...interface{}) interface{} {
|
||||
config, err := s.GetByKey(ctx, key)
|
||||
if err != nil || config == nil {
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据配置类型转换值
|
||||
switch config.ConfigType {
|
||||
case "number":
|
||||
return gconv.Int(config.ConfigValue)
|
||||
case "boolean":
|
||||
return gconv.Bool(config.ConfigValue)
|
||||
case "json", "array":
|
||||
var result interface{}
|
||||
if err := json.Unmarshal([]byte(config.ConfigValue), &result); err == nil {
|
||||
return result
|
||||
}
|
||||
return config.ConfigValue
|
||||
default:
|
||||
return config.ConfigValue
|
||||
}
|
||||
}
|
||||
|
||||
// GetString 获取字符串配置值
|
||||
func (s *sConfig) GetString(ctx context.Context, key string, defaultValue ...string) string {
|
||||
value := s.GetValue(ctx, key)
|
||||
if value == nil {
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return gconv.String(value)
|
||||
}
|
||||
|
||||
// GetInt 获取整数配置值
|
||||
func (s *sConfig) GetInt(ctx context.Context, key string, defaultValue ...int) int {
|
||||
value := s.GetValue(ctx, key)
|
||||
if value == nil {
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return gconv.Int(value)
|
||||
}
|
||||
|
||||
// GetBool 获取布尔配置值
|
||||
func (s *sConfig) GetBool(ctx context.Context, key string, defaultValue ...bool) bool {
|
||||
value := s.GetValue(ctx, key)
|
||||
if value == nil {
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
}
|
||||
return false
|
||||
}
|
||||
return gconv.Bool(value)
|
||||
}
|
||||
|
||||
// List 获取配置列表
|
||||
func (s *sConfig) List(ctx context.Context, req *model.ConfigListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
configs, total, err := dao.Config.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: configs,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByGroup 根据分组获取配置
|
||||
func (s *sConfig) GetByGroup(ctx context.Context, groupName string) ([]*model.SiteConfig, error) {
|
||||
return dao.Config.GetByGroup(ctx, groupName)
|
||||
}
|
||||
|
||||
// GetGroups 获取所有配置分组
|
||||
func (s *sConfig) GetGroups(ctx context.Context) ([]string, error) {
|
||||
return dao.Config.GetGroups(ctx)
|
||||
}
|
||||
|
||||
// Create 创建配置
|
||||
func (s *sConfig) Create(ctx context.Context, req *model.CreateConfigRequest) error {
|
||||
// 检查配置键是否存在
|
||||
existConfig, err := dao.Config.GetByKey(ctx, req.ConfigKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existConfig != nil {
|
||||
return gerror.New("配置键已存在")
|
||||
}
|
||||
|
||||
// 验证配置值格式
|
||||
if err := s.validateConfigValue(req.ConfigValue, req.ConfigType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建配置
|
||||
config := &model.SiteConfig{
|
||||
ConfigKey: req.ConfigKey,
|
||||
ConfigValue: req.ConfigValue,
|
||||
ConfigType: req.ConfigType,
|
||||
GroupName: req.GroupName,
|
||||
Description: req.Description,
|
||||
SortOrder: req.SortOrder,
|
||||
IsSystem: req.IsSystem,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
_, err = dao.Config.Create(ctx, config)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新配置
|
||||
func (s *sConfig) Update(ctx context.Context, key string, req *model.UpdateConfigRequest) error {
|
||||
// 检查配置是否存在
|
||||
config, err := s.GetByKey(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config == nil {
|
||||
return gerror.New("配置不存在")
|
||||
}
|
||||
|
||||
// 验证配置值格式
|
||||
if err := s.validateConfigValue(req.ConfigValue, req.ConfigType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
updateData := g.Map{
|
||||
"config_value": req.ConfigValue,
|
||||
"config_type": req.ConfigType,
|
||||
"group_name": req.GroupName,
|
||||
"description": req.Description,
|
||||
"sort_order": req.SortOrder,
|
||||
"is_system": req.IsSystem,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.Config.UpdateByKey(ctx, key, updateData)
|
||||
}
|
||||
|
||||
// UpdateValue 更新配置值
|
||||
func (s *sConfig) UpdateValue(ctx context.Context, key string, value interface{}) error {
|
||||
// 检查配置是否存在
|
||||
config, err := s.GetByKey(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config == nil {
|
||||
return gerror.New("配置不存在")
|
||||
}
|
||||
|
||||
// 转换值为字符串
|
||||
var valueStr string
|
||||
switch config.ConfigType {
|
||||
case "json", "array":
|
||||
if jsonBytes, err := json.Marshal(value); err == nil {
|
||||
valueStr = string(jsonBytes)
|
||||
} else {
|
||||
return gerror.New("配置值格式错误")
|
||||
}
|
||||
default:
|
||||
valueStr = gconv.String(value)
|
||||
}
|
||||
|
||||
// 验证配置值格式
|
||||
if err := s.validateConfigValue(valueStr, config.ConfigType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新配置值
|
||||
return dao.Config.UpdateValue(ctx, key, valueStr)
|
||||
}
|
||||
|
||||
// BatchUpdate 批量更新配置
|
||||
func (s *sConfig) BatchUpdate(ctx context.Context, req *model.BatchUpdateConfigRequest) error {
|
||||
for key, value := range req.Configs {
|
||||
if err := s.UpdateValue(ctx, key, value); err != nil {
|
||||
g.Log().Error(ctx, "批量更新配置失败, 键:", key, "错误:", err)
|
||||
// 继续更新其他配置,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除配置
|
||||
func (s *sConfig) Delete(ctx context.Context, key string) error {
|
||||
// 检查配置是否存在
|
||||
config, err := s.GetByKey(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config == nil {
|
||||
return gerror.New("配置不存在")
|
||||
}
|
||||
|
||||
// 检查是否为系统配置
|
||||
if config.IsSystem == 1 {
|
||||
return gerror.New("系统配置不能删除")
|
||||
}
|
||||
|
||||
return dao.Config.DeleteByKey(ctx, key)
|
||||
}
|
||||
|
||||
// InitDefaultConfigs 初始化默认配置
|
||||
func (s *sConfig) InitDefaultConfigs(ctx context.Context) error {
|
||||
defaultConfigs := []model.SiteConfig{
|
||||
{
|
||||
ConfigKey: "site_name",
|
||||
ConfigValue: "企业官网CMS系统",
|
||||
ConfigType: "string",
|
||||
GroupName: "基础设置",
|
||||
Description: "网站名称",
|
||||
SortOrder: 1,
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "site_title",
|
||||
ConfigValue: "企业官网CMS系统 - 专业的内容管理平台",
|
||||
ConfigType: "string",
|
||||
GroupName: "基础设置",
|
||||
Description: "网站标题",
|
||||
SortOrder: 2,
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "site_description",
|
||||
ConfigValue: "专业的企业官网内容管理系统,提供完整的内容发布和管理功能",
|
||||
ConfigType: "string",
|
||||
GroupName: "基础设置",
|
||||
Description: "网站描述",
|
||||
SortOrder: 3,
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "site_keywords",
|
||||
ConfigValue: "企业官网,CMS,内容管理,新闻发布",
|
||||
ConfigType: "string",
|
||||
GroupName: "基础设置",
|
||||
Description: "网站关键词",
|
||||
SortOrder: 4,
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "contact_phone",
|
||||
ConfigValue: "400-123-4567",
|
||||
ConfigType: "string",
|
||||
GroupName: "联系方式",
|
||||
Description: "联系电话",
|
||||
SortOrder: 1,
|
||||
IsSystem: 0,
|
||||
},
|
||||
{
|
||||
ConfigKey: "contact_email",
|
||||
ConfigValue: "contact@example.com",
|
||||
ConfigType: "string",
|
||||
GroupName: "联系方式",
|
||||
Description: "联系邮箱",
|
||||
SortOrder: 2,
|
||||
IsSystem: 0,
|
||||
},
|
||||
{
|
||||
ConfigKey: "contact_address",
|
||||
ConfigValue: "北京市朝阳区xxx大厦xxx室",
|
||||
ConfigType: "string",
|
||||
GroupName: "联系方式",
|
||||
Description: "联系地址",
|
||||
SortOrder: 3,
|
||||
IsSystem: 0,
|
||||
},
|
||||
{
|
||||
ConfigKey: "upload_max_size",
|
||||
ConfigValue: "10485760",
|
||||
ConfigType: "number",
|
||||
GroupName: "上传设置",
|
||||
Description: "最大上传文件大小(字节)",
|
||||
SortOrder: 1,
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "allowed_file_types",
|
||||
ConfigValue: `["jpg","jpeg","png","gif","pdf","doc","docx","xls","xlsx"]`,
|
||||
ConfigType: "array",
|
||||
GroupName: "上传设置",
|
||||
Description: "允许上传的文件类型",
|
||||
SortOrder: 2,
|
||||
IsSystem: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, config := range defaultConfigs {
|
||||
// 检查配置是否已存在
|
||||
existConfig, err := dao.Config.GetByKey(ctx, config.ConfigKey)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if existConfig != nil {
|
||||
continue // 跳过已存在的配置
|
||||
}
|
||||
|
||||
// 创建配置
|
||||
config.CreatedAt = gtime.Now()
|
||||
config.UpdatedAt = gtime.Now()
|
||||
dao.Config.Create(ctx, &config)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateConfigValue 验证配置值格式
|
||||
func (s *sConfig) validateConfigValue(value, configType string) error {
|
||||
switch configType {
|
||||
case "number":
|
||||
if _, err := strconv.ParseFloat(value, 64); err != nil {
|
||||
return gerror.New("配置值必须是数字")
|
||||
}
|
||||
case "boolean":
|
||||
if value != "true" && value != "false" && value != "1" && value != "0" {
|
||||
return gerror.New("配置值必须是布尔值")
|
||||
}
|
||||
case "json", "array":
|
||||
var temp interface{}
|
||||
if err := json.Unmarshal([]byte(value), &temp); err != nil {
|
||||
return gerror.New("配置值必须是有效的JSON格式")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isNumeric 检查字符串是否为数字
|
||||
func (s *sConfig) isNumeric(str string) bool {
|
||||
matched, _ := regexp.MatchString(`^-?\d+(\.\d+)?$`, str)
|
||||
return matched
|
||||
}
|
||||
|
||||
// GetPublicConfigs 获取公开配置(前台可访问)
|
||||
func (s *sConfig) GetPublicConfigs(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 定义公开配置键
|
||||
publicKeys := []string{
|
||||
"site_name",
|
||||
"site_title",
|
||||
"site_description",
|
||||
"site_keywords",
|
||||
"contact_phone",
|
||||
"contact_email",
|
||||
"contact_address",
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
for _, key := range publicKeys {
|
||||
value := s.GetValue(ctx, key)
|
||||
if value != nil {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
286
internal/service/contact.go
Normal file
286
internal/service/contact.go
Normal file
@@ -0,0 +1,286 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sContact struct{}
|
||||
|
||||
func Contact() *sContact {
|
||||
return &sContact{}
|
||||
}
|
||||
|
||||
// GetById 根据ID获取联系信息
|
||||
func (s *sContact) GetById(ctx context.Context, id int) (*model.Contact, error) {
|
||||
return dao.Contact.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// List 获取联系信息列表
|
||||
func (s *sContact) List(ctx context.Context, req *model.ContactListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
contacts, total, err := dao.Contact.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: contacts,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建联系信息(前台提交表单)
|
||||
func (s *sContact) Create(ctx context.Context, req *model.CreateContactRequest) error {
|
||||
// 获取客户端信息
|
||||
request := g.RequestFromCtx(ctx)
|
||||
ip := request.GetClientIp()
|
||||
userAgent := request.Header.Get("User-Agent")
|
||||
|
||||
// 创建联系信息
|
||||
contact := &model.Contact{
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Phone: req.Phone,
|
||||
Company: req.Company,
|
||||
Subject: req.Subject,
|
||||
Message: req.Message,
|
||||
Ip: ip,
|
||||
UserAgent: userAgent,
|
||||
Status: 0, // 未处理
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
_, err := dao.Contact.Create(ctx, contact)
|
||||
return err
|
||||
}
|
||||
|
||||
// Reply 回复联系信息
|
||||
func (s *sContact) Reply(ctx context.Context, id int, req *model.ReplyContactRequest) error {
|
||||
// 检查联系信息是否存在
|
||||
contact, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if contact == nil {
|
||||
return gerror.New("联系信息不存在")
|
||||
}
|
||||
|
||||
// 获取回复人ID
|
||||
repliedBy := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id"))
|
||||
if repliedBy == 0 {
|
||||
return gerror.New("未获取到管理员信息")
|
||||
}
|
||||
|
||||
// 更新回复信息
|
||||
updateData := g.Map{
|
||||
"reply": req.Reply,
|
||||
"status": 2, // 已回复
|
||||
"replied_at": gtime.Now(),
|
||||
"replied_by": repliedBy,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.Contact.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新处理状态
|
||||
func (s *sContact) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
// 检查联系信息是否存在
|
||||
contact, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if contact == nil {
|
||||
return gerror.New("联系信息不存在")
|
||||
}
|
||||
|
||||
updateData := g.Map{
|
||||
"status": status,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
// 如果状态改为已处理但没有回复,只更新状态
|
||||
if status == 1 && contact.Status == 0 {
|
||||
// 已处理状态
|
||||
}
|
||||
|
||||
return dao.Contact.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除联系信息
|
||||
func (s *sContact) Delete(ctx context.Context, id int) error {
|
||||
// 检查联系信息是否存在
|
||||
contact, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if contact == nil {
|
||||
return gerror.New("联系信息不存在")
|
||||
}
|
||||
|
||||
return dao.Contact.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// GetStats 获取联系信息统计
|
||||
func (s *sContact) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总联系信息数
|
||||
totalCount, err := dao.Contact.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取各状态联系信息数
|
||||
unprocessedCount, err := dao.Contact.GetCountByStatus(ctx, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
processedCount, err := dao.Contact.GetCountByStatus(ctx, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repliedCount, err := dao.Contact.GetCountByStatus(ctx, 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取今日新增联系信息数
|
||||
todayCount, err := dao.Contact.GetTodayCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取本周新增联系信息数
|
||||
weekCount, err := dao.Contact.GetWeekCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取本月新增联系信息数
|
||||
monthCount, err := dao.Contact.GetMonthCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"unprocessed_count": unprocessedCount,
|
||||
"processed_count": processedCount,
|
||||
"replied_count": repliedCount,
|
||||
"today_count": todayCount,
|
||||
"week_count": weekCount,
|
||||
"month_count": monthCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetLatest 获取最新联系信息
|
||||
func (s *sContact) GetLatest(ctx context.Context, limit int) ([]*model.Contact, error) {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
return dao.Contact.GetLatest(ctx, limit)
|
||||
}
|
||||
|
||||
// GetUnprocessed 获取未处理的联系信息
|
||||
func (s *sContact) GetUnprocessed(ctx context.Context, limit int) ([]*model.Contact, error) {
|
||||
if limit <= 0 {
|
||||
limit = 20
|
||||
}
|
||||
return dao.Contact.GetUnprocessed(ctx, limit)
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新状态
|
||||
func (s *sContact) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
|
||||
for _, id := range ids {
|
||||
if err := s.UpdateStatus(ctx, id, status); err != nil {
|
||||
g.Log().Error(ctx, "批量更新联系信息状态失败, ID:", id, "错误:", err)
|
||||
// 继续更新其他记录,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除联系信息
|
||||
func (s *sContact) BatchDelete(ctx context.Context, ids []int) error {
|
||||
for _, id := range ids {
|
||||
if err := s.Delete(ctx, id); err != nil {
|
||||
g.Log().Error(ctx, "批量删除联系信息失败, ID:", id, "错误:", err)
|
||||
// 继续删除其他记录,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search 搜索联系信息
|
||||
func (s *sContact) Search(ctx context.Context, keyword string, page, pageSize int) (*model.PageResponse, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
contacts, total, err := dao.Contact.Search(ctx, keyword, page, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: contacts,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Export 导出联系信息
|
||||
func (s *sContact) Export(ctx context.Context, req *model.ContactListRequest) ([]*model.Contact, error) {
|
||||
// 设置大的页面大小来获取所有数据
|
||||
req.Page = 1
|
||||
req.PageSize = 10000
|
||||
|
||||
contacts, _, err := dao.Contact.List(ctx, req)
|
||||
return contacts, err
|
||||
}
|
||||
|
||||
// GetContactTrends 获取联系信息趋势数据
|
||||
func (s *sContact) GetContactTrends(ctx context.Context, days int) ([]map[string]interface{}, error) {
|
||||
if days <= 0 {
|
||||
days = 7 // 默认7天
|
||||
}
|
||||
return dao.Contact.GetTrends(ctx, days)
|
||||
}
|
||||
|
||||
// GetTrends 获取联系信息趋势数据(兼容性方法)
|
||||
func (s *sContact) GetTrends(ctx context.Context, days int) ([]map[string]interface{}, error) {
|
||||
return s.GetContactTrends(ctx, days)
|
||||
}
|
||||
355
internal/service/news.go
Normal file
355
internal/service/news.go
Normal file
@@ -0,0 +1,355 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sNews struct{}
|
||||
|
||||
func News() *sNews {
|
||||
return &sNews{}
|
||||
}
|
||||
|
||||
// GetById 根据ID获取新闻信息
|
||||
func (s *sNews) GetById(ctx context.Context, id int) (*model.News, error) {
|
||||
news, err := dao.News.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if news != nil {
|
||||
// 增加浏览次数
|
||||
go func() {
|
||||
dao.News.IncrementViewCount(context.Background(), id)
|
||||
}()
|
||||
}
|
||||
return news, nil
|
||||
}
|
||||
|
||||
// GetBySlug 根据URL别名获取新闻
|
||||
func (s *sNews) GetBySlug(ctx context.Context, slug string) (*model.News, error) {
|
||||
news, err := dao.News.GetBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if news != nil {
|
||||
// 增加浏览次数
|
||||
go func() {
|
||||
dao.News.IncrementViewCount(context.Background(), news.Id)
|
||||
}()
|
||||
}
|
||||
return news, nil
|
||||
}
|
||||
|
||||
// List 获取新闻列表
|
||||
func (s *sNews) List(ctx context.Context, req *model.NewsListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
newsList, total, err := dao.News.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: newsList,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建新闻
|
||||
func (s *sNews) Create(ctx context.Context, req *model.CreateNewsRequest) error {
|
||||
// 检查URL别名是否存在
|
||||
if req.Slug != "" {
|
||||
existNews, err := dao.News.GetBySlug(ctx, req.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existNews != nil {
|
||||
return gerror.New("URL别名已存在")
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供slug,则根据标题生成
|
||||
req.Slug = s.generateSlug(req.Title)
|
||||
}
|
||||
|
||||
// 创建新闻
|
||||
news := &model.News{
|
||||
Title: req.Title,
|
||||
Slug: req.Slug,
|
||||
Summary: req.Summary,
|
||||
Content: req.Content,
|
||||
CoverImage: req.CoverImage,
|
||||
Category: req.Category,
|
||||
Source: req.Source,
|
||||
Author: req.Author,
|
||||
IsPublished: req.IsPublished,
|
||||
IsFeatured: req.IsFeatured,
|
||||
IsTop: req.IsTop,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
if req.IsPublished == 1 {
|
||||
news.PublishedAt = gtime.Now()
|
||||
}
|
||||
|
||||
_, err := dao.News.Create(ctx, news)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新新闻
|
||||
func (s *sNews) Update(ctx context.Context, id int, req *model.UpdateNewsRequest) error {
|
||||
// 检查新闻是否存在
|
||||
news, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if news == nil {
|
||||
return gerror.New("新闻不存在")
|
||||
}
|
||||
|
||||
// 检查URL别名是否被其他新闻使用
|
||||
if req.Slug != "" && req.Slug != news.Slug {
|
||||
existNews, err := dao.News.GetBySlug(ctx, req.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existNews != nil && existNews.Id != id {
|
||||
return gerror.New("URL别名已被使用")
|
||||
}
|
||||
}
|
||||
|
||||
// 更新新闻
|
||||
updateData := g.Map{
|
||||
"title": req.Title,
|
||||
"slug": req.Slug,
|
||||
"summary": req.Summary,
|
||||
"content": req.Content,
|
||||
"cover_image": req.CoverImage,
|
||||
"category": req.Category,
|
||||
"source": req.Source,
|
||||
"author": req.Author,
|
||||
"is_published": req.IsPublished,
|
||||
"is_featured": req.IsFeatured,
|
||||
"is_top": req.IsTop,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
// 如果从草稿变为发布状态,设置发布时间
|
||||
if req.IsPublished == 1 && news.IsPublished == 0 {
|
||||
updateData["published_at"] = gtime.Now()
|
||||
}
|
||||
|
||||
return dao.News.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除新闻
|
||||
func (s *sNews) Delete(ctx context.Context, id int) error {
|
||||
// 检查新闻是否存在
|
||||
news, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if news == nil {
|
||||
return gerror.New("新闻不存在")
|
||||
}
|
||||
|
||||
return dao.News.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新新闻状态
|
||||
func (s *sNews) UpdateStatus(ctx context.Context, id int, isPublished int) error {
|
||||
// 检查新闻是否存在
|
||||
news, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if news == nil {
|
||||
return gerror.New("新闻不存在")
|
||||
}
|
||||
|
||||
updateData := g.Map{
|
||||
"is_published": isPublished,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
// 如果是发布状态,设置发布时间
|
||||
if isPublished == 1 && news.IsPublished == 0 {
|
||||
updateData["published_at"] = gtime.Now()
|
||||
}
|
||||
|
||||
return dao.News.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// SetFeatured 设置推荐状态
|
||||
func (s *sNews) SetFeatured(ctx context.Context, id int, isFeatured int) error {
|
||||
// 检查新闻是否存在
|
||||
news, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if news == nil {
|
||||
return gerror.New("新闻不存在")
|
||||
}
|
||||
|
||||
return dao.News.Update(ctx, id, g.Map{
|
||||
"is_featured": isFeatured,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// SetTop 设置置顶状态
|
||||
func (s *sNews) SetTop(ctx context.Context, id int, isTop int) error {
|
||||
// 检查新闻是否存在
|
||||
news, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if news == nil {
|
||||
return gerror.New("新闻不存在")
|
||||
}
|
||||
|
||||
return dao.News.Update(ctx, id, g.Map{
|
||||
"is_top": isTop,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats 获取新闻统计信息
|
||||
func (s *sNews) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总新闻数
|
||||
totalCount, err := dao.News.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取已发布新闻数
|
||||
publishedCount, err := dao.News.GetPublishedCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取草稿数
|
||||
draftCount := totalCount - publishedCount
|
||||
|
||||
// 获取推荐新闻数
|
||||
featuredCount, err := dao.News.GetFeaturedCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取各分类新闻数
|
||||
companyNewsCount, err := dao.News.GetCountByCategory(ctx, "company_news")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
industryNewsCount, err := dao.News.GetCountByCategory(ctx, "industry_news")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"published_count": publishedCount,
|
||||
"draft_count": draftCount,
|
||||
"featured_count": featuredCount,
|
||||
"company_news_count": companyNewsCount,
|
||||
"industry_news_count": industryNewsCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐新闻
|
||||
func (s *sNews) GetFeatured(ctx context.Context, limit int) ([]*model.News, error) {
|
||||
if limit <= 0 {
|
||||
limit = 5
|
||||
}
|
||||
return dao.News.GetFeatured(ctx, limit)
|
||||
}
|
||||
|
||||
// GetLatest 获取最新新闻
|
||||
func (s *sNews) GetLatest(ctx context.Context, limit int) ([]*model.News, error) {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
return dao.News.GetLatest(ctx, limit)
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取新闻
|
||||
func (s *sNews) GetByCategory(ctx context.Context, category string, page, pageSize int) (*model.PageResponse, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
newsList, total, err := dao.News.GetByCategory(ctx, category, page, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: newsList,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateSlug 生成URL别名
|
||||
func (s *sNews) generateSlug(title string) string {
|
||||
// 简单的slug生成逻辑
|
||||
slug := strings.ToLower(title)
|
||||
slug = strings.ReplaceAll(slug, " ", "-")
|
||||
slug = strings.ReplaceAll(slug, " ", "-") // 全角空格
|
||||
return slug
|
||||
}
|
||||
|
||||
// Search 搜索新闻
|
||||
func (s *sNews) Search(ctx context.Context, keyword string, page, pageSize int) (*model.PageResponse, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
newsList, total, err := dao.News.Search(ctx, keyword, page, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: newsList,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
264
internal/service/partner.go
Normal file
264
internal/service/partner.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sPartner struct{}
|
||||
|
||||
func Partner() *sPartner {
|
||||
return &sPartner{}
|
||||
}
|
||||
|
||||
// GetById 根据ID获取合作伙伴信息
|
||||
func (s *sPartner) GetById(ctx context.Context, id int) (*model.Partner, error) {
|
||||
return dao.Partner.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// List 获取合作伙伴列表
|
||||
func (s *sPartner) List(ctx context.Context, req *model.PartnerListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
partners, total, err := dao.Partner.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: partners,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAll 获取所有启用的合作伙伴
|
||||
func (s *sPartner) GetAll(ctx context.Context) ([]*model.Partner, error) {
|
||||
return dao.Partner.GetAll(ctx)
|
||||
}
|
||||
|
||||
// GetFeatured 获取推荐合作伙伴
|
||||
func (s *sPartner) GetFeatured(ctx context.Context, limit int) ([]*model.Partner, error) {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
return dao.Partner.GetFeatured(ctx, limit)
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取合作伙伴
|
||||
func (s *sPartner) GetByCategory(ctx context.Context, category string) ([]*model.Partner, error) {
|
||||
return dao.Partner.GetByCategory(ctx, category)
|
||||
}
|
||||
|
||||
// Create 创建合作伙伴
|
||||
func (s *sPartner) Create(ctx context.Context, req *model.CreatePartnerRequest) error {
|
||||
// 检查合作伙伴名称是否存在
|
||||
existPartner, err := dao.Partner.GetByName(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existPartner != nil {
|
||||
return gerror.New("合作伙伴名称已存在")
|
||||
}
|
||||
|
||||
// 创建合作伙伴
|
||||
partner := &model.Partner{
|
||||
Name: req.Name,
|
||||
Logo: req.Logo,
|
||||
Website: req.Website,
|
||||
Description: req.Description,
|
||||
Category: req.Category,
|
||||
SortOrder: req.SortOrder,
|
||||
IsFeatured: req.IsFeatured,
|
||||
Status: req.Status,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
_, err = dao.Partner.Create(ctx, partner)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新合作伙伴
|
||||
func (s *sPartner) Update(ctx context.Context, id int, req *model.UpdatePartnerRequest) error {
|
||||
// 检查合作伙伴是否存在
|
||||
partner, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partner == nil {
|
||||
return gerror.New("合作伙伴不存在")
|
||||
}
|
||||
|
||||
// 检查合作伙伴名称是否被其他记录使用
|
||||
existPartner, err := dao.Partner.GetByName(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existPartner != nil && existPartner.Id != id {
|
||||
return gerror.New("合作伙伴名称已被使用")
|
||||
}
|
||||
|
||||
// 更新合作伙伴
|
||||
updateData := g.Map{
|
||||
"name": req.Name,
|
||||
"logo": req.Logo,
|
||||
"website": req.Website,
|
||||
"description": req.Description,
|
||||
"category": req.Category,
|
||||
"sort_order": req.SortOrder,
|
||||
"is_featured": req.IsFeatured,
|
||||
"status": req.Status,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.Partner.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除合作伙伴
|
||||
func (s *sPartner) Delete(ctx context.Context, id int) error {
|
||||
// 检查合作伙伴是否存在
|
||||
partner, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partner == nil {
|
||||
return gerror.New("合作伙伴不存在")
|
||||
}
|
||||
|
||||
return dao.Partner.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新合作伙伴状态
|
||||
func (s *sPartner) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
// 检查合作伙伴是否存在
|
||||
partner, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partner == nil {
|
||||
return gerror.New("合作伙伴不存在")
|
||||
}
|
||||
|
||||
return dao.Partner.UpdateStatus(ctx, id, status)
|
||||
}
|
||||
|
||||
// SetFeatured 设置推荐状态
|
||||
func (s *sPartner) SetFeatured(ctx context.Context, id int, isFeatured int) error {
|
||||
// 检查合作伙伴是否存在
|
||||
partner, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partner == nil {
|
||||
return gerror.New("合作伙伴不存在")
|
||||
}
|
||||
|
||||
return dao.Partner.Update(ctx, id, g.Map{
|
||||
"is_featured": isFeatured,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateSortOrder 更新排序
|
||||
func (s *sPartner) UpdateSortOrder(ctx context.Context, id int, sortOrder int) error {
|
||||
// 检查合作伙伴是否存在
|
||||
partner, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partner == nil {
|
||||
return gerror.New("合作伙伴不存在")
|
||||
}
|
||||
|
||||
return dao.Partner.Update(ctx, id, g.Map{
|
||||
"sort_order": sortOrder,
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats 获取合作伙伴统计信息
|
||||
func (s *sPartner) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总合作伙伴数
|
||||
totalCount, err := dao.Partner.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取启用的合作伙伴数
|
||||
activeCount, err := dao.Partner.GetCountByStatus(ctx, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取推荐合作伙伴数
|
||||
featuredCount, err := dao.Partner.GetFeaturedCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取各分类合作伙伴数
|
||||
categories, err := dao.Partner.GetCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
categoryStats := make(map[string]int)
|
||||
for _, category := range categories {
|
||||
count, err := dao.Partner.GetCountByCategory(ctx, category)
|
||||
if err == nil {
|
||||
categoryStats[category] = count
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"active_count": activeCount,
|
||||
"featured_count": featuredCount,
|
||||
"category_stats": categoryStats,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新状态
|
||||
func (s *sPartner) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
|
||||
for _, id := range ids {
|
||||
if err := s.UpdateStatus(ctx, id, status); err != nil {
|
||||
g.Log().Error(ctx, "批量更新合作伙伴状态失败, ID:", id, "错误:", err)
|
||||
// 继续更新其他记录,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除合作伙伴
|
||||
func (s *sPartner) BatchDelete(ctx context.Context, ids []int) error {
|
||||
for _, id := range ids {
|
||||
if err := s.Delete(ctx, id); err != nil {
|
||||
g.Log().Error(ctx, "批量删除合作伙伴失败, ID:", id, "错误:", err)
|
||||
// 继续删除其他记录,不中断整个过程
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
227
internal/service/role.go
Normal file
227
internal/service/role.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sRole struct{}
|
||||
|
||||
func Role() *sRole {
|
||||
return &sRole{}
|
||||
}
|
||||
|
||||
// GetById 根据ID获取角色信息
|
||||
func (s *sRole) GetById(ctx context.Context, id int) (*model.Role, error) {
|
||||
return dao.Role.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// List 获取角色列表
|
||||
func (s *sRole) List(ctx context.Context, req *model.RoleListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
roles, total, err := dao.Role.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: roles,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAll 获取所有启用的角色
|
||||
func (s *sRole) GetAll(ctx context.Context) ([]*model.Role, error) {
|
||||
return dao.Role.GetAll(ctx)
|
||||
}
|
||||
|
||||
// Create 创建角色
|
||||
func (s *sRole) Create(ctx context.Context, req *model.CreateRoleRequest) error {
|
||||
// 检查角色名称是否存在
|
||||
existRole, err := dao.Role.GetByName(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existRole != nil {
|
||||
return gerror.New("角色名称已存在")
|
||||
}
|
||||
|
||||
// 序列化权限列表
|
||||
permissionsJson, err := json.Marshal(req.Permissions)
|
||||
if err != nil {
|
||||
return gerror.New("权限数据格式错误")
|
||||
}
|
||||
|
||||
// 创建角色
|
||||
role := &model.Role{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Permissions: string(permissionsJson),
|
||||
Status: req.Status,
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
_, err = dao.Role.Create(ctx, role)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新角色
|
||||
func (s *sRole) Update(ctx context.Context, id int, req *model.UpdateRoleRequest) error {
|
||||
// 检查角色是否存在
|
||||
role, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if role == nil {
|
||||
return gerror.New("角色不存在")
|
||||
}
|
||||
|
||||
// 检查角色名称是否被其他角色使用
|
||||
existRole, err := dao.Role.GetByName(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existRole != nil && existRole.Id != id {
|
||||
return gerror.New("角色名称已被使用")
|
||||
}
|
||||
|
||||
// 序列化权限列表
|
||||
permissionsJson, err := json.Marshal(req.Permissions)
|
||||
if err != nil {
|
||||
return gerror.New("权限数据格式错误")
|
||||
}
|
||||
|
||||
// 更新角色
|
||||
updateData := g.Map{
|
||||
"name": req.Name,
|
||||
"description": req.Description,
|
||||
"permissions": string(permissionsJson),
|
||||
"status": req.Status,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.Role.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除角色
|
||||
func (s *sRole) Delete(ctx context.Context, id int) error {
|
||||
// 检查角色是否存在
|
||||
role, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if role == nil {
|
||||
return gerror.New("角色不存在")
|
||||
}
|
||||
|
||||
// TODO: 检查是否有用户使用该角色
|
||||
// 这里可以添加检查逻辑,防止删除正在使用的角色
|
||||
|
||||
return dao.Role.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新角色状态
|
||||
func (s *sRole) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
// 检查角色是否存在
|
||||
role, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if role == nil {
|
||||
return gerror.New("角色不存在")
|
||||
}
|
||||
|
||||
return dao.Role.UpdateStatus(ctx, id, status)
|
||||
}
|
||||
|
||||
// GetPermissions 获取所有可用权限
|
||||
func (s *sRole) GetPermissions(ctx context.Context) ([]model.PermissionGroup, error) {
|
||||
// 定义系统权限
|
||||
permissions := []model.PermissionGroup{
|
||||
{
|
||||
Name: "用户管理",
|
||||
Description: "用户相关权限",
|
||||
Permissions: []model.Permission{
|
||||
{Key: "user.list", Name: "查看用户列表", Description: "查看用户列表权限", Group: "用户管理"},
|
||||
{Key: "user.create", Name: "创建用户", Description: "创建用户权限", Group: "用户管理"},
|
||||
{Key: "user.update", Name: "编辑用户", Description: "编辑用户权限", Group: "用户管理"},
|
||||
{Key: "user.delete", Name: "删除用户", Description: "删除用户权限", Group: "用户管理"},
|
||||
{Key: "user.status", Name: "管理用户状态", Description: "管理用户状态权限", Group: "用户管理"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "角色管理",
|
||||
Description: "角色权限相关权限",
|
||||
Permissions: []model.Permission{
|
||||
{Key: "role.list", Name: "查看角色列表", Description: "查看角色列表权限", Group: "角色管理"},
|
||||
{Key: "role.create", Name: "创建角色", Description: "创建角色权限", Group: "角色管理"},
|
||||
{Key: "role.update", Name: "编辑角色", Description: "编辑角色权限", Group: "角色管理"},
|
||||
{Key: "role.delete", Name: "删除角色", Description: "删除角色权限", Group: "角色管理"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "内容管理",
|
||||
Description: "内容相关权限",
|
||||
Permissions: []model.Permission{
|
||||
{Key: "article.list", Name: "查看文章列表", Description: "查看文章列表权限", Group: "内容管理"},
|
||||
{Key: "article.create", Name: "创建文章", Description: "创建文章权限", Group: "内容管理"},
|
||||
{Key: "article.update", Name: "编辑文章", Description: "编辑文章权限", Group: "内容管理"},
|
||||
{Key: "article.delete", Name: "删除文章", Description: "删除文章权限", Group: "内容管理"},
|
||||
{Key: "news.list", Name: "查看新闻列表", Description: "查看新闻列表权限", Group: "内容管理"},
|
||||
{Key: "news.create", Name: "创建新闻", Description: "创建新闻权限", Group: "内容管理"},
|
||||
{Key: "news.update", Name: "编辑新闻", Description: "编辑新闻权限", Group: "内容管理"},
|
||||
{Key: "news.delete", Name: "删除新闻", Description: "删除新闻权限", Group: "内容管理"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "附件管理",
|
||||
Description: "附件相关权限",
|
||||
Permissions: []model.Permission{
|
||||
{Key: "attachment.list", Name: "查看附件列表", Description: "查看附件列表权限", Group: "附件管理"},
|
||||
{Key: "attachment.upload", Name: "上传附件", Description: "上传附件权限", Group: "附件管理"},
|
||||
{Key: "attachment.delete", Name: "删除附件", Description: "删除附件权限", Group: "附件管理"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "系统管理",
|
||||
Description: "系统相关权限",
|
||||
Permissions: []model.Permission{
|
||||
{Key: "config.list", Name: "查看系统配置", Description: "查看系统配置权限", Group: "系统管理"},
|
||||
{Key: "config.update", Name: "修改系统配置", Description: "修改系统配置权限", Group: "系统管理"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// CheckPermission 检查权限
|
||||
func (s *sRole) CheckPermission(ctx context.Context, roleId int, permission string) (bool, error) {
|
||||
return dao.Role.CheckPermission(ctx, roleId, permission)
|
||||
}
|
||||
311
internal/service/user.go
Normal file
311
internal/service/user.go
Normal file
@@ -0,0 +1,311 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cms-api/internal/dao"
|
||||
"cms-api/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sUser struct{}
|
||||
|
||||
func User() *sUser {
|
||||
return &sUser{}
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func (s *sUser) Login(ctx context.Context, req *model.UserLoginRequest) (*model.UserLoginResponse, error) {
|
||||
// 查询用户
|
||||
user, err := dao.User.GetByAccount(ctx, req.Account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, gerror.New("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if !s.VerifyPassword(req.Password, user.Password) {
|
||||
return nil, gerror.New("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 检查状态
|
||||
if user.Status != 0 {
|
||||
statusText := map[int]string{
|
||||
1: "账号已被冻结",
|
||||
2: "账号已被封号",
|
||||
3: "账号已注销",
|
||||
}
|
||||
return nil, gerror.New(statusText[user.Status])
|
||||
}
|
||||
|
||||
// 更新登录信息
|
||||
err = dao.User.Update(ctx, user.Id, g.Map{
|
||||
"ip": g.RequestFromCtx(ctx).GetClientIp(),
|
||||
"updated_at": gtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "更新用户登录信息失败:", err)
|
||||
}
|
||||
|
||||
// 生成token (暂时使用简单的token生成)
|
||||
token := gmd5.MustEncrypt(gconv.String(user.Id) + "_" + gconv.String(gtime.Now().Unix()))
|
||||
|
||||
// 清除密码字段
|
||||
user.Password = ""
|
||||
|
||||
return &model.UserLoginResponse{
|
||||
Token: token,
|
||||
ExpiresIn: 7200, // 2小时
|
||||
User: user,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetById 根据ID获取用户信息
|
||||
func (s *sUser) GetById(ctx context.Context, id int) (*model.User, error) {
|
||||
user, err := dao.User.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// 清除密码字段
|
||||
user.Password = ""
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// List 获取用户列表
|
||||
func (s *sUser) List(ctx context.Context, req *model.UserListRequest) (*model.PageResponse, error) {
|
||||
var (
|
||||
page = req.Page
|
||||
pageSize = req.PageSize
|
||||
)
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 设置分页参数
|
||||
req.Page = page
|
||||
req.PageSize = pageSize
|
||||
|
||||
// 获取列表
|
||||
users, total, err := dao.User.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 清除密码字段
|
||||
for _, user := range users {
|
||||
user.Password = ""
|
||||
}
|
||||
|
||||
return &model.PageResponse{
|
||||
List: users,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: (total + pageSize - 1) / pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (s *sUser) Create(ctx context.Context, req *model.CreateUserRequest) error {
|
||||
// 检查账号是否存在
|
||||
existUser, err := dao.User.GetByAccount(ctx, req.Account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existUser != nil {
|
||||
return gerror.New("账号已存在")
|
||||
}
|
||||
|
||||
// 检查邮箱是否存在
|
||||
existUser, err = dao.User.GetByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existUser != nil {
|
||||
return gerror.New("邮箱已存在")
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
hashedPassword := s.HashPassword(req.Password)
|
||||
|
||||
// 创建用户
|
||||
user := &model.User{
|
||||
Account: req.Account,
|
||||
NickName: req.NickName,
|
||||
Avatar: req.Avatar,
|
||||
Email: req.Email,
|
||||
Password: hashedPassword,
|
||||
Balance: req.Balance,
|
||||
RoleId: req.RoleId,
|
||||
IsSysNotifications: req.IsSysNotifications,
|
||||
IsCollectionNotifications: req.IsCollectionNotifications,
|
||||
IsMarketingNotifications: req.IsMarketingNotifications,
|
||||
Status: 0, // 默认正常状态
|
||||
CreatedAt: gtime.Now(),
|
||||
UpdatedAt: gtime.Now(),
|
||||
}
|
||||
|
||||
_, err = dao.User.Create(ctx, user)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update 更新用户
|
||||
func (s *sUser) Update(ctx context.Context, id int, req *model.UpdateUserRequest) error {
|
||||
// 检查用户是否存在
|
||||
user, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
return gerror.New("用户不存在")
|
||||
}
|
||||
|
||||
// 检查邮箱是否被其他用户使用
|
||||
existUser, err := dao.User.GetByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existUser != nil && existUser.Id != id {
|
||||
return gerror.New("邮箱已被使用")
|
||||
}
|
||||
|
||||
// 更新用户
|
||||
updateData := g.Map{
|
||||
"nick_name": req.NickName,
|
||||
"email": req.Email,
|
||||
"avatar": req.Avatar,
|
||||
"balance": req.Balance,
|
||||
"role_id": req.RoleId,
|
||||
"status": req.Status,
|
||||
"is_sys_notifications": req.IsSysNotifications,
|
||||
"is_collection_notifications": req.IsCollectionNotifications,
|
||||
"is_marketing_notifications": req.IsMarketingNotifications,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.User.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (s *sUser) Delete(ctx context.Context, id int) error {
|
||||
// 检查用户是否存在
|
||||
user, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
return gerror.New("用户不存在")
|
||||
}
|
||||
|
||||
return dao.User.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (s *sUser) ChangePassword(ctx context.Context, id int, req *model.ChangePasswordRequest) error {
|
||||
// 获取用户信息
|
||||
user, err := dao.User.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
return gerror.New("用户不存在")
|
||||
}
|
||||
|
||||
// 验证原密码
|
||||
if !s.VerifyPassword(req.OldPassword, user.Password) {
|
||||
return gerror.New("原密码错误")
|
||||
}
|
||||
|
||||
// 加密新密码
|
||||
hashedPassword := s.HashPassword(req.NewPassword)
|
||||
|
||||
// 更新密码
|
||||
updateData := g.Map{
|
||||
"password": hashedPassword,
|
||||
"last_reset_password_at": gtime.Now().Unix(),
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
|
||||
return dao.User.Update(ctx, id, updateData)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新用户状态
|
||||
func (s *sUser) UpdateStatus(ctx context.Context, id int, status int) error {
|
||||
// 检查用户是否存在
|
||||
user, err := s.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
return gerror.New("用户不存在")
|
||||
}
|
||||
|
||||
return dao.User.UpdateStatus(ctx, id, status)
|
||||
}
|
||||
|
||||
// HashPassword 加密密码
|
||||
func (s *sUser) HashPassword(password string) string {
|
||||
return gmd5.MustEncrypt(password + "cms_user_salt_2024")
|
||||
}
|
||||
|
||||
// VerifyPassword 验证密码
|
||||
func (s *sUser) VerifyPassword(password, hashedPassword string) bool {
|
||||
return gmd5.MustEncrypt(password+"cms_user_salt_2024") == hashedPassword
|
||||
}
|
||||
|
||||
// Profile 获取当前用户信息
|
||||
func (s *sUser) Profile(ctx context.Context) (*model.User, error) {
|
||||
userId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("user_id"))
|
||||
return s.GetById(ctx, userId)
|
||||
}
|
||||
|
||||
// UpdateProfile 更新当前用户信息
|
||||
func (s *sUser) UpdateProfile(ctx context.Context, req *model.UpdateUserRequest) error {
|
||||
userId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("user_id"))
|
||||
return s.Update(ctx, userId, req)
|
||||
}
|
||||
|
||||
// GetStats 获取用户统计信息
|
||||
func (s *sUser) GetStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 获取总用户数
|
||||
totalCount, err := dao.User.GetCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取各状态用户数
|
||||
normalCount, err := dao.User.GetCountByStatus(ctx, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frozenCount, err := dao.User.GetCountByStatus(ctx, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bannedCount, err := dao.User.GetCountByStatus(ctx, 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_count": totalCount,
|
||||
"normal_count": normalCount,
|
||||
"frozen_count": frozenCount,
|
||||
"banned_count": bannedCount,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user