初始化v1

This commit is contained in:
2025-08-03 00:11:15 +08:00
commit b88c8b8afa
171 changed files with 22353 additions and 0 deletions

131
internal/cmd/cmd.go Normal file
View File

@@ -0,0 +1,131 @@
package cmd
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"nl-video-api/internal/controller/admin"
"nl-video-api/internal/controller/user"
"nl-video-api/api/middleware"
"nl-video-api/api/v1"
)
var (
Main = gcmd.Command{
Name: "main",
Usage: "main",
Brief: "start http server",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
// 初始化日志配置
g.Log().Info(ctx, "开始初始化日志系统...")
s := g.Server()
// 设置静态文件服务
s.SetServerRoot("resource/public")
s.AddStaticPath("/uploads", "resource/public/uploads")
// 记录服务器启动日志
g.Log().Info(ctx, "服务器配置初始化完成")
// 注册中间件
s.Use(middleware.CORS)
s.Use(middleware.ErrorHandler) // 错误处理中间件
s.Use(middleware.ResponseHandler) // 响应处理中间件
// 注册路由
s.Group("/api/v1", func(group *ghttp.RouterGroup) {
// === 用户端路由 ===
// 用户认证路由(无需认证)
group.Group("/auth", func(authGroup *ghttp.RouterGroup) {
v1.AuthGroup(authGroup)
})
// 影片管理路由
v1.MovieGroup(group)
// 用户收藏路由
v1.UserCollectGroup(group)
// 用户观看历史路由
v1.UserWatchHistoryGroup(group)
// 轮播图路由
group.Group("/banner", func(bannerGroup *ghttp.RouterGroup) {
bannerGroup.GET("/list", user.Banner.GetList)
})
// 支付订单路由
group.Group("/payment", func(paymentGroup *ghttp.RouterGroup) {
paymentGroup.POST("/create", user.PaymentOrder.Create)
paymentGroup.GET("/list", user.PaymentOrder.GetList)
paymentGroup.GET("/{id}", user.PaymentOrder.GetDetail)
})
// VIP等级路由
group.Group("/vip", func(vipGroup *ghttp.RouterGroup) {
vipGroup.GET("/levels", user.VipLevel.GetList)
vipGroup.GET("/my", user.VipLevel.GetDetail)
})
// 附件管理路由
group.Group("/attachment", func(attachmentGroup *ghttp.RouterGroup) {
attachmentGroup.POST("/upload", user.NewAttachmentController().Upload)
attachmentGroup.GET("/list", user.NewAttachmentController().GetList)
})
// 系统配置路由
group.Group("/config", func(configGroup *ghttp.RouterGroup) {
configGroup.GET("/public", user.Config.GetList)
})
// 日志管理路由
group.Group("/log", func(logGroup *ghttp.RouterGroup) {
logGroup.GET("/my", user.Log.GetList)
})
// === 管理员端路由 ===
// 管理员认证路由(无需认证)
group.Group("/admin", func(adminGroup *ghttp.RouterGroup) {
// 管理员基础认证接口
adminController := &admin.AdminController{}
adminGroup.POST("/login", adminController.Login)
adminGroup.POST("/register", adminController.Register)
adminGroup.GET("/profile", adminController.Profile)
adminGroup.POST("/profile", adminController.UpdateProfile)
adminGroup.POST("/logout", adminController.Logout)
adminGroup.POST("/refresh", adminController.RefreshToken)
// 管理员业务功能路由
v1.AdminGroup(adminGroup)
})
})
s.Run()
return nil
},
}
// InitDB 数据库初始化命令
InitDB = gcmd.Command{
Name: "init-db",
Usage: "init-db",
Brief: "initialize database",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Info(ctx, "开始初始化数据库...")
if err := InitDatabase(ctx); err != nil {
g.Log().Errorf(ctx, "数据库初始化失败: %v", err)
return err
}
g.Log().Info(ctx, "数据库初始化完成!")
return nil
},
}
)

254
internal/cmd/init_db.go Normal file
View File

@@ -0,0 +1,254 @@
package cmd
import (
"context"
"fmt"
"io/ioutil"
"strings"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"golang.org/x/crypto/bcrypt"
)
// InitDatabase 初始化数据库
func InitDatabase(ctx context.Context) error {
db := g.DB()
// 检查数据库连接
if err := db.PingMaster(); err != nil {
return fmt.Errorf("数据库连接失败: %v", err)
}
g.Log().Info(ctx, "数据库连接成功,开始初始化数据库...")
// 读取SQL文件
sqlFile := "nl_video_database.sql"
if !gfile.Exists(sqlFile) {
return fmt.Errorf("SQL文件不存在: %s", sqlFile)
}
sqlContent, err := ioutil.ReadFile(sqlFile)
if err != nil {
return fmt.Errorf("读取SQL文件失败: %v", err)
}
// 分割SQL语句
sqlStatements := strings.Split(string(sqlContent), ";")
// 执行SQL语句
for i, statement := range sqlStatements {
statement = strings.TrimSpace(statement)
if statement == "" || strings.HasPrefix(statement, "--") || strings.HasPrefix(statement, "/*") {
continue
}
g.Log().Infof(ctx, "执行SQL语句 %d/%d", i+1, len(sqlStatements))
if _, err := db.Exec(ctx, statement); err != nil {
// 如果是表已存在的错误,跳过
if strings.Contains(err.Error(), "already exists") {
g.Log().Warning(ctx, "表已存在,跳过创建:", statement[:50])
continue
}
return fmt.Errorf("执行SQL失败: %v, SQL: %s", err, statement[:100])
}
}
// 初始化基础数据
if err := initBaseData(ctx); err != nil {
return fmt.Errorf("初始化基础数据失败: %v", err)
}
g.Log().Info(ctx, "数据库初始化完成!")
return nil
}
// initBaseData 初始化基础数据
func initBaseData(ctx context.Context) error {
db := g.DB()
// 检查是否已有管理员数据
count, err := db.Model("nl_admin").Count()
if err != nil {
return err
}
if count > 0 {
g.Log().Info(ctx, "管理员数据已存在,跳过初始化")
return nil
}
// 创建默认超级管理员
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("admin123"), bcrypt.DefaultCost)
if err != nil {
return err
}
// 插入超级管理员角色
roleResult, err := db.Model("nl_role").Data(g.Map{
"name": "超级管理员",
"code": "super_admin",
"level": 1,
"is_system": 1,
"description": "系统超级管理员,拥有所有权限",
"status": 1,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
return err
}
roleId, err := roleResult.LastInsertId()
if err != nil {
return err
}
// 插入超级管理员账号
_, err = db.Model("nl_admin").Data(g.Map{
"username": "admin",
"password": string(hashedPassword),
"email": "admin@nlvideo.com",
"real_name": "系统管理员",
"nickname": "超级管理员",
"role_id": roleId,
"status": 1,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
return err
}
// 初始化基础权限
if err := initPermissions(ctx, roleId); err != nil {
return err
}
// 初始化影片分类
if err := initCategories(ctx); err != nil {
return err
}
// 初始化VIP等级
if err := initVipLevels(ctx); err != nil {
return err
}
g.Log().Info(ctx, "基础数据初始化完成")
g.Log().Info(ctx, "默认管理员账号: admin")
g.Log().Info(ctx, "默认管理员密码: admin123")
return nil
}
// initPermissions 初始化权限数据
func initPermissions(ctx context.Context, roleId int64) error {
db := g.DB()
permissions := []g.Map{
{
"name": "系统管理", "code": "system", "type": "menu", "parent_id": 0,
"path": "/system", "component": "Layout", "icon": "system",
"sort": 1, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
{
"name": "用户管理", "code": "user", "type": "menu", "parent_id": 0,
"path": "/user", "component": "Layout", "icon": "user",
"sort": 2, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
{
"name": "影片管理", "code": "movie", "type": "menu", "parent_id": 0,
"path": "/movie", "component": "Layout", "icon": "movie",
"sort": 3, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
{
"name": "权限管理", "code": "permission", "type": "menu", "parent_id": 0,
"path": "/permission", "component": "Layout", "icon": "permission",
"sort": 4, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
}
for _, perm := range permissions {
result, err := db.Model("nl_permission").Data(perm).Insert()
if err != nil {
return err
}
permId, err := result.LastInsertId()
if err != nil {
return err
}
// 给超级管理员分配权限
_, err = db.Model("nl_role_permission").Data(g.Map{
"role_id": roleId,
"permission_id": permId,
"created_at": gtime.Now(),
}).Insert()
if err != nil {
return err
}
}
return nil
}
// initCategories 初始化影片分类
func initCategories(ctx context.Context) error {
db := g.DB()
categories := []g.Map{
{"name": "电影", "code": "movie", "parent_id": 0, "sort": 1, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now()},
{"name": "电视剧", "code": "tv", "parent_id": 0, "sort": 2, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now()},
{"name": "综艺", "code": "variety", "parent_id": 0, "sort": 3, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now()},
{"name": "动漫", "code": "anime", "parent_id": 0, "sort": 4, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now()},
{"name": "纪录片", "code": "documentary", "parent_id": 0, "sort": 5, "status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now()},
}
for _, category := range categories {
_, err := db.Model("nl_category").Data(category).Insert()
if err != nil {
return err
}
}
return nil
}
// initVipLevels 初始化VIP等级
func initVipLevels(ctx context.Context) error {
db := g.DB()
vipLevels := []g.Map{
{
"name": "普通用户", "level": 0, "price": 0, "duration": 0,
"description": "免费用户,可观看部分免费内容",
"privileges": `{"free_content": true, "hd_quality": false, "download": false}`,
"status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
{
"name": "月度VIP", "level": 1, "price": 1500, "duration": 30,
"description": "月度会员,享受高清观看和下载权限",
"privileges": `{"free_content": true, "hd_quality": true, "download": true, "ad_free": true}`,
"status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
{
"name": "年度VIP", "level": 2, "price": 15000, "duration": 365,
"description": "年度会员,享受所有内容和特权",
"privileges": `{"free_content": true, "hd_quality": true, "download": true, "ad_free": true, "exclusive_content": true}`,
"status": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(),
},
}
for _, vip := range vipLevels {
_, err := db.Model("nl_vip_level").Data(vip).Insert()
if err != nil {
return err
}
}
return nil
}

65
internal/consts/consts.go Normal file
View File

@@ -0,0 +1,65 @@
package consts
// 用户类型
const (
UserTypeUser = "user" // 普通用户
UserTypeAdmin = "admin" // 管理员
)
// 用户状态
const (
UserStatusNormal = 1 // 正常
UserStatusDisable = 2 // 禁用
)
// VIP等级
const (
VipLevelNormal = 1 // 普通用户
VipLevelGold = 2 // 黄金VIP
VipLevelPlatinum = 3 // 铂金VIP
VipLevelDiamond = 4 // 钻石VIP
)
// 影片状态
const (
MovieStatusDraft = 1 // 草稿
MovieStatusPublished = 2 // 已发布
MovieStatusOffline = 3 // 已下线
)
// 订单状态
const (
OrderStatusPending = 1 // 待支付
OrderStatusPaid = 2 // 已支付
OrderStatusCancelled = 3 // 已取消
OrderStatusRefunded = 4 // 已退款
)
// 支付方式
const (
PaymentTypeAlipay = "alipay" // 支付宝
PaymentTypeWechat = "wechat" // 微信支付
)
// 文件类型
const (
FileTypeImage = "image" // 图片
FileTypeVideo = "video" // 视频
FileTypeOther = "other" // 其他
)
// 缓存键前缀
const (
CacheKeyUserInfo = "user:info:"
CacheKeyMovieInfo = "movie:info:"
CacheKeyMovieList = "movie:list:"
CacheKeyConfig = "config:"
CacheKeyStatistics = "statistics:"
)
// 默认分页参数
const (
DefaultPage = 1
DefaultPageSize = 20
MaxPageSize = 100
)

View File

@@ -0,0 +1,370 @@
package admin
import (
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gconv"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/crypto"
"nl-video-api/utility/jwt"
"nl-video-api/utility/response"
)
type AdminController struct{}
// AdminLoginReq 管理员登录请求
type AdminLoginReq struct {
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
}
// AdminLoginRes 管理员登录响应
type AdminLoginRes struct {
Token string `json:"token"`
AdminInfo *entity.NlAdmin `json:"admin_info"`
}
// AdminRegisterReq 管理员注册请求
type AdminRegisterReq struct {
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
Email string `json:"email" v:"required|email#邮箱不能为空|邮箱格式不正确"`
RealName string `json:"real_name" v:"required|length:2,10#真实姓名不能为空|真实姓名长度为2-10位"`
}
// AdminProfileRes 管理员信息响应
type AdminProfileRes struct {
AdminInfo *entity.NlAdmin `json:"admin_info"`
}
// AdminUpdateReq 更新管理员信息请求
type AdminUpdateReq struct {
Email string `json:"email" v:"email#邮箱格式不正确"`
RealName string `json:"real_name" v:"length:2,10#真实姓名长度为2-10位"`
Avatar string `json:"avatar" v:"url#头像必须是有效的URL"`
}
// Login 管理员登录
func (c *AdminController) Login(r *ghttp.Request) {
ctx := r.Context()
var req AdminLoginReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数错误: "+err.Error())
return
}
// 检查数据库连接
if err := g.DB().PingMaster(); err != nil {
g.Log().Error(ctx, "数据库连接失败:", err)
response.Error(r, response.CodeInternalError, "数据库连接失败,请稍后重试")
return
}
// 查询管理员信息
var admin *entity.NlAdmin
err := g.DB().Model("nl_admin").Where("username", req.Username).Scan(&admin)
if err != nil {
g.Log().Error(ctx, "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "数据库查询失败")
return
}
if admin == nil {
response.Error(r, response.CodeInvalidParam, "用户名或密码错误")
return
}
// 验证密码
if !crypto.CheckPassword(req.Password, admin.Password) {
response.Error(r, response.CodeInvalidParam, "用户名或密码错误")
return
}
// 检查管理员状态
if admin.Status != 1 {
response.Error(r, response.CodeForbidden, "账号已被禁用")
return
}
// 生成JWT Token
token, err := jwt.GenerateToken(admin.Id, admin.Username, "admin")
if err != nil {
g.Log().Error(ctx, "生成Token失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 更新最后登录时间
now := int(time.Now().Unix())
_, err = g.DB().Model("nl_admin").Where("id", admin.Id).Update(g.Map{
"last_login_time": now,
"last_login_ip": r.GetClientIp(),
"updated_at": now,
})
if err != nil {
g.Log().Error(ctx, "更新登录时间失败:", err)
}
// 格式化管理员信息返回
adminMap := g.Map{
"id": admin.Id,
"username": admin.Username,
"nick_name": admin.NickName,
"avatar": admin.Avatar,
"phone": admin.Phone,
"email": admin.Email,
"role_id": admin.RoleId,
"department": admin.Department,
"status": admin.Status,
"last_login_time": response.FormatTimestamp(now),
"created_at": response.FormatTimestamp(admin.CreatedAt),
"updated_at": response.FormatTimestamp(now),
}
response.Success(r, g.Map{
"token": token,
"admin_info": adminMap,
})
}
// Register 管理员注册
func (c *AdminController) Register(r *ghttp.Request) {
ctx := r.Context()
var req AdminRegisterReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数错误: "+err.Error())
return
}
// 检查用户名是否已存在
count, err := g.DB().Model("nl_admin").Where("username", req.Username).Count()
if err != nil {
g.Log().Error(ctx, "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "用户名已存在")
return
}
// 检查邮箱是否已存在
count, err = g.DB().Model("nl_admin").Where("email", req.Email).Count()
if err != nil {
g.Log().Error(ctx, "查询邮箱失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "邮箱已存在")
return
}
// 加密密码
hashedPassword, err := crypto.HashPassword(req.Password)
if err != nil {
g.Log().Error(ctx, "密码加密失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 创建管理员
now := int(time.Now().Unix())
adminData := g.Map{
"username": req.Username,
"password": hashedPassword,
"email": req.Email,
"nick_name": req.RealName,
"role_id": 1, // 默认角色ID
"status": 1, // 默认启用
"created_at": now,
"updated_at": now,
}
result, err := g.DB().Model("nl_admin").Insert(adminData)
if err != nil {
g.Log().Error(ctx, "创建管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 获取新创建的管理员ID
adminId, err := result.LastInsertId()
if err != nil {
g.Log().Error(ctx, "获取管理员ID失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 生成JWT Token
token, err := jwt.GenerateToken(gconv.Uint(adminId), req.Username, "admin")
if err != nil {
g.Log().Error(ctx, "生成Token失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 查询新创建的管理员信息
var admin *entity.NlAdmin
err = g.DB().Model("nl_admin").Where("id", adminId).Scan(&admin)
if err != nil {
g.Log().Error(ctx, "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 清除密码字段
admin.Password = ""
response.Success(r, AdminLoginRes{
Token: token,
AdminInfo: admin,
})
}
// Profile 获取管理员信息
func (c *AdminController) Profile(r *ghttp.Request) {
ctx := r.Context()
// 从上下文获取管理员ID
adminId := r.GetCtxVar("admin_id")
if adminId == nil {
response.Error(r, response.CodeUnauthorized, "未授权访问")
return
}
// 查询管理员信息
var admin *entity.NlAdmin
err := g.DB().Model("nl_admin").Where("id", adminId).Scan(&admin)
if err != nil {
g.Log().Error(ctx, "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if admin == nil {
response.Error(r, response.CodeNotFound, "管理员不存在")
return
}
// 清除密码字段
admin.Password = ""
response.Success(r, AdminProfileRes{
AdminInfo: admin,
})
}
// UpdateProfile 更新管理员信息
func (c *AdminController) UpdateProfile(r *ghttp.Request) {
ctx := r.Context()
var req AdminUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数错误: "+err.Error())
return
}
// 从上下文获取管理员ID
adminId := r.GetCtxVar("admin_id")
if adminId == nil {
response.Error(r, response.CodeUnauthorized, "未授权访问")
return
}
// 构建更新数据
updateData := g.Map{
"updated_at": int(time.Now().Unix()),
}
if req.Email != "" {
// 检查邮箱是否已被其他管理员使用
count, err := g.DB().Model("nl_admin").Where("email", req.Email).Where("id !=", adminId).Count()
if err != nil {
g.Log().Error(ctx, "查询邮箱失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "邮箱已被使用")
return
}
updateData["email"] = req.Email
}
if req.RealName != "" {
updateData["real_name"] = req.RealName
}
if req.Avatar != "" {
updateData["avatar"] = req.Avatar
}
// 更新管理员信息
_, err := g.DB().Model("nl_admin").Where("id", adminId).Update(updateData)
if err != nil {
g.Log().Error(ctx, "更新管理员信息失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 查询更新后的管理员信息
var admin *entity.NlAdmin
err = g.DB().Model("nl_admin").Where("id", adminId).Scan(&admin)
if err != nil {
g.Log().Error(ctx, "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
// 清除密码字段
admin.Password = ""
response.Success(r, AdminProfileRes{
AdminInfo: admin,
})
}
// Logout 管理员登出
func (c *AdminController) Logout(r *ghttp.Request) {
// 这里可以实现Token黑名单机制
// 目前简单返回成功
response.Success(r, nil)
}
// RefreshToken 刷新Token
func (c *AdminController) RefreshToken(r *ghttp.Request) {
// 从上下文获取管理员ID
adminId := r.GetCtxVar("admin_id")
if adminId == nil {
response.Error(r, response.CodeUnauthorized, "未授权访问")
return
}
// 查询管理员信息获取用户名
var admin *entity.NlAdmin
err := g.DB().Model("nl_admin").Where("id", adminId).Scan(&admin)
if err != nil {
g.Log().Error(r.Context(), "查询管理员失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if admin == nil {
response.Error(r, response.CodeNotFound, "管理员不存在")
return
}
// 生成新的Token
token, err := jwt.GenerateToken(admin.Id, admin.Username, "admin")
if err != nil {
g.Log().Error(r.Context(), "生成Token失败:", err)
response.Error(r, response.CodeInternalError, "系统错误")
return
}
response.Success(r, g.Map{
"token": token,
})
}

View File

@@ -0,0 +1,74 @@
package admin
import (
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
)
// AttachmentController 管理员附件管理控制器
type AttachmentController struct{}
// NewAttachmentController 创建管理员附件管理控制器实例
func NewAttachmentController() *AttachmentController {
return &AttachmentController{}
}
// GetList 获取附件列表
func (c *AttachmentController) GetList(r *ghttp.Request) {
service.NewAttachmentService().AdminGetList(r)
}
// GetDetail 获取附件详情
func (c *AttachmentController) GetDetail(r *ghttp.Request) {
service.NewAttachmentService().AdminGetDetail(r)
}
// Update 更新附件
func (c *AttachmentController) Update(r *ghttp.Request) {
service.NewAttachmentService().AdminUpdate(r)
}
// Delete 删除附件
func (c *AttachmentController) Delete(r *ghttp.Request) {
service.NewAttachmentService().AdminDelete(r)
}
// BatchDelete 批量删除附件
func (c *AttachmentController) BatchDelete(r *ghttp.Request) {
service.NewAttachmentService().AdminBatchDelete(r)
}
// Download 下载附件
func (c *AttachmentController) Download(r *ghttp.Request) {
service.NewAttachmentService().AdminDownload(r)
}
// Move 移动附件
func (c *AttachmentController) Move(r *ghttp.Request) {
service.NewAttachmentService().AdminMove(r)
}
// Copy 复制附件
func (c *AttachmentController) Copy(r *ghttp.Request) {
service.NewAttachmentService().AdminCopy(r)
}
// Rename 重命名附件
func (c *AttachmentController) Rename(r *ghttp.Request) {
service.NewAttachmentService().AdminRename(r)
}
// Search 搜索附件
func (c *AttachmentController) Search(r *ghttp.Request) {
service.NewAttachmentService().AdminSearch(r)
}
// GetCategoryList 获取附件分类列表
func (c *AttachmentController) GetCategoryList(r *ghttp.Request) {
service.NewAttachmentService().AdminGetCategoryList(r)
}
// GetStatistics 获取附件统计
func (c *AttachmentController) GetStatistics(r *ghttp.Request) {
service.NewAttachmentService().AdminGetStatistics(r)
}

View File

@@ -0,0 +1,162 @@
package admin
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
"nl-video-api/utility/response"
)
var Banner = cAdminBanner{}
type cAdminBanner struct{}
// Create 创建轮播图
func (c *cAdminBanner) Create(r *ghttp.Request) {
service.Banner.AdminCreate(r)
}
// Update 更新轮播图
func (c *cAdminBanner) Update(r *ghttp.Request) {
var req *service.AdminBannerUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = uint(id)
}
}
err := service.Banner.AdminUpdate(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// Delete 删除轮播图
func (c *cAdminBanner) Delete(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "轮播图ID无效")
return
}
req := &service.AdminBannerDeleteReq{Id: uint(id)}
err = service.Banner.AdminDelete(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "删除成功",
})
}
// GetDetail 获取轮播图详情
func (c *cAdminBanner) GetDetail(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "轮播图ID无效")
return
}
req := &service.AdminBannerDetailReq{Id: uint(id)}
result, err := service.Banner.AdminGetDetail(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, result)
}
// GetList 获取轮播图列表
func (c *cAdminBanner) GetList(r *ghttp.Request) {
var req *service.AdminBannerListReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
result, err := service.Banner.AdminGetList(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, result)
}
// UpdateStatus 更新轮播图状态
func (c *cAdminBanner) UpdateStatus(r *ghttp.Request) {
var req *service.AdminBannerUpdateStatusReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = uint(id)
}
}
err := service.Banner.AdminUpdateStatus(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "状态更新成功",
})
}
// BatchDelete 批量删除轮播图
func (c *cAdminBanner) BatchDelete(r *ghttp.Request) {
var req *service.AdminBannerBatchDeleteReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
if len(req.Ids) == 0 {
response.Error(r, 1001, "轮播图ID列表不能为空")
return
}
err := service.Banner.AdminBatchDelete(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "批量删除成功",
})
}

View File

@@ -0,0 +1,27 @@
package admin
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// CommentController 管理员评论控制器
type CommentController struct{}
var Comment = &CommentController{}
// List 管理员评论列表
func (c *CommentController) List(r *ghttp.Request) {
service.Comment.AdminList(r)
}
// UpdateStatus 更新评论状态
func (c *CommentController) UpdateStatus(r *ghttp.Request) {
service.Comment.AdminUpdateStatus(r)
}
// BatchDelete 批量删除评论
func (c *CommentController) BatchDelete(r *ghttp.Request) {
service.Comment.AdminBatchDelete(r)
}

View File

@@ -0,0 +1,109 @@
package admin
import (
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
)
// ConfigController 系统配置管理控制器
type ConfigController struct{}
// NewConfigController 创建系统配置管理控制器实例
func NewConfigController() *ConfigController {
return &ConfigController{}
}
// List 获取配置列表
func (c *ConfigController) List(r *ghttp.Request) {
service.NewConfigService().AdminList(r)
}
// Detail 获取配置详情
func (c *ConfigController) Detail(r *ghttp.Request) {
service.NewConfigService().AdminDetail(r)
}
// Create 创建配置
func (c *ConfigController) Create(r *ghttp.Request) {
service.NewConfigService().AdminCreate(r)
}
// Update 更新配置
func (c *ConfigController) Update(r *ghttp.Request) {
service.NewConfigService().AdminUpdate(r)
}
// Delete 删除配置
func (c *ConfigController) Delete(r *ghttp.Request) {
service.NewConfigService().AdminDelete(r)
}
// BatchDelete 批量删除配置
func (c *ConfigController) BatchDelete(r *ghttp.Request) {
service.NewConfigService().AdminBatchDelete(r)
}
// GetByKey 根据键获取配置
func (c *ConfigController) GetByKey(r *ghttp.Request) {
service.NewConfigService().AdminGetByKey(r)
}
// GetByGroup 根据分组获取配置
func (c *ConfigController) GetByGroup(r *ghttp.Request) {
service.NewConfigService().AdminGetByGroup(r)
}
// Set 设置配置
func (c *ConfigController) Set(r *ghttp.Request) {
service.NewConfigService().AdminSet(r)
}
// BatchSet 批量设置配置
func (c *ConfigController) BatchSet(r *ghttp.Request) {
service.NewConfigService().AdminBatchSet(r)
}
// GetGroupList 获取配置分组列表
func (c *ConfigController) GetGroupList(r *ghttp.Request) {
service.NewConfigService().AdminGetGroupList(r)
}
// Export 导出配置
func (c *ConfigController) Export(r *ghttp.Request) {
service.NewConfigService().AdminExport(r)
}
// Import 导入配置
func (c *ConfigController) Import(r *ghttp.Request) {
service.NewConfigService().AdminImport(r)
}
// Cache 缓存配置
func (c *ConfigController) Cache(r *ghttp.Request) {
service.NewConfigService().AdminCache(r)
}
// ClearCache 清除配置缓存
func (c *ConfigController) ClearCache(r *ghttp.Request) {
service.NewConfigService().AdminClearCache(r)
}
// Validate 验证配置
func (c *ConfigController) Validate(r *ghttp.Request) {
service.NewConfigService().AdminValidate(r)
}
// Backup 备份配置
func (c *ConfigController) Backup(r *ghttp.Request) {
service.NewConfigService().AdminBackup(r)
}
// Restore 恢复配置
func (c *ConfigController) Restore(r *ghttp.Request) {
service.NewConfigService().AdminRestore(r)
}
// GetHistory 获取配置历史
func (c *ConfigController) GetHistory(r *ghttp.Request) {
service.NewConfigService().AdminGetHistory(r)
}

View File

@@ -0,0 +1,88 @@
package admin
import (
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
)
// LogController 日志管理控制器
type LogController struct{}
// NewLogController 创建日志管理控制器实例
func NewLogController() *LogController {
return &LogController{}
}
// ===== 管理员日志管理 =====
// AdminLogList 获取管理员日志列表
func (c *LogController) AdminLogList(r *ghttp.Request) {
service.NewLogService().AdminLogList(r)
}
// AdminLogDetail 获取管理员日志详情
func (c *LogController) AdminLogDetail(r *ghttp.Request) {
service.NewLogService().AdminLogDetail(r)
}
// AdminLogDelete 删除管理员日志
func (c *LogController) AdminLogDelete(r *ghttp.Request) {
service.NewLogService().AdminLogDelete(r)
}
// AdminLogBatchDelete 批量删除管理员日志
func (c *LogController) AdminLogBatchDelete(r *ghttp.Request) {
service.NewLogService().AdminLogBatchDelete(r)
}
// AdminLogClear 清空管理员日志
func (c *LogController) AdminLogClear(r *ghttp.Request) {
service.NewLogService().AdminLogClear(r)
}
// AdminLogExport 导出管理员日志
func (c *LogController) AdminLogExport(r *ghttp.Request) {
service.NewLogService().AdminLogExport(r)
}
// AdminLogStats 管理员日志统计
func (c *LogController) AdminLogStats(r *ghttp.Request) {
service.NewLogService().AdminLogStats(r)
}
// ===== 用户日志管理 =====
// UserLogList 获取用户日志列表
func (c *LogController) UserLogList(r *ghttp.Request) {
service.NewLogService().UserLogList(r)
}
// UserLogDetail 获取用户日志详情
func (c *LogController) UserLogDetail(r *ghttp.Request) {
service.NewLogService().UserLogDetail(r)
}
// UserLogDelete 删除用户日志
func (c *LogController) UserLogDelete(r *ghttp.Request) {
service.NewLogService().UserLogDelete(r)
}
// UserLogBatchDelete 批量删除用户日志
func (c *LogController) UserLogBatchDelete(r *ghttp.Request) {
service.NewLogService().UserLogBatchDelete(r)
}
// UserLogClear 清空用户日志
func (c *LogController) UserLogClear(r *ghttp.Request) {
service.NewLogService().UserLogClear(r)
}
// UserLogExport 导出用户日志
func (c *LogController) UserLogExport(r *ghttp.Request) {
service.NewLogService().UserLogExport(r)
}
// UserLogStats 用户日志统计
func (c *LogController) UserLogStats(r *ghttp.Request) {
service.NewLogService().UserLogStats(r)
}

View File

@@ -0,0 +1,74 @@
package admin
import (
"nl-video-api/internal/service"
"nl-video-api/utility/response"
"github.com/gogf/gf/v2/net/ghttp"
)
var PaymentOrder = cAdminPaymentOrder{}
type cAdminPaymentOrder struct{}
// GetList 管理员获取支付订单列表
func (c *cAdminPaymentOrder) GetList(r *ghttp.Request) {
service.NewPaymentOrderService().AdminGetList(r)
}
// GetDetail 管理员获取支付订单详情
func (c *cAdminPaymentOrder) GetDetail(r *ghttp.Request) {
service.NewPaymentOrderService().AdminGetDetail(r)
}
// Refund 管理员订单退款
func (c *cAdminPaymentOrder) Refund(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
reason := r.Get("reason").String()
if id == 0 {
response.Error(r, response.CodeInvalidParam, "订单ID不能为空")
return
}
req := &service.PaymentOrderRefundReq{
Id: id,
Reason: reason,
}
err := service.NewPaymentOrderService().AdminRefund(r.Context(), req)
if err != nil {
response.Error(r, response.CodeInternalError, err.Error())
return
}
response.Success(r, "退款成功")
}
// GetStatistics 获取支付统计
func (c *cAdminPaymentOrder) GetStatistics(r *ghttp.Request) {
startDate := r.Get("start_date").String()
endDate := r.Get("end_date").String()
// 设置默认时间范围
if startDate == "" {
startDate = "2024-01-01"
}
if endDate == "" {
endDate = "2024-12-31"
}
result, err := service.NewPaymentOrderService().GetStatistics(r.Context(), startDate, endDate)
if err != nil {
response.Error(r, response.CodeInternalError, err.Error())
return
}
response.Success(r, result)
}
// Create 创建支付订单
func (c *cAdminPaymentOrder) Create(r *ghttp.Request) {
service.NewPaymentOrderService().Create(r)
}

View File

@@ -0,0 +1,245 @@
package admin
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/dao"
"nl-video-api/internal/service/auth"
"nl-video-api/utility/response"
)
// PermissionController 权限控制器
type PermissionController struct{}
var Permission = &PermissionController{}
// Create 创建权限
func (c *PermissionController) Create(r *ghttp.Request) {
var req *auth.PermissionCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
id, err := auth.Permission.Create(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"id": id,
})
}
// Update 更新权限
func (c *PermissionController) Update(r *ghttp.Request) {
var req *auth.PermissionUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = id
}
}
if req.Id <= 0 {
response.Error(r, response.CodeInvalidParam, "权限ID无效")
return
}
err := auth.Permission.Update(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, "更新成功")
}
// GetById 获取权限详情
func (c *PermissionController) GetById(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, response.CodeInvalidParam, "权限ID无效")
return
}
permission, err := auth.Permission.GetById(r.Context(), id)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
if permission == nil {
response.Error(r, response.CodeNotFound, "权限不存在")
return
}
response.Success(r, permission)
}
// GetList 获取权限列表
func (c *PermissionController) GetList(r *ghttp.Request) {
var req *dao.PermissionListReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
permissions, total, err := auth.Permission.GetList(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": permissions,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
})
}
// GetTree 获取权限树形结构
func (c *PermissionController) GetTree(r *ghttp.Request) {
permissions, err := auth.Permission.GetTree(r.Context())
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"tree": permissions,
})
}
// Delete 删除权限
func (c *PermissionController) Delete(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, response.CodeInvalidParam, "权限ID无效")
return
}
err = auth.Permission.Delete(r.Context(), id)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, "删除成功")
}
// GetMenuPermissions 获取菜单权限
func (c *PermissionController) GetMenuPermissions(r *ghttp.Request) {
permissions, err := auth.Permission.GetMenuPermissions(r.Context())
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": permissions,
})
}
// GetApiPermissions 获取API权限
func (c *PermissionController) GetApiPermissions(r *ghttp.Request) {
permissions, err := auth.Permission.GetApiPermissions(r.Context())
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": permissions,
})
}
// GetUserPermissions 获取用户权限
func (c *PermissionController) GetUserPermissions(r *ghttp.Request) {
userIdStr := r.Get("user_id").String()
userId, err := strconv.Atoi(userIdStr)
if err != nil || userId <= 0 {
response.Error(r, response.CodeInvalidParam, "用户ID无效")
return
}
permissions, err := auth.Permission.GetUserPermissions(r.Context(), userId)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": permissions,
})
}
// CheckPermission 检查权限
func (c *PermissionController) CheckPermission(r *ghttp.Request) {
userIdStr := r.Get("user_id").String()
userId, err := strconv.Atoi(userIdStr)
if err != nil || userId <= 0 {
response.Error(r, response.CodeInvalidParam, "用户ID无效")
return
}
permissionCode := r.Get("permission_code").String()
if permissionCode == "" {
response.Error(r, response.CodeInvalidParam, "权限编码不能为空")
return
}
hasPermission, err := auth.Permission.CheckUserPermission(r.Context(), userId, permissionCode)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"has_permission": hasPermission,
})
}
// CheckApiPermission 检查API权限
func (c *PermissionController) CheckApiPermission(r *ghttp.Request) {
userIdStr := r.Get("user_id").String()
userId, err := strconv.Atoi(userIdStr)
if err != nil || userId <= 0 {
response.Error(r, response.CodeInvalidParam, "用户ID无效")
return
}
apiPath := r.Get("api_path").String()
if apiPath == "" {
response.Error(r, response.CodeInvalidParam, "API路径不能为空")
return
}
method := r.Get("method").String()
if method == "" {
response.Error(r, response.CodeInvalidParam, "请求方法不能为空")
return
}
hasPermission, err := auth.Permission.CheckApiPermission(r.Context(), userId, apiPath, method)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"has_permission": hasPermission,
})
}

View File

@@ -0,0 +1,270 @@
package admin
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/dao"
"nl-video-api/internal/service/auth"
"nl-video-api/utility/response"
)
// RoleController 角色控制器
type RoleController struct{}
var Role = &RoleController{}
// Create 创建角色
func (c *RoleController) Create(r *ghttp.Request) {
var req *auth.RoleCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
id, err := auth.Role.Create(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"id": id,
})
}
// Update 更新角色
func (c *RoleController) Update(r *ghttp.Request) {
var req *auth.RoleUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = id
}
}
if req.Id <= 0 {
response.Error(r, response.CodeInvalidParam, "角色ID无效")
return
}
err := auth.Role.Update(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, "更新成功")
}
// GetById 获取角色详情
func (c *RoleController) GetById(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, response.CodeInvalidParam, "角色ID无效")
return
}
role, err := auth.Role.GetById(r.Context(), id)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
if role == nil {
response.Error(r, response.CodeNotFound, "角色不存在")
return
}
response.Success(r, role)
}
// GetList 获取角色列表
func (c *RoleController) GetList(r *ghttp.Request) {
var req *dao.RoleListReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
roles, total, err := auth.Role.GetList(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": roles,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
})
}
// GetAll 获取所有角色
func (c *RoleController) GetAll(r *ghttp.Request) {
roles, err := auth.Role.GetAll(r.Context())
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": roles,
})
}
// Delete 删除角色
func (c *RoleController) Delete(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, response.CodeInvalidParam, "角色ID无效")
return
}
err = auth.Role.Delete(r.Context(), id)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, "删除成功")
}
// AssignPermissions 为角色分配权限
func (c *RoleController) AssignPermissions(r *ghttp.Request) {
var req *auth.RolePermissionReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取角色ID
if req.RoleId == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.RoleId = id
}
}
if req.RoleId <= 0 {
response.Error(r, response.CodeInvalidParam, "角色ID无效")
return
}
err := auth.Role.AssignPermissions(r.Context(), req)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, "权限分配成功")
}
// GetRolePermissions 获取角色权限
func (c *RoleController) GetRolePermissions(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, response.CodeInvalidParam, "角色ID无效")
return
}
permissions, err := auth.Role.GetRolePermissions(r.Context(), id)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
// 同时返回权限ID列表方便前端处理
permissionIds, _ := auth.Role.GetRolePermissionIds(r.Context(), id)
response.Success(r, g.Map{
"permissions": permissions,
"permission_ids": permissionIds,
})
}
// BatchUpdateStatus 批量更新角色状态
func (c *RoleController) BatchUpdateStatus(r *ghttp.Request) {
type BatchUpdateReq struct {
Ids []int `json:"ids" v:"required#请选择要操作的角色"`
Status int `json:"status" v:"required|in:0,1#状态只能为0或1"`
}
var req BatchUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
if len(req.Ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要操作的角色")
return
}
err := auth.Role.BatchUpdateStatus(r.Context(), req.Ids, req.Status)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"count": len(req.Ids),
})
}
// CopyRole 复制角色
func (c *RoleController) CopyRole(r *ghttp.Request) {
type CopyRoleReq struct {
SourceId int `json:"source_id" v:"required|min:1#源角色ID不能为空"`
Name string `json:"name" v:"required|length:2,50#角色名称不能为空|角色名称长度为2-50位"`
Code string `json:"code" v:"required|length:2,50#角色编码不能为空|角色编码长度为2-50位"`
}
var req CopyRoleReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, "参数解析失败: "+err.Error())
return
}
newRoleId, err := auth.Role.CopyRole(r.Context(), req.SourceId, req.Name, req.Code)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"id": newRoleId,
})
}
// GetRolesByLevel 根据等级获取角色
func (c *RoleController) GetRolesByLevel(r *ghttp.Request) {
levelStr := r.Get("level").String()
level, err := strconv.Atoi(levelStr)
if err != nil || level <= 0 {
response.Error(r, response.CodeInvalidParam, "角色等级无效")
return
}
roles, err := auth.Role.GetRolesByLevel(r.Context(), level)
if err != nil {
response.Error(r, response.CodeError, err.Error())
return
}
response.Success(r, g.Map{
"list": roles,
"level": level,
})
}

View File

@@ -0,0 +1,295 @@
package admin
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/service/user"
"nl-video-api/utility/crypto"
"nl-video-api/utility/response"
)
var User = cAdminUser{}
type cAdminUser struct{}
// AdminUserCreateReq 管理员创建用户请求
type AdminUserCreateReq struct {
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
Email string `json:"email" v:"email#邮箱格式不正确"`
Phone string `json:"phone" v:"phone#手机号格式不正确"`
Status int `json:"status" v:"in:0,1#状态值不正确"`
}
// AdminUserUpdateReq 管理员更新用户请求
type AdminUserUpdateReq struct {
Id uint `json:"id" v:"required|min:1#用户ID不能为空"`
Username string `json:"username" v:"length:3,20#用户名长度为3-20位"`
Email string `json:"email" v:"email#邮箱格式不正确"`
Phone string `json:"phone" v:"phone#手机号格式不正确"`
Status int `json:"status" v:"in:0,1#状态值不正确"`
}
// AdminUserDetailReq 管理员获取用户详情请求
type AdminUserDetailReq struct {
Id uint `json:"id" v:"required|min:1#用户ID不能为空"`
}
// AdminUserListReq 管理员获取用户列表请求
type AdminUserListReq struct {
Page int `json:"page" v:"min:1#页码不能小于1"`
Size int `json:"size" v:"min:1|max:100#每页数量不能小于1且不能大于100"`
Username string `json:"username"`
Email string `json:"email"`
Status int `json:"status" v:"in:-1,0,1#状态值不正确"`
}
// AdminUserDeleteReq 管理员删除用户请求
type AdminUserDeleteReq struct {
Id uint `json:"id" v:"required|min:1#用户ID不能为空"`
}
// Create 管理员创建用户
func (c *cAdminUser) Create(r *ghttp.Request) {
var req *AdminUserCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 构造用户创建请求
createReq := &user.UserCreateReq{
Username: req.Username,
Password: req.Password,
Email: req.Email,
Phone: req.Phone,
}
// 调用用户服务创建用户
_, err := user.User.Create(r.Context(), createReq)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "创建成功",
})
}
// Update 管理员更新用户
func (c *cAdminUser) Update(r *ghttp.Request) {
var req *AdminUserUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = uint(id)
}
}
if req.Id == 0 {
response.Error(r, 1001, "用户ID不能为空")
return
}
// 构造用户更新请求
updateReq := &user.UserUpdateReq{
Id: int(req.Id),
Username: req.Username,
Email: req.Email,
Phone: req.Phone,
Status: req.Status,
}
// 调用用户服务更新用户
err := user.User.Update(r.Context(), updateReq)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// GetDetail 管理员获取用户详情
func (c *cAdminUser) GetDetail(r *ghttp.Request) {
var req *AdminUserDetailReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = uint(id)
}
}
if req.Id == 0 {
response.Error(r, 1001, "用户ID不能为空")
return
}
// 调用用户服务获取用户详情
result, err := user.User.GetById(r.Context(), int(req.Id))
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, result)
}
// GetList 管理员获取用户列表
func (c *cAdminUser) GetList(r *ghttp.Request) {
var req *AdminUserListReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
// 构造用户列表请求
listReq := &dao.UserListReq{
Page: req.Page,
Username: req.Username,
Email: req.Email,
Status: req.Status,
}
// 调用用户服务获取用户列表
result, total, err := user.User.GetList(r.Context(), listReq)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
// 构造响应数据
responseData := g.Map{
"list": result,
"total": total,
"page": req.Page,
"size": req.Size,
}
response.Success(r, responseData)
}
// Delete 管理员删除用户
func (c *cAdminUser) Delete(r *ghttp.Request) {
var req *AdminUserDeleteReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = uint(id)
}
}
if req.Id == 0 {
response.Error(r, 1001, "用户ID不能为空")
return
}
// 调用用户服务删除用户
err := user.User.Delete(r.Context(), int(req.Id))
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "删除成功",
})
}
// UpdateStatus 管理员更新用户状态
func (c *cAdminUser) UpdateStatus(r *ghttp.Request) {
id := r.Get("id").Uint()
status := r.Get("status").Int()
if id == 0 {
response.Error(r, 1001, "用户ID不能为空")
return
}
// 构造用户更新请求
updateReq := &user.UserUpdateReq{
Id: int(id),
Status: status,
}
// 调用用户服务更新用户
err := user.User.Update(r.Context(), updateReq)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "状态更新成功",
})
}
// ResetPassword 管理员重置用户密码
func (c *cAdminUser) ResetPassword(r *ghttp.Request) {
id := r.Get("id").Uint()
newPassword := r.Get("password").String()
if id == 0 {
response.Error(r, 1001, "用户ID不能为空")
return
}
if newPassword == "" {
response.Error(r, 1001, "新密码不能为空")
return
}
// 直接更新密码,管理员重置不需要原密码验证
hashedPassword, err := crypto.HashPassword(newPassword)
if err != nil {
response.Error(r, 1002, "密码加密失败: "+err.Error())
return
}
// 更新用户密码
_, err = g.DB().Model("nl_user").Ctx(r.Context()).Where("id", id).Data(g.Map{
"password": hashedPassword,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "密码重置成功",
})
}

View File

@@ -0,0 +1,59 @@
package admin
import (
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
)
// VipLevelController VIP等级控制器管理端
type VipLevelController struct{}
// NewVipLevelController 创建VIP等级控制器实例
func NewVipLevelController() *VipLevelController {
return &VipLevelController{}
}
// Create 创建VIP等级
func (c *VipLevelController) Create(r *ghttp.Request) {
service.VipLevel.AdminCreate(r)
}
// Update 更新VIP等级
func (c *VipLevelController) Update(r *ghttp.Request) {
service.VipLevel.AdminUpdate(r)
}
// List 获取VIP等级列表
func (c *VipLevelController) List(r *ghttp.Request) {
service.VipLevel.AdminGetList(r)
}
// Detail 获取VIP等级详情
func (c *VipLevelController) Detail(r *ghttp.Request) {
service.VipLevel.GetDetail(r)
}
// Delete 删除VIP等级
func (c *VipLevelController) Delete(r *ghttp.Request) {
service.VipLevel.AdminDelete(r)
}
// UpdateStatus 更新VIP等级状态
func (c *VipLevelController) UpdateStatus(r *ghttp.Request) {
service.VipLevel.AdminUpdateStatus(r)
}
// BatchDelete 批量删除VIP等级
func (c *VipLevelController) BatchDelete(r *ghttp.Request) {
service.VipLevel.AdminBatchDelete(r)
}
// GetAll 获取所有VIP等级
func (c *VipLevelController) GetAll(r *ghttp.Request) {
service.VipLevel.GetAll(r)
}
// GetActiveList 获取启用的VIP等级列表
func (c *VipLevelController) GetActiveList(r *ghttp.Request) {
service.VipLevel.GetActiveList(r)
}

View File

@@ -0,0 +1,283 @@
package auth
import (
"nl-video-api/internal/consts"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/crypto"
"nl-video-api/utility/jwt"
"nl-video-api/utility/response"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
type cAuth struct{}
var Auth = cAuth{}
// LoginReq 登录请求
type LoginReq struct {
Username string `json:"username" v:"required#用户名不能为空"`
Password string `json:"password" v:"required#密码不能为空"`
}
// RegisterReq 注册请求
type RegisterReq struct {
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
Phone string `json:"phone" v:"required|phone#手机号不能为空|手机号格式错误"`
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
Code string `json:"code" v:"required#验证码不能为空"`
}
// UpdateProfileReq 更新资料请求
type UpdateProfileReq struct {
Nickname string `json:"nickname" v:"length:1,20#昵称长度为1-20位"`
Avatar string `json:"avatar" v:"url#头像格式错误"`
Gender int `json:"gender" v:"in:0,1,2#性别参数错误"`
Birthday string `json:"birthday" v:"date#生日格式错误"`
}
// Login 用户登录
func (c *cAuth) Login(r *ghttp.Request) {
var req LoginReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 查询用户
var user entity.NlUser
err := g.DB().Model("nl_user").Where("username = ? OR phone = ?", req.Username, req.Username).Scan(&user)
if err != nil || user.Id == 0 {
response.Error(r, response.CodeError, "用户名或密码错误")
return
}
// 验证密码
if !crypto.CheckPassword(req.Password, user.Password) {
response.Error(r, response.CodeError, "用户名或密码错误")
return
}
// 检查用户状态
if user.Status != consts.UserStatusNormal {
response.Error(r, response.CodeError, "账号已被禁用")
return
}
// 生成Token
token, err := jwt.GenerateToken(user.Id, user.Username, consts.UserTypeUser)
if err != nil {
response.Error(r, response.CodeServerError, "Token生成失败")
return
}
// 更新最后登录信息
now := int(time.Now().Unix())
clientIP := r.GetClientIp()
// 将IPv6地址::1转换为IPv4地址127.0.0.1或者使用IP地址的哈希值
if clientIP == "::1" {
clientIP = "127.0.0.1"
}
g.DB().Model("nl_user").Where("id", user.Id).Update(g.Map{
"last_login_time": now,
"last_login_ip": clientIP,
"login_count": g.DB().Raw("login_count + 1"),
})
// 格式化用户信息返回
userMap := g.Map{
"id": user.Id,
"username": user.Username,
"nick_name": user.NickName,
"avatar": user.Avatar,
"phone": user.Phone,
"email": user.Email,
"gender": user.Gender,
"vip_level": user.VipLevel,
"vip_expire_time": response.FormatTimestamp(user.VipExpireTime),
"balance": user.Balance,
"points": user.Points,
"status": user.Status,
"last_login_time": response.FormatTimestamp(now),
"created_at": response.FormatTimestamp(user.CreatedAt),
"updated_at": response.FormatTimestamp(user.UpdatedAt),
}
response.Success(r, g.Map{
"token": token,
"user": userMap,
})
}
// Register 用户注册
func (c *cAuth) Register(r *ghttp.Request) {
var req RegisterReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 验证验证码(这里简化处理,实际应该验证短信验证码)
if req.Code != "123456" {
response.Error(r, response.CodeError, "验证码错误")
return
}
// 检查用户名是否存在
count, _ := g.DB().Model("nl_user").Where("username", req.Username).Count()
if count > 0 {
response.Error(r, response.CodeError, "用户名已存在")
return
}
// 检查手机号是否存在
count, _ = g.DB().Model("nl_user").Where("phone", req.Phone).Count()
if count > 0 {
response.Error(r, response.CodeError, "手机号已注册")
return
}
// 加密密码
hashedPassword, err := crypto.HashPassword(req.Password)
if err != nil {
response.Error(r, response.CodeServerError, "密码加密失败")
return
}
// 创建用户
now := int(time.Now().Unix())
userId, err := g.DB().Model("nl_user").InsertAndGetId(g.Map{
"username": req.Username,
"nick_name": req.Username,
"phone": req.Phone,
"password": hashedPassword,
"vip_level": consts.VipLevelNormal,
"status": consts.UserStatusNormal,
"created_at": now,
"updated_at": now,
})
if err != nil {
response.Error(r, response.CodeServerError, "注册失败")
return
}
// 生成Token
token, err := jwt.GenerateToken(uint(userId), req.Username, consts.UserTypeUser)
if err != nil {
response.Error(r, response.CodeServerError, "Token生成失败")
return
}
response.Success(r, g.Map{
"token": token,
"user_id": userId,
"message": "注册成功",
})
}
// Profile 获取用户信息
func (c *cAuth) Profile(r *ghttp.Request) {
userId := r.GetCtxVar("user_id").Uint()
var user entity.NlUser
err := g.DB().Model("nl_user").Where("id", userId).Scan(&user)
if err != nil {
response.Error(r, response.CodeServerError, "获取用户信息失败")
return
}
// 格式化用户信息返回
userMap := g.Map{
"id": user.Id,
"username": user.Username,
"nick_name": user.NickName,
"avatar": user.Avatar,
"phone": user.Phone,
"email": user.Email,
"gender": user.Gender,
"birthday": user.Birthday,
"vip_level": user.VipLevel,
"vip_expire_time": response.FormatTimestamp(user.VipExpireTime),
"balance": user.Balance,
"points": user.Points,
"status": user.Status,
"last_login_time": response.FormatTimestamp(user.LastLoginTime),
"login_count": user.LoginCount,
"desc": user.Desc,
"created_at": response.FormatTimestamp(user.CreatedAt),
"updated_at": response.FormatTimestamp(user.UpdatedAt),
}
response.Success(r, userMap)
}
// UpdateProfile 更新用户信息
func (c *cAuth) UpdateProfile(r *ghttp.Request) {
var req UpdateProfileReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
userId := r.GetCtxVar("user_id").Uint()
// 构建更新数据
updateData := g.Map{
"updated_at": int(time.Now().Unix()),
}
if req.Nickname != "" {
updateData["nickname"] = req.Nickname
}
if req.Avatar != "" {
updateData["avatar"] = req.Avatar
}
if req.Gender > 0 {
updateData["gender"] = req.Gender
}
if req.Birthday != "" {
updateData["birthday"] = req.Birthday
}
// 更新用户信息
_, err := g.DB().Model("nl_user").Where("id", userId).Update(updateData)
if err != nil {
response.Error(r, response.CodeServerError, "更新失败")
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// Logout 用户登出
func (c *cAuth) Logout(r *ghttp.Request) {
// 这里可以将token加入黑名单简化处理直接返回成功
response.Success(r, g.Map{
"message": "登出成功",
})
}
// RefreshToken 刷新Token
func (c *cAuth) RefreshToken(r *ghttp.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
response.Error(r, response.CodeUnauthorized, "请提供认证令牌")
return
}
tokenString := authHeader[7:] // 去掉 "Bearer "
newToken, err := jwt.RefreshToken(tokenString)
if err != nil {
response.Error(r, response.CodeTokenInvalid, "Token刷新失败")
return
}
response.Success(r, g.Map{
"token": newToken,
})
}

View File

@@ -0,0 +1,5 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package hello

View File

@@ -0,0 +1,16 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package hello
import (
"nl-video-api/api/hello"
)
type ControllerV1 struct{}
func NewV1() hello.IHelloV1 {
return &ControllerV1{}
}

View File

@@ -0,0 +1,13 @@
package hello
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"nl-video-api/api/hello/v1"
)
func (c *ControllerV1) Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) {
g.RequestFromCtx(ctx).Response.Writeln("Hello World!")
return
}

View File

@@ -0,0 +1,337 @@
package movie
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/service/movie"
"nl-video-api/utility/response"
)
// EpisodeController 集数控制器
type EpisodeController struct{}
var Episode = &EpisodeController{}
// Create 创建集数
func (c *EpisodeController) Create(r *ghttp.Request) {
var req *movie.EpisodeCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
episodeService := movie.NewEpisodeService()
err := episodeService.Create(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "创建成功",
"id": req.MovieId,
})
}
// Update 更新集数
func (c *EpisodeController) Update(r *ghttp.Request) {
var req *movie.EpisodeUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = id
}
}
if req.Id <= 0 {
response.Error(r, 1001, "集数ID无效")
return
}
episodeService := movie.NewEpisodeService()
err := episodeService.Update(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// GetById 获取集数详情
func (c *EpisodeController) GetById(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "集数ID无效")
return
}
episodeService := movie.NewEpisodeService()
episode, err := episodeService.GetById(r.Context(), &movie.EpisodeDetailReq{Id: id})
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": episode,
})
}
// GetByMovieId 根据影片ID获取集数列表
func (c *EpisodeController) GetByMovieId(r *ghttp.Request) {
movieIdStr := r.Get("movie_id").String()
movieId, err := strconv.Atoi(movieIdStr)
if err != nil || movieId <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
episodeService := movie.NewEpisodeService()
episodes, err := episodeService.GetByMovieId(r.Context(), movieId)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"list": episodes,
"total": len(episodes),
"movie_id": movieId,
})
}
// Delete 删除集数
func (c *EpisodeController) Delete(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "集数ID无效")
return
}
episodeService := movie.NewEpisodeService()
err = episodeService.Delete(r.Context(), &movie.EpisodeDeleteReq{Id: id})
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "删除成功",
})
}
// BatchCreate 批量创建集数
func (c *EpisodeController) BatchCreate(r *ghttp.Request) {
type BatchCreateReq struct {
MovieId int `json:"movie_id" v:"required|min:1#请选择影片"`
Episodes []movie.EpisodeCreateReq `json:"episodes" v:"required#集数列表不能为空"`
}
var req BatchCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
if len(req.Episodes) == 0 {
response.Error(r, 1001, "集数列表不能为空")
return
}
// 设置影片ID
for i := range req.Episodes {
req.Episodes[i].MovieId = req.MovieId
}
episodeService := movie.NewEpisodeService()
// 由于BatchCreate方法不存在我们逐个创建
successCount := 0
for _, episodeReq := range req.Episodes {
if err := episodeService.Create(r.Context(), &episodeReq); err != nil {
g.Log().Errorf(r.Context(), "创建集数失败: %v", err)
} else {
successCount++
}
}
response.Success(r, g.Map{
"message": "批量创建完成",
"movie_id": req.MovieId,
"total_count": len(req.Episodes),
"success_count": successCount,
"fail_count": len(req.Episodes) - successCount,
})
}
// UploadVideo 上传集数视频
func (c *EpisodeController) UploadVideo(r *ghttp.Request) {
file := r.GetUploadFile("video")
if file == nil {
response.Error(r, 1001, "请选择要上传的视频文件")
return
}
// 验证文件类型
allowedTypes := []string{"video/mp4", "video/avi", "video/mkv", "video/mov", "video/wmv"}
isValidType := false
for _, allowedType := range allowedTypes {
if file.Header.Get("Content-Type") == allowedType {
isValidType = true
break
}
}
if !isValidType {
response.Error(r, 1001, "不支持的视频格式")
return
}
// 检查文件大小限制5GB
maxSize := int64(5 * 1024 * 1024 * 1024)
if file.Size > maxSize {
response.Error(r, 1001, "视频文件过大最大支持5GB")
return
}
// 生成文件名
timestamp := gtime.Now().TimestampStr()
filename := timestamp + "_" + file.Filename
uploadPath := "resource/public/uploads/episodes/" + filename
// 保存文件
if _, err := file.Save(uploadPath); err != nil {
response.Error(r, 1002, "视频上传失败: "+err.Error())
return
}
// 返回文件信息
relativePath := "/uploads/episodes/" + filename
response.Success(r, g.Map{
"message": "上传成功",
"url": relativePath,
"filename": filename,
"size": file.Size,
})
}
// GenerateThumbnail 生成缩略图
func (c *EpisodeController) GenerateThumbnail(r *ghttp.Request) {
videoUrl := r.Get("video_url").String()
if videoUrl == "" {
response.Error(r, 1001, "视频地址不能为空")
return
}
// 生成缩略图文件路径
timestamp := gtime.Now().TimestampStr()
filename := "thumb_" + timestamp + ".jpg"
thumbDir := "resource/public/uploads/thumbnails"
_ = thumbDir + "/" + filename // 避免未使用变量错误
// 提取缩略图 (这里需要实际的视频处理库)
// if err := video.ExtractCover(videoUrl, thumbPath); err != nil {
// response.Error(r, 1002, "生成缩略图失败: "+err.Error())
// return
// }
// 返回缩略图信息
relativePath := "/uploads/thumbnails/" + filename
response.Success(r, g.Map{
"message": "生成成功",
"thumbnail": relativePath,
"filename": filename,
})
}
// GetVideoInfo 获取视频信息
func (c *EpisodeController) GetVideoInfo(r *ghttp.Request) {
videoUrl := r.Get("video_url").String()
if videoUrl == "" {
response.Error(r, 1001, "视频地址不能为空")
return
}
// 获取视频信息 (这里需要实际的视频处理库)
// videoInfo, err := video.GetVideoInfo(videoUrl)
// if err != nil {
// response.Error(r, 1002, "获取视频信息失败: "+err.Error())
// return
// }
// 模拟返回视频信息
videoInfo := g.Map{
"duration": 0,
"width": 1920,
"height": 1080,
"bitrate": "2000kbps",
"format": "mp4",
"size": 0,
"created_at": "",
}
response.Success(r, g.Map{
"message": "获取成功",
"data": videoInfo,
})
}
// BatchUpdateStatus 批量更新集数状态
func (c *EpisodeController) BatchUpdateStatus(r *ghttp.Request) {
type BatchUpdateReq struct {
Ids []int `json:"ids" v:"required#请选择要操作的集数"`
Status int `json:"status" v:"required|in:0,1#状态只能为0或1"`
}
var req BatchUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
if len(req.Ids) == 0 {
response.Error(r, 1001, "请选择要操作的集数")
return
}
ctx := r.Context()
successCount := 0
failCount := 0
for _, id := range req.Ids {
updateData := g.Map{
"status": req.Status,
}
episodeDao := dao.NewEpisodeDao()
if err := episodeDao.Update(ctx, id, updateData); err != nil {
g.Log().Errorf(ctx, "批量更新集数状态失败: ID=%d, 错误=%v", id, err)
failCount++
} else {
successCount++
}
}
response.Success(r, g.Map{
"message": "批量更新完成",
"success_count": successCount,
"fail_count": failCount,
"total_count": len(req.Ids),
})
}

View File

@@ -0,0 +1,418 @@
package movie
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service/movie"
"nl-video-api/utility/response"
)
// MovieController 影片控制器
type MovieController struct{}
var Movie = &MovieController{}
// Create 创建影片
func (c *MovieController) Create(r *ghttp.Request) {
var req *movie.MovieCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
movieService := movie.NewMovieService()
err := movieService.Create(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "创建成功",
})
}
// Update 更新影片
func (c *MovieController) Update(r *ghttp.Request) {
var req *movie.MovieUpdateReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 从URL路径获取ID
if req.Id == 0 {
idStr := r.Get("id").String()
if id, err := strconv.Atoi(idStr); err == nil {
req.Id = id
}
}
if req.Id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
movieService := movie.NewMovieService()
err := movieService.Update(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// GetById 获取影片详情
func (c *MovieController) GetById(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
movieService := movie.NewMovieService()
movieDetail, err := movieService.GetById(r.Context(), &movie.MovieDetailReq{Id: id})
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": movieDetail,
})
}
// GetList 获取影片列表
func (c *MovieController) GetList(r *ghttp.Request) {
var req *movie.MovieListReq
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
movieService := movie.NewMovieService()
result, err := movieService.GetList(r.Context(), req)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": result,
})
}
// Delete 删除影片
func (c *MovieController) Delete(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
movieService := movie.NewMovieService()
err = movieService.Delete(r.Context(), &movie.MovieDeleteReq{Id: id})
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "删除成功",
})
}
// GetHot 获取热门影片
func (c *MovieController) GetHot(r *ghttp.Request) {
limitStr := r.Get("limit", "10").String()
limit, err := strconv.Atoi(limitStr)
if err != nil || limit <= 0 {
limit = 10
}
movieService := movie.NewMovieService()
movies, err := movieService.GetHotMovies(r.Context(), limit)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": movies,
})
}
// GetRecommend 获取推荐影片
func (c *MovieController) GetRecommend(r *ghttp.Request) {
limitStr := r.Get("limit", "10").String()
limit, err := strconv.Atoi(limitStr)
if err != nil || limit <= 0 {
limit = 10
}
movieService := movie.NewMovieService()
movies, err := movieService.GetRecommendMovies(r.Context(), limit)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": movies,
})
}
// GetNew 获取最新影片
func (c *MovieController) GetNew(r *ghttp.Request) {
limitStr := r.Get("limit", "10").String()
limit, err := strconv.Atoi(limitStr)
if err != nil || limit <= 0 {
limit = 10
}
movieService := movie.NewMovieService()
movies, err := movieService.GetNewMovies(r.Context(), limit)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": movies,
})
}
// Search 搜索影片
func (c *MovieController) Search(r *ghttp.Request) {
keyword := r.Get("keyword").String()
if keyword == "" {
response.Error(r, 1001, "搜索关键词不能为空")
return
}
pageStr := r.Get("page", "1").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page <= 0 {
page = 1
}
pageSizeStr := r.Get("page_size", "10").String()
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize <= 0 {
pageSize = 10
}
movieService := movie.NewMovieService()
movies, total, err := movieService.SearchMovies(r.Context(), keyword, page, pageSize)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "搜索成功",
"data": g.Map{
"list": movies,
"total": total,
"page": page,
"page_size": pageSize,
"keyword": keyword,
},
})
}
// GetByCategory 根据分类获取影片
func (c *MovieController) GetByCategory(r *ghttp.Request) {
categoryIdStr := r.Get("category_id").String()
categoryId, err := strconv.Atoi(categoryIdStr)
if err != nil || categoryId <= 0 {
response.Error(r, 1001, "分类ID无效")
return
}
pageStr := r.Get("page", "1").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page <= 0 {
page = 1
}
pageSizeStr := r.Get("page_size", "10").String()
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize <= 0 {
pageSize = 10
}
movieService := movie.NewMovieService()
movies, total, err := movieService.GetMoviesByCategory(r.Context(), categoryId, page, pageSize)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": g.Map{
"list": movies,
"total": total,
"page": page,
"page_size": pageSize,
"category_id": categoryId,
},
})
}
// UpdateViewCount 更新观看次数
func (c *MovieController) UpdateViewCount(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
movieService := movie.NewMovieService()
err = movieService.UpdateViewCount(r.Context(), id)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// UpdateLikeCount 更新点赞数
func (c *MovieController) UpdateLikeCount(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
incrementStr := r.Get("increment", "1").String()
increment, err := strconv.Atoi(incrementStr)
if err != nil {
increment = 1
}
movieService := movie.NewMovieService()
err = movieService.UpdateLikeCount(r.Context(), id, increment)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// UpdateCollectCount 更新收藏数
func (c *MovieController) UpdateCollectCount(r *ghttp.Request) {
idStr := r.Get("id").String()
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
response.Error(r, 1001, "影片ID无效")
return
}
incrementStr := r.Get("increment", "1").String()
increment, err := strconv.Atoi(incrementStr)
if err != nil {
increment = 1
}
movieService := movie.NewMovieService()
err = movieService.UpdateCollectCount(r.Context(), id, increment)
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "更新成功",
})
}
// GetStatistics 获取影片统计
func (c *MovieController) GetStatistics(r *ghttp.Request) {
movieService := movie.NewMovieService()
stats, err := movieService.GetStatistics(r.Context())
if err != nil {
response.Error(r, 1002, err.Error())
return
}
response.Success(r, g.Map{
"message": "获取成功",
"data": stats,
})
}
// BatchUpdate 批量更新影片
func (c *MovieController) BatchUpdate(r *ghttp.Request) {
var req struct {
Ids []int `json:"ids" v:"required#影片ID列表不能为空"`
Status *int `json:"status"`
IsVip *int `json:"is_vip"`
}
if err := r.Parse(&req); err != nil {
response.Error(r, 1001, "参数解析失败: "+err.Error())
return
}
if len(req.Ids) == 0 {
response.Error(r, 1001, "影片ID列表不能为空")
return
}
// TODO: 实现批量更新逻辑
response.Success(r, g.Map{
"message": "批量更新成功",
})
}
// UploadVideo 上传视频
func (c *MovieController) UploadVideo(r *ghttp.Request) {
// TODO: 实现视频上传逻辑
response.Success(r, g.Map{
"message": "视频上传成功",
"data": g.Map{
"url": "/uploads/videos/example.mp4",
},
})
}
// UploadPoster 上传海报
func (c *MovieController) UploadPoster(r *ghttp.Request) {
// TODO: 实现海报上传逻辑
response.Success(r, g.Map{
"message": "海报上传成功",
"data": g.Map{
"url": "/uploads/posters/example.jpg",
},
})
}

View File

@@ -0,0 +1,64 @@
package user
import (
"github.com/gogf/gf/v2/net/ghttp"
"nl-video-api/internal/service"
)
// AttachmentController 用户附件管理控制器
type AttachmentController struct{}
// NewAttachmentController 创建用户附件管理控制器实例
func NewAttachmentController() *AttachmentController {
return &AttachmentController{}
}
// Upload 上传附件
func (c *AttachmentController) Upload(r *ghttp.Request) {
service.NewAttachmentService().UserUpload(r)
}
// GetList 获取用户附件列表
func (c *AttachmentController) GetList(r *ghttp.Request) {
service.NewAttachmentService().UserGetList(r)
}
// GetDetail 获取附件详情
func (c *AttachmentController) GetDetail(r *ghttp.Request) {
service.NewAttachmentService().UserGetDetail(r)
}
// Update 更新附件
func (c *AttachmentController) Update(r *ghttp.Request) {
service.NewAttachmentService().UserUpdate(r)
}
// Delete 删除附件
func (c *AttachmentController) Delete(r *ghttp.Request) {
service.NewAttachmentService().UserDelete(r)
}
// Download 下载附件
func (c *AttachmentController) Download(r *ghttp.Request) {
service.NewAttachmentService().UserDownload(r)
}
// Copy 复制附件
func (c *AttachmentController) Copy(r *ghttp.Request) {
service.NewAttachmentService().UserCopy(r)
}
// Rename 重命名附件
func (c *AttachmentController) Rename(r *ghttp.Request) {
service.NewAttachmentService().UserRename(r)
}
// Search 搜索附件
func (c *AttachmentController) Search(r *ghttp.Request) {
service.NewAttachmentService().UserSearch(r)
}
// GetCategoryList 获取附件分类列表
func (c *AttachmentController) GetCategoryList(r *ghttp.Request) {
service.NewAttachmentService().UserGetCategoryList(r)
}

View File

@@ -0,0 +1,21 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
var Banner = cBanner{}
type cBanner struct{}
// GetList 获取轮播图列表
func (c *cBanner) GetList(r *ghttp.Request) {
service.Banner.UserGetList(r)
}
// GetDetail 获取轮播图详情
func (c *cBanner) GetDetail(r *ghttp.Request) {
service.Banner.UserGetDetail(r)
}

View File

@@ -0,0 +1,42 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// CommentController 评论控制器
type CommentController struct{}
var Comment = &CommentController{}
// Add 添加评论
func (c *CommentController) Add(r *ghttp.Request) {
service.Comment.Add(r)
}
// List 评论列表
func (c *CommentController) List(r *ghttp.Request) {
service.Comment.GetList(r)
}
// Delete 删除评论
func (c *CommentController) Delete(r *ghttp.Request) {
service.Comment.Delete(r)
}
// Like 点赞评论
func (c *CommentController) Like(r *ghttp.Request) {
service.Comment.Like(r)
}
// Unlike 取消点赞
func (c *CommentController) Unlike(r *ghttp.Request) {
service.Comment.Unlike(r)
}
// Report 举报评论
func (c *CommentController) Report(r *ghttp.Request) {
service.Comment.Report(r)
}

View File

@@ -0,0 +1,22 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// ConfigController 系统配置控制器
type ConfigController struct{}
var Config = &ConfigController{}
// GetList 获取配置列表
func (c *ConfigController) GetList(r *ghttp.Request) {
service.Config.GetList(r)
}
// GetByKey 根据键获取配置
func (c *ConfigController) GetByKey(r *ghttp.Request) {
service.Config.GetByKey(r)
}

View File

@@ -0,0 +1,17 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// LogController 日志控制器
type LogController struct{}
var Log = &LogController{}
// GetList 获取日志列表
func (c *LogController) GetList(r *ghttp.Request) {
service.Log.GetList(r)
}

View File

@@ -0,0 +1,37 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// PaymentOrderController 支付订单控制器
type PaymentOrderController struct{}
var PaymentOrder = &PaymentOrderController{}
// Create 创建支付订单
func (c *PaymentOrderController) Create(r *ghttp.Request) {
service.PaymentOrder.Create(r)
}
// GetList 获取支付订单列表
func (c *PaymentOrderController) GetList(r *ghttp.Request) {
service.PaymentOrder.GetList(r)
}
// GetDetail 获取支付订单详情
func (c *PaymentOrderController) GetDetail(r *ghttp.Request) {
service.PaymentOrder.GetDetail(r)
}
// Pay 支付订单
func (c *PaymentOrderController) Pay(r *ghttp.Request) {
service.PaymentOrder.Pay(r)
}
// Cancel 取消订单
func (c *PaymentOrderController) Cancel(r *ghttp.Request) {
service.PaymentOrder.Cancel(r)
}

View File

@@ -0,0 +1,49 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// UserCollectController 用户收藏控制器
type UserCollectController struct{}
var UserCollect = &UserCollectController{}
// Add 添加收藏
func (c *UserCollectController) Add(r *ghttp.Request) {
service.UserCollect.Add(r)
}
// Remove 取消收藏
func (c *UserCollectController) Remove(r *ghttp.Request) {
service.UserCollect.Remove(r)
}
// GetList 获取收藏列表
func (c *UserCollectController) GetList(r *ghttp.Request) {
service.NewUserCollectService().GetList(r)
}
// List 获取收藏列表(别名方法)
func (c *UserCollectController) List(r *ghttp.Request) {
c.GetList(r)
}
// Check 检查收藏状态
func (c *UserCollectController) Check(r *ghttp.Request) {
// TODO: 实现检查收藏状态逻辑
r.Response.WriteJson(map[string]interface{}{
"code": 0,
"message": "检查成功",
"data": map[string]interface{}{
"is_collected": false,
},
})
}
// CheckCollect 检查是否已收藏
func (c *UserCollectController) CheckCollect(r *ghttp.Request) {
service.UserCollect.CheckCollect(r)
}

View File

@@ -0,0 +1,80 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// UserWatchHistoryController 用户观看历史控制器
type UserWatchHistoryController struct{}
var UserWatchHistory = &UserWatchHistoryController{}
// Add 添加观看历史
func (c *UserWatchHistoryController) Add(r *ghttp.Request) {
service.NewUserWatchHistoryService().Add(r)
}
// GetList 获取观看历史列表
func (c *UserWatchHistoryController) GetList(r *ghttp.Request) {
service.NewUserWatchHistoryService().GetList(r)
}
// Delete 删除观看历史
func (c *UserWatchHistoryController) Delete(r *ghttp.Request) {
service.NewUserWatchHistoryService().Delete(r)
}
// Clear 清空观看历史
func (c *UserWatchHistoryController) Clear(r *ghttp.Request) {
// 获取用户ID需要实现GetUserIdFromContext函数
userId := service.GetUserIdFromContext(r.Context())
if userId == 0 {
r.Response.WriteJson(map[string]interface{}{
"code": 401,
"message": "请先登录",
})
return
}
err := service.NewUserWatchHistoryService().Clear(r.Context(), &service.UserWatchHistoryClearReq{
UserId: uint(userId),
})
if err != nil {
r.Response.WriteJson(map[string]interface{}{
"code": 1002,
"message": err.Error(),
})
return
}
r.Response.WriteJson(map[string]interface{}{
"code": 0,
"message": "清空成功",
})
}
// GetProgress 获取观看进度
func (c *UserWatchHistoryController) GetProgress(r *ghttp.Request) {
service.NewUserWatchHistoryService().GetProgress(r)
}
// List 获取观看历史列表(别名方法)
func (c *UserWatchHistoryController) List(r *ghttp.Request) {
c.GetList(r)
}
// Get 获取单个观看历史记录
func (c *UserWatchHistoryController) Get(r *ghttp.Request) {
// TODO: 实现获取单个观看历史记录逻辑
r.Response.WriteJson(map[string]interface{}{
"code": 0,
"message": "获取成功",
"data": map[string]interface{}{
"id": 1,
"movie_id": 1,
"progress": 50,
"watch_time": 3600,
},
})
}

View File

@@ -0,0 +1,27 @@
package user
import (
"nl-video-api/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
// VipLevelController VIP等级控制器
type VipLevelController struct{}
var VipLevel = &VipLevelController{}
// GetList 获取VIP等级列表
func (c *VipLevelController) GetList(r *ghttp.Request) {
service.VipLevel.GetList(r)
}
// GetAll 获取所有VIP等级
func (c *VipLevelController) GetAll(r *ghttp.Request) {
service.VipLevel.GetAll(r)
}
// GetDetail 获取VIP等级详情
func (c *VipLevelController) GetDetail(r *ghttp.Request) {
service.VipLevel.GetDetail(r)
}

0
internal/dao/.gitkeep Normal file
View File

82
internal/dao/admin_log.go Normal file
View File

@@ -0,0 +1,82 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalAdminLogDao is internal type for wrapping internal DAO implements.
type internalAdminLogDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns AdminLogColumns // columns contains all the column names of Table for convenient usage.
}
// AdminLogColumns defines and stores column names for table nl_admin_log.
type AdminLogColumns struct {
Id string // 日志ID
AdminId string // 管理员ID
Action string // 操作动作
Module string // 操作模块
Content string // 操作内容
Ip string // IP地址
UserAgent string // 用户代理
CreatedAt string // 创建时间
}
// adminLogColumns holds the columns for table nl_admin_log.
var adminLogColumns = AdminLogColumns{
Id: "id",
AdminId: "admin_id",
Action: "action",
Module: "module",
Content: "content",
Ip: "ip",
UserAgent: "user_agent",
CreatedAt: "created_at",
}
// NewAdminLogDao creates and returns a new DAO object for table data access.
func NewAdminLogDao() *internalAdminLogDao {
return &internalAdminLogDao{
group: "default",
table: "nl_admin_log",
columns: adminLogColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalAdminLogDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalAdminLogDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalAdminLogDao) Columns() AdminLogColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalAdminLogDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalAdminLogDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalAdminLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,88 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalAttachmentDao is internal type for wrapping internal DAO implements.
type internalAttachmentDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns AttachmentColumns // columns contains all the column names of Table for convenient usage.
}
// AttachmentColumns defines and stores column names for table nl_attachment.
type AttachmentColumns struct {
Id string // 附件ID
Name string // 文件名
Path string // 文件路径
Url string // 访问URL
Size string // 文件大小(字节)
MimeType string // 文件类型
Extension string // 文件扩展名
UserId string // 上传用户ID
Status string // 状态0禁用1启用
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// attachmentColumns holds the columns for table nl_attachment.
var attachmentColumns = AttachmentColumns{
Id: "id",
Name: "name",
Path: "path",
Url: "url",
Size: "size",
MimeType: "mime_type",
Extension: "extension",
UserId: "user_id",
Status: "status",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewAttachmentDao creates and returns a new DAO object for table data access.
func NewAttachmentDao() *internalAttachmentDao {
return &internalAttachmentDao{
group: "default",
table: "nl_attachment",
columns: attachmentColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalAttachmentDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalAttachmentDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalAttachmentDao) Columns() AttachmentColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalAttachmentDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalAttachmentDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalAttachmentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

82
internal/dao/banner.go Normal file
View File

@@ -0,0 +1,82 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalBannerDao is internal type for wrapping internal DAO implements.
type internalBannerDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns BannerColumns // columns contains all the column names of Table for convenient usage.
}
// BannerColumns defines and stores column names for table nl_banner.
type BannerColumns struct {
Id string // 轮播图ID
Title string // 轮播图标题
ImageUrl string // 图片URL
LinkUrl string // 跳转链接
Sort string // 排序
Status string // 状态0禁用1启用
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// bannerColumns holds the columns for table nl_banner.
var bannerColumns = BannerColumns{
Id: "id",
Title: "title",
ImageUrl: "image_url",
LinkUrl: "link_url",
Sort: "sort",
Status: "status",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewBannerDao creates and returns a new DAO object for table data access.
func NewBannerDao() *internalBannerDao {
return &internalBannerDao{
group: "default",
table: "nl_banner",
columns: bannerColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalBannerDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalBannerDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalBannerDao) Columns() BannerColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalBannerDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalBannerDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalBannerDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

84
internal/dao/comment.go Normal file
View File

@@ -0,0 +1,84 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalCommentDao is internal type for wrapping internal DAO implements.
type internalCommentDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns CommentColumns // columns contains all the column names of Table for convenient usage.
}
// CommentColumns defines and stores column names for table nl_comment.
type CommentColumns struct {
Id string // 评论ID
UserId string // 用户ID
MovieId string // 影片ID
ParentId string // 父评论ID0为顶级评论
Content string // 评论内容
LikeCount string // 点赞数
Status string // 状态0待审核1已通过2已拒绝
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// commentColumns holds the columns for table nl_comment.
var commentColumns = CommentColumns{
Id: "id",
UserId: "user_id",
MovieId: "movie_id",
ParentId: "parent_id",
Content: "content",
LikeCount: "like_count",
Status: "status",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewCommentDao creates and returns a new DAO object for table data access.
func NewCommentDao() *internalCommentDao {
return &internalCommentDao{
group: "default",
table: "nl_comment",
columns: commentColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalCommentDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalCommentDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalCommentDao) Columns() CommentColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalCommentDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalCommentDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalCommentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"nl-video-api/internal/dao/internal"
)
// internalCommentLikeDao is internal type for wrapping internal DAO implements.
type internalCommentLikeDao = *internal.CommentLikeDao
// commentLikeDao is the data access object for table comment_like.
// You can define custom methods on it to extend its functionality as you wish.
type commentLikeDao struct {
internalCommentLikeDao
}
// Fill with you ideas below.

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"nl-video-api/internal/dao/internal"
)
// internalCommentReportDao is internal type for wrapping internal DAO implements.
type internalCommentReportDao = *internal.CommentReportDao
// commentReportDao is the data access object for table comment_report.
// You can define custom methods on it to extend its functionality as you wish.
type commentReportDao struct {
internalCommentReportDao
}
// Fill with you ideas below.

82
internal/dao/config.go Normal file
View File

@@ -0,0 +1,82 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalConfigDao is internal type for wrapping internal DAO implements.
type internalConfigDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns ConfigColumns // columns contains all the column names of Table for convenient usage.
}
// ConfigColumns defines and stores column names for table nl_config.
type ConfigColumns struct {
Id string // 配置ID
ConfigKey string // 配置键
ConfigValue string // 配置值
ConfigType string // 配置类型
Description string // 配置描述
Status string // 状态0禁用1启用
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// configColumns holds the columns for table nl_config.
var configColumns = ConfigColumns{
Id: "id",
ConfigKey: "config_key",
ConfigValue: "config_value",
ConfigType: "config_type",
Description: "description",
Status: "status",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewConfigDao creates and returns a new DAO object for table data access.
func NewConfigDao() *internalConfigDao {
return &internalConfigDao{
group: "default",
table: "nl_config",
columns: configColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalConfigDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalConfigDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalConfigDao) Columns() ConfigColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalConfigDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalConfigDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

109
internal/dao/episode.go Normal file
View File

@@ -0,0 +1,109 @@
package dao
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// EpisodeDao 集数数据访问对象
type EpisodeDao struct{}
// episodeDao 集数DAO实例
var episodeDao = &EpisodeDao{}
// NewEpisodeDao 创建集数DAO实例
func NewEpisodeDao() *EpisodeDao {
return episodeDao
}
// GetByMovieId 根据影片ID获取集数列表
func (d *EpisodeDao) GetByMovieId(ctx context.Context, movieId int) ([]*entity.Episode, error) {
var episodes []*entity.Episode
err := g.DB().Model("nl_episode").
Where("movie_id = ? AND deleted_at = 0", movieId).
Order("episode_num ASC, sort ASC").
Scan(&episodes)
return episodes, err
}
// GetById 根据ID获取集数详情
func (d *EpisodeDao) GetById(ctx context.Context, id int) (*entity.Episode, error) {
var episode *entity.Episode
err := g.DB().Model("nl_episode").
Where("id = ? AND deleted_at = 0", id).
Scan(&episode)
return episode, err
}
// Create 创建集数
func (d *EpisodeDao) Create(ctx context.Context, episode *entity.Episode) (int64, error) {
result, err := g.DB().Model("nl_episode").Data(episode).Insert()
if err != nil {
return 0, err
}
return result.LastInsertId()
}
// Update 更新集数
func (d *EpisodeDao) Update(ctx context.Context, id int, data g.Map) error {
data["updated_at"] = gtime.Now().Unix()
_, err := g.DB().Model("nl_episode").Where("id = ?", id).Data(data).Update()
return err
}
// Delete 删除集数(软删除)
func (d *EpisodeDao) Delete(ctx context.Context, id int) error {
_, err := g.DB().Model("nl_episode").Where("id = ?", id).Data(g.Map{
"deleted_at": gtime.Now().Unix(),
"updated_at": gtime.Now().Unix(),
}).Update()
return err
}
// UpdateViewCount 更新观看次数
func (d *EpisodeDao) UpdateViewCount(ctx context.Context, id int) error {
_, err := g.DB().Model("nl_episode").Where("id = ?", id).Increment("view_count", 1)
return err
}
// GetMaxEpisodeNum 获取影片的最大集数
func (d *EpisodeDao) GetMaxEpisodeNum(ctx context.Context, movieId int) (int, error) {
var maxNum int
err := g.DB().Model("nl_episode").
Where("movie_id = ? AND deleted_at = 0", movieId).
Fields("MAX(episode_num) as max_num").
Scan(&maxNum)
return maxNum, err
}
// BatchCreate 批量创建集数
func (d *EpisodeDao) BatchCreate(ctx context.Context, episodes []*entity.Episode) error {
if len(episodes) == 0 {
return nil
}
_, err := g.DB().Model("nl_episode").Data(episodes).Insert()
return err
}
// GetEpisodesByRange 获取指定范围的集数
func (d *EpisodeDao) GetEpisodesByRange(ctx context.Context, movieId, startNum, endNum int) ([]*entity.Episode, error) {
var episodes []*entity.Episode
err := g.DB().Model("nl_episode").
Where("movie_id = ? AND episode_num >= ? AND episode_num <= ? AND deleted_at = 0",
movieId, startNum, endNum).
Order("episode_num ASC").
Scan(&episodes)
return episodes, err
}
// CheckEpisodeExists 检查集数是否存在
func (d *EpisodeDao) CheckEpisodeExists(ctx context.Context, movieId, episodeNum int) (bool, error) {
count, err := g.DB().Model("nl_episode").
Where("movie_id = ? AND episode_num = ? AND deleted_at = 0", movieId, episodeNum).
Count()
return count > 0, err
}

75
internal/dao/internal.go Normal file
View File

@@ -0,0 +1,75 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"nl-video-api/internal/dao/internal"
)
// internalDao is internal type for wrapping internal DAO implements.
type internalDao = *internal.Dao
// Internal returns the internal DAO implements.
func Internal() internalDao {
return internal.New()
}
// New returns the DAO implements.
func New() *internal.Dao {
return internal.New()
}
var (
// User is globally public accessible object for table nl_user operations.
User = NewUserDao()
// Movie is globally public accessible object for table nl_movie operations.
Movie = NewMovieDao()
// Episode is globally public accessible object for table nl_episode operations.
Episode = NewEpisodeDao()
// Role is globally public accessible object for table nl_role operations.
Role = &RoleDao{}
// Permission is globally public accessible object for table nl_permission operations.
Permission = &PermissionDao{}
// UserCollect is globally public accessible object for table nl_user_collect operations.
UserCollect = NewUserCollectDao()
// UserWatchHistory is globally public accessible object for table nl_user_watch_history operations.
UserWatchHistory = NewUserWatchHistoryDao()
// Comment is globally public accessible object for table nl_comment operations.
Comment = NewCommentDao()
// Banner is globally public accessible object for table nl_banner operations.
Banner = NewBannerDao()
// PaymentOrder is globally public accessible object for table nl_payment_order operations.
PaymentOrder = NewPaymentOrderDao()
// VipLevel is globally public accessible object for table nl_vip_level operations.
VipLevel = NewVipLevelDao()
// Attachment is globally public accessible object for table nl_attachment operations.
Attachment = NewAttachmentDao()
// Config is globally public accessible object for table nl_config operations.
Config = NewConfigDao()
// AdminLog is globally public accessible object for table nl_admin_log operations.
AdminLog = NewAdminLogDao()
// UserLog is globally public accessible object for table nl_user_log operations.
UserLog = NewUserLogDao()
// CommentLike is globally public accessible object for table comment_like operations.
CommentLike = internal.NewCommentLikeDao()
// CommentReport is globally public accessible object for table comment_report operations.
CommentReport = internal.NewCommentReportDao()
)

View File

@@ -0,0 +1,69 @@
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// CommentLikeDao is the data access object for table comment_like.
type CommentLikeDao struct {
table string
group string
columns CommentLikeColumns
}
// CommentLikeColumns defines and stores column names for table comment_like.
type CommentLikeColumns struct {
Id string
CommentId string
UserId string
CreatedAt string
}
// commentLikeColumns holds the columns for table comment_like.
var commentLikeColumns = CommentLikeColumns{
Id: "id",
CommentId: "comment_id",
UserId: "user_id",
CreatedAt: "created_at",
}
// NewCommentLikeDao creates and returns a new DAO object for table data access.
func NewCommentLikeDao() *CommentLikeDao {
return &CommentLikeDao{
group: "default",
table: "comment_like",
columns: commentLikeColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *CommentLikeDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *CommentLikeDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *CommentLikeDao) Columns() CommentLikeColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *CommentLikeDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *CommentLikeDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *CommentLikeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,73 @@
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// CommentReportDao is the data access object for table comment_report.
type CommentReportDao struct {
table string
group string
columns CommentReportColumns
}
// CommentReportColumns defines and stores column names for table comment_report.
type CommentReportColumns struct {
Id string
CommentId string
UserId string
Reason string
Status string
CreatedAt string
}
// commentReportColumns holds the columns for table comment_report.
var commentReportColumns = CommentReportColumns{
Id: "id",
CommentId: "comment_id",
UserId: "user_id",
Reason: "reason",
Status: "status",
CreatedAt: "created_at",
}
// NewCommentReportDao creates and returns a new DAO object for table data access.
func NewCommentReportDao() *CommentReportDao {
return &CommentReportDao{
group: "default",
table: "comment_report",
columns: commentReportColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *CommentReportDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *CommentReportDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *CommentReportDao) Columns() CommentReportColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *CommentReportDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *CommentReportDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *CommentReportDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,51 @@
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// Dao is the data access object for database operations.
type Dao struct {
table string
group string
columns []string
}
// New creates and returns a new DAO object.
func New() *Dao {
return &Dao{
group: "default",
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *Dao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *Dao) Columns() []string {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

259
internal/dao/movie.go Normal file
View File

@@ -0,0 +1,259 @@
package dao
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// MovieDao 影片数据访问对象
type MovieDao struct{}
// movieDao 影片DAO实例
var movieDao = &MovieDao{}
// NewMovieDao 创建影片DAO实例
func NewMovieDao() *MovieDao {
return movieDao
}
// GetList 获取影片列表
func (d *MovieDao) GetList(ctx context.Context, req *MovieListReq) ([]*entity.Movie, int, error) {
db := g.DB()
model := db.Model("nl_movie").Where("deleted_at = 0")
// 条件筛选
if req.CategoryId > 0 {
model = model.Where("category_id = ?", req.CategoryId)
}
if req.Type > 0 {
model = model.Where("type = ?", req.Type)
}
if req.Year > 0 {
model = model.Where("year = ?", req.Year)
}
if req.Area != "" {
model = model.Where("area = ?", req.Area)
}
if req.Language != "" {
model = model.Where("language = ?", req.Language)
}
if req.IsVip >= 0 {
model = model.Where("is_vip = ?", req.IsVip)
}
if req.IsRecommend >= 0 {
model = model.Where("is_recommend = ?", req.IsRecommend)
}
if req.IsHot >= 0 {
model = model.Where("is_hot = ?", req.IsHot)
}
if req.IsNew >= 0 {
model = model.Where("is_new = ?", req.IsNew)
}
if req.Status >= 0 {
model = model.Where("status = ?", req.Status)
}
if req.Keyword != "" {
model = model.Where("title LIKE ? OR original_title LIKE ? OR director LIKE ? OR actor LIKE ?",
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
}
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 排序
orderBy := "created_at DESC"
if req.OrderBy != "" {
orderBy = req.OrderBy
}
model = model.Order(orderBy)
// 分页
if req.Page > 0 && req.PageSize > 0 {
offset := (req.Page - 1) * req.PageSize
model = model.Limit(req.PageSize).Offset(offset)
}
var movies []*entity.Movie
err = model.Scan(&movies)
if err != nil {
return nil, 0, err
}
return movies, total, nil
}
// GetById 根据ID获取影片
func (d *MovieDao) GetById(ctx context.Context, id int) (*entity.Movie, error) {
var movie *entity.Movie
err := g.DB().Model("nl_movie").Where("id = ? AND deleted_at = 0", id).Scan(&movie)
if err != nil {
return nil, err
}
return movie, nil
}
// Create 创建影片
func (d *MovieDao) Create(ctx context.Context, movie *entity.Movie) (int64, error) {
result, err := g.DB().Model("nl_movie").Data(movie).Insert()
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return id, nil
}
// Update 更新影片
func (d *MovieDao) Update(ctx context.Context, id int, data g.Map) error {
data["updated_at"] = gtime.Now().Unix()
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Data(data).Update()
return err
}
// Delete 删除影片(软删除)
func (d *MovieDao) Delete(ctx context.Context, id int) error {
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Data(g.Map{
"deleted_at": gtime.Now().Unix(),
"updated_at": gtime.Now().Unix(),
}).Update()
return err
}
// UpdateViewCount 更新观看次数
func (d *MovieDao) UpdateViewCount(ctx context.Context, id int) error {
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Increment("view_count", 1)
return err
}
// UpdateLikeCount 更新点赞数
func (d *MovieDao) UpdateLikeCount(ctx context.Context, id int, increment int) error {
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Increment("like_count", increment)
return err
}
// UpdateCollectCount 更新收藏数
func (d *MovieDao) UpdateCollectCount(ctx context.Context, id int, increment int) error {
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Increment("collect_count", increment)
return err
}
// UpdateCommentCount 更新评论数
func (d *MovieDao) UpdateCommentCount(ctx context.Context, id int, increment int) error {
_, err := g.DB().Model("nl_movie").Where("id = ?", id).Increment("comment_count", increment)
return err
}
// GetHotMovies 获取热门影片
func (d *MovieDao) GetHotMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
var movies []*entity.Movie
err := g.DB().Model("nl_movie").
Where("status = 1 AND deleted_at = 0").
Order("view_count DESC, rating DESC").
Limit(limit).
Scan(&movies)
return movies, err
}
// GetRecommendMovies 获取推荐影片
func (d *MovieDao) GetRecommendMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
var movies []*entity.Movie
err := g.DB().Model("nl_movie").
Where("is_recommend = 1 AND status = 1 AND deleted_at = 0").
Order("sort ASC, created_at DESC").
Limit(limit).
Scan(&movies)
return movies, err
}
// GetNewMovies 获取最新影片
func (d *MovieDao) GetNewMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
var movies []*entity.Movie
err := g.DB().Model("nl_movie").
Where("status = 1 AND deleted_at = 0").
Order("created_at DESC").
Limit(limit).
Scan(&movies)
return movies, err
}
// SearchMovies 搜索影片
func (d *MovieDao) SearchMovies(ctx context.Context, keyword string, page, pageSize int) ([]*entity.Movie, int, error) {
db := g.DB()
model := db.Model("nl_movie").
Where("deleted_at = 0 AND status = 1").
Where("title LIKE ? OR original_title LIKE ? OR director LIKE ? OR actor LIKE ? OR description LIKE ?",
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页
if page > 0 && pageSize > 0 {
offset := (page - 1) * pageSize
model = model.Limit(pageSize).Offset(offset)
}
var movies []*entity.Movie
err = model.Order("rating DESC, view_count DESC").Scan(&movies)
if err != nil {
return nil, 0, err
}
return movies, total, nil
}
// GetMoviesByCategory 根据分类获取影片
func (d *MovieDao) GetMoviesByCategory(ctx context.Context, categoryId, page, pageSize int) ([]*entity.Movie, int, error) {
db := g.DB()
model := db.Model("nl_movie").
Where("category_id = ? AND status = 1 AND deleted_at = 0", categoryId)
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页
if page > 0 && pageSize > 0 {
offset := (page - 1) * pageSize
model = model.Limit(pageSize).Offset(offset)
}
var movies []*entity.Movie
err = model.Order("sort ASC, created_at DESC").Scan(&movies)
if err != nil {
return nil, 0, err
}
return movies, total, nil
}
// MovieListReq 影片列表请求参数
type MovieListReq struct {
Page int `json:"page"` // 页码
PageSize int `json:"page_size"` // 每页数量
CategoryId int `json:"category_id"` // 分类ID
Type int `json:"type"` // 类型
Year int `json:"year"` // 年份
Area string `json:"area"` // 地区
Language string `json:"language"` // 语言
IsVip int `json:"is_vip"` // 是否VIP专享
IsRecommend int `json:"is_recommend"` // 是否推荐
IsHot int `json:"is_hot"` // 是否热门
IsNew int `json:"is_new"` // 是否最新
Status int `json:"status"` // 状态
Keyword string `json:"keyword"` // 关键词
OrderBy string `json:"order_by"` // 排序
}

View File

@@ -0,0 +1,90 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalPaymentOrderDao is internal type for wrapping internal DAO implements.
type internalPaymentOrderDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns PaymentOrderColumns // columns contains all the column names of Table for convenient usage.
}
// PaymentOrderColumns defines and stores column names for table nl_payment_order.
type PaymentOrderColumns struct {
Id string // 订单ID
OrderNo string // 订单号
UserId string // 用户ID
VipLevelId string // VIP等级ID
Amount string // 订单金额
PaymentMethod string // 支付方式
PaymentStatus string // 支付状态0待支付1已支付2已取消3已退款
PaymentTime string // 支付时间
ExpireTime string // 过期时间
Remark string // 备注
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// paymentOrderColumns holds the columns for table nl_payment_order.
var paymentOrderColumns = PaymentOrderColumns{
Id: "id",
OrderNo: "order_no",
UserId: "user_id",
VipLevelId: "vip_level_id",
Amount: "amount",
PaymentMethod: "payment_method",
PaymentStatus: "payment_status",
PaymentTime: "payment_time",
ExpireTime: "expire_time",
Remark: "remark",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewPaymentOrderDao creates and returns a new DAO object for table data access.
func NewPaymentOrderDao() *internalPaymentOrderDao {
return &internalPaymentOrderDao{
group: "default",
table: "nl_payment_order",
columns: paymentOrderColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalPaymentOrderDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalPaymentOrderDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalPaymentOrderDao) Columns() PaymentOrderColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalPaymentOrderDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalPaymentOrderDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalPaymentOrderDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

248
internal/dao/permission.go Normal file
View File

@@ -0,0 +1,248 @@
package dao
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// PermissionDao 权限数据访问对象
type PermissionDao struct{}
// TableName 获取表名
func (dao *PermissionDao) TableName() string {
return "nl_permission"
}
// Create 创建权限
func (dao *PermissionDao) Create(ctx context.Context, data *entity.Permission) (int, error) {
result, err := g.DB().Model(dao.TableName()).Ctx(ctx).Data(data).Insert()
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}
// GetById 根据ID获取权限
func (dao *PermissionDao) GetById(ctx context.Context, id int) (*entity.Permission, error) {
var permission *entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ? AND deleted_at = 0", id).Scan(&permission)
if err != nil {
return nil, err
}
return permission, nil
}
// GetByCode 根据权限编码获取权限
func (dao *PermissionDao) GetByCode(ctx context.Context, code string) (*entity.Permission, error) {
var permission *entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("code = ? AND deleted_at = 0", code).Scan(&permission)
if err != nil {
return nil, err
}
return permission, nil
}
// Update 更新权限
func (dao *PermissionDao) Update(ctx context.Context, id int, data g.Map) error {
data["updated_at"] = gtime.Now().Unix()
_, err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ?", id).Data(data).Update()
return err
}
// Delete 删除权限(软删除)
func (dao *PermissionDao) Delete(ctx context.Context, id int) error {
data := g.Map{
"deleted_at": gtime.Now().Unix(),
"updated_at": gtime.Now().Unix(),
}
_, err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ?", id).Data(data).Update()
return err
}
// GetList 获取权限列表
func (dao *PermissionDao) GetList(ctx context.Context, req *PermissionListReq) ([]*entity.Permission, int, error) {
model := g.DB().Model(dao.TableName()).Ctx(ctx).Where("deleted_at = 0")
// 条件筛选
if req.Name != "" {
model = model.WhereLike("name", "%"+req.Name+"%")
}
if req.Code != "" {
model = model.WhereLike("code", "%"+req.Code+"%")
}
if req.Type != "" {
model = model.Where("type = ?", req.Type)
}
if req.Status >= 0 {
model = model.Where("status = ?", req.Status)
}
if req.ParentId >= 0 {
model = model.Where("parent_id = ?", req.ParentId)
}
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页和排序
if req.Page > 0 && req.PageSize > 0 {
offset := (req.Page - 1) * req.PageSize
model = model.Limit(req.PageSize).Offset(offset)
}
model = model.OrderAsc("sort").OrderAsc("id")
var permissions []*entity.Permission
err = model.Scan(&permissions)
if err != nil {
return nil, 0, err
}
return permissions, total, nil
}
// GetTree 获取权限树形结构
func (dao *PermissionDao) GetTree(ctx context.Context) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("deleted_at = 0 AND status = 1").
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
if err != nil {
return nil, err
}
// 构建树形结构
return dao.buildTree(permissions, 0), nil
}
// buildTree 构建树形结构
func (dao *PermissionDao) buildTree(permissions []*entity.Permission, parentId int) []*entity.Permission {
var tree []*entity.Permission
for _, permission := range permissions {
if permission.ParentId == parentId {
children := dao.buildTree(permissions, permission.Id)
if len(children) > 0 {
// 这里需要在Permission实体中添加Children字段
// permission.Children = children
}
tree = append(tree, permission)
}
}
return tree
}
// GetAll 获取所有权限
func (dao *PermissionDao) GetAll(ctx context.Context) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("deleted_at = 0 AND status = 1").
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// GetByParentId 根据父级ID获取权限
func (dao *PermissionDao) GetByParentId(ctx context.Context, parentId int) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("parent_id = ? AND deleted_at = 0 AND status = 1", parentId).
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// GetByType 根据类型获取权限
func (dao *PermissionDao) GetByType(ctx context.Context, permissionType string) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("type = ? AND deleted_at = 0 AND status = 1", permissionType).
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// CheckCodeExists 检查权限编码是否存在
func (dao *PermissionDao) CheckCodeExists(ctx context.Context, code string, excludeId int) (bool, error) {
model := g.DB().Model(dao.TableName()).Ctx(ctx).Where("code = ? AND deleted_at = 0", code)
if excludeId > 0 {
model = model.Where("id != ?", excludeId)
}
count, err := model.Count()
if err != nil {
return false, err
}
return count > 0, nil
}
// GetPermissionsByIds 根据ID列表获取权限
func (dao *PermissionDao) GetPermissionsByIds(ctx context.Context, ids []int) ([]*entity.Permission, error) {
if len(ids) == 0 {
return []*entity.Permission{}, nil
}
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("id IN (?) AND deleted_at = 0", ids).
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// GetMenuPermissions 获取菜单权限
func (dao *PermissionDao) GetMenuPermissions(ctx context.Context) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("type = 'menu' AND deleted_at = 0 AND status = 1").
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// GetApiPermissions 获取API权限
func (dao *PermissionDao) GetApiPermissions(ctx context.Context) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("type = 'api' AND deleted_at = 0 AND status = 1").
OrderAsc("sort").OrderAsc("id").
Scan(&permissions)
return permissions, err
}
// GetUserPermissions 获取用户权限(通过角色)
func (dao *PermissionDao) GetUserPermissions(ctx context.Context, userId int) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model("nl_permission p").Ctx(ctx).
LeftJoin("nl_role_permission rp", "p.id = rp.permission_id").
LeftJoin("nl_admin a", "a.role_id = rp.role_id").
Where("a.id = ? AND p.deleted_at = 0 AND p.status = 1", userId).
OrderAsc("p.sort").OrderAsc("p.id").
Scan(&permissions)
return permissions, err
}
// PermissionListReq 权限列表请求参数
type PermissionListReq struct {
Page int `json:"page" v:"min:1#页码最小为1"`
PageSize int `json:"page_size" v:"min:1,max:100#每页数量范围1-100"`
Name string `json:"name"` // 权限名称
Code string `json:"code"` // 权限编码
Type string `json:"type"` // 权限类型menu-菜单button-按钮api-接口
Status int `json:"status"` // 状态:-1-全部0-禁用1-启用
ParentId int `json:"parent_id"` // 父级ID-1-全部0-顶级,>0-指定父级
}

200
internal/dao/role.go Normal file
View File

@@ -0,0 +1,200 @@
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// RoleDao 角色数据访问对象
type RoleDao struct{}
// TableName 获取表名
func (dao *RoleDao) TableName() string {
return "nl_role"
}
// Create 创建角色
func (dao *RoleDao) Create(ctx context.Context, data *entity.Role) (int, error) {
result, err := g.DB().Model(dao.TableName()).Ctx(ctx).Data(data).Insert()
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}
// GetById 根据ID获取角色
func (dao *RoleDao) GetById(ctx context.Context, id int) (*entity.Role, error) {
var role *entity.Role
err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ? AND deleted_at = 0", id).Scan(&role)
if err != nil {
return nil, err
}
return role, nil
}
// GetByCode 根据角色编码获取角色
func (dao *RoleDao) GetByCode(ctx context.Context, code string) (*entity.Role, error) {
var role *entity.Role
err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("code = ? AND deleted_at = 0", code).Scan(&role)
if err != nil {
return nil, err
}
return role, nil
}
// Update 更新角色
func (dao *RoleDao) Update(ctx context.Context, id int, data g.Map) error {
data["updated_at"] = gtime.Now().Unix()
_, err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ?", id).Data(data).Update()
return err
}
// Delete 删除角色(软删除)
func (dao *RoleDao) Delete(ctx context.Context, id int) error {
data := g.Map{
"deleted_at": gtime.Now().Unix(),
"updated_at": gtime.Now().Unix(),
}
_, err := g.DB().Model(dao.TableName()).Ctx(ctx).Where("id = ?", id).Data(data).Update()
return err
}
// GetList 获取角色列表
func (dao *RoleDao) GetList(ctx context.Context, req *RoleListReq) ([]*entity.Role, int, error) {
model := g.DB().Model(dao.TableName()).Ctx(ctx).Where("deleted_at = 0")
// 条件筛选
if req.Name != "" {
model = model.WhereLike("name", "%"+req.Name+"%")
}
if req.Code != "" {
model = model.WhereLike("code", "%"+req.Code+"%")
}
if req.Status >= 0 {
model = model.Where("status = ?", req.Status)
}
if req.Level > 0 {
model = model.Where("level = ?", req.Level)
}
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页和排序
if req.Page > 0 && req.PageSize > 0 {
offset := (req.Page - 1) * req.PageSize
model = model.Limit(req.PageSize).Offset(offset)
}
model = model.OrderDesc("sort").OrderDesc("id")
var roles []*entity.Role
err = model.Scan(&roles)
if err != nil {
return nil, 0, err
}
return roles, total, nil
}
// GetAll 获取所有角色
func (dao *RoleDao) GetAll(ctx context.Context) ([]*entity.Role, error) {
var roles []*entity.Role
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("deleted_at = 0 AND status = 1").
OrderDesc("sort").OrderDesc("id").
Scan(&roles)
return roles, err
}
// CheckCodeExists 检查角色编码是否存在
func (dao *RoleDao) CheckCodeExists(ctx context.Context, code string, excludeId int) (bool, error) {
model := g.DB().Model(dao.TableName()).Ctx(ctx).Where("code = ? AND deleted_at = 0", code)
if excludeId > 0 {
model = model.Where("id != ?", excludeId)
}
count, err := model.Count()
if err != nil {
return false, err
}
return count > 0, nil
}
// GetRolesByIds 根据ID列表获取角色
func (dao *RoleDao) GetRolesByIds(ctx context.Context, ids []int) ([]*entity.Role, error) {
if len(ids) == 0 {
return []*entity.Role{}, nil
}
var roles []*entity.Role
err := g.DB().Model(dao.TableName()).Ctx(ctx).
Where("id IN (?) AND deleted_at = 0", ids).
OrderDesc("sort").OrderDesc("id").
Scan(&roles)
return roles, err
}
// GetRolePermissions 获取角色的权限列表
func (dao *RoleDao) GetRolePermissions(ctx context.Context, roleId int) ([]*entity.Permission, error) {
var permissions []*entity.Permission
err := g.DB().Model("nl_permission p").Ctx(ctx).
LeftJoin("nl_role_permission rp", "p.id = rp.permission_id").
Where("rp.role_id = ? AND p.deleted_at = 0 AND p.status = 1", roleId).
OrderAsc("p.sort").OrderAsc("p.id").
Scan(&permissions)
return permissions, err
}
// AssignPermissions 为角色分配权限
func (dao *RoleDao) AssignPermissions(ctx context.Context, roleId int, permissionIds []int) error {
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 先删除原有权限
_, err := tx.Model("nl_role_permission").Ctx(ctx).Where("role_id = ?", roleId).Delete()
if err != nil {
return err
}
// 添加新权限
if len(permissionIds) > 0 {
data := make([]g.Map, 0, len(permissionIds))
for _, permissionId := range permissionIds {
data = append(data, g.Map{
"role_id": roleId,
"permission_id": permissionId,
"created_at": gtime.Now().Unix(),
})
}
_, err = tx.Model("nl_role_permission").Ctx(ctx).Data(data).Insert()
if err != nil {
return err
}
}
return nil
})
}
// RoleListReq 角色列表请求参数
type RoleListReq struct {
Page int `json:"page" v:"min:1#页码最小为1"`
PageSize int `json:"page_size" v:"min:1,max:100#每页数量范围1-100"`
Name string `json:"name"` // 角色名称
Code string `json:"code"` // 角色编码
Status int `json:"status"` // 状态:-1-全部0-禁用1-启用
Level int `json:"level"` // 角色等级
}

356
internal/dao/user.go Normal file
View File

@@ -0,0 +1,356 @@
package dao
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// UserDao 用户数据访问对象
type UserDao struct {
table string
group string
columns UserColumns
}
// UserColumns 用户表字段
type UserColumns struct {
Id string
Username string
Phone string
Email string
Password string
Nickname string
Avatar string
Gender string
Birthday string
VipLevel string
VipExpireAt string
Balance string
Points string
Status string
LastLoginAt string
LastLoginIp string
CreatedAt string
UpdatedAt string
DeletedAt string
}
// UserListReq 用户列表请求
type UserListReq struct {
Page int `json:"page" d:"1"`
PageSize int `json:"page_size" d:"20"`
Username string `json:"username"`
Phone string `json:"phone"`
Email string `json:"email"`
Status int `json:"status"`
VipLevel int `json:"vip_level"`
Gender int `json:"gender"`
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
}
// userDao 用户DAO实例
var userDao = UserDao{
table: "nl_user",
group: "default",
columns: UserColumns{
Id: "id",
Username: "username",
Phone: "phone",
Email: "email",
Password: "password",
Nickname: "nickname",
Avatar: "avatar",
Gender: "gender",
Birthday: "birthday",
VipLevel: "vip_level",
VipExpireAt: "vip_expire_at",
Balance: "balance",
Points: "points",
Status: "status",
LastLoginAt: "last_login_at",
LastLoginIp: "last_login_ip",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",
},
}
// NewUserDao 创建用户DAO实例
func NewUserDao() *UserDao {
return &userDao
}
// Create 创建用户
func (dao *UserDao) Create(ctx context.Context, data *entity.NlUser) (int64, error) {
data.CreatedAt = int(gtime.Now().Unix())
data.UpdatedAt = int(gtime.Now().Unix())
result, err := g.DB(dao.group).Model(dao.table).Data(data).Insert()
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
return id, err
}
// GetById 根据ID获取用户
func (dao *UserDao) GetById(ctx context.Context, id int) (*entity.NlUser, error) {
var user *entity.NlUser
err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Id, id).
Where(dao.columns.DeletedAt, 0).
Scan(&user)
return user, err
}
// GetByUsername 根据用户名获取用户
func (dao *UserDao) GetByUsername(ctx context.Context, username string) (*entity.NlUser, error) {
var user *entity.NlUser
err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Username, username).
Where(dao.columns.DeletedAt, 0).
Scan(&user)
return user, err
}
// GetByPhone 根据手机号获取用户
func (dao *UserDao) GetByPhone(ctx context.Context, phone string) (*entity.NlUser, error) {
var user *entity.NlUser
err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Phone, phone).
Where(dao.columns.DeletedAt, 0).
Scan(&user)
return user, err
}
// GetByEmail 根据邮箱获取用户
func (dao *UserDao) GetByEmail(ctx context.Context, email string) (*entity.NlUser, error) {
var user *entity.NlUser
err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Email, email).
Where(dao.columns.DeletedAt, 0).
Scan(&user)
return user, err
}
// Update 更新用户
func (dao *UserDao) Update(ctx context.Context, id int, data g.Map) error {
data[dao.columns.UpdatedAt] = gtime.Now().Unix()
_, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Id, id).
Where(dao.columns.DeletedAt, 0).
Data(data).
Update()
return err
}
// Delete 删除用户(软删除)
func (dao *UserDao) Delete(ctx context.Context, id int) error {
_, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Id, id).
Data(g.Map{
dao.columns.DeletedAt: gtime.Now().Unix(),
dao.columns.UpdatedAt: gtime.Now().Unix(),
}).
Update()
return err
}
// GetList 获取用户列表
func (dao *UserDao) GetList(ctx context.Context, req *UserListReq) ([]*entity.NlUser, int, error) {
model := g.DB(dao.group).Model(dao.table).Where(dao.columns.DeletedAt, 0)
// 添加查询条件
if req.Username != "" {
model = model.WhereLike(dao.columns.Username, "%"+req.Username+"%")
}
if req.Phone != "" {
model = model.WhereLike(dao.columns.Phone, "%"+req.Phone+"%")
}
if req.Email != "" {
model = model.WhereLike(dao.columns.Email, "%"+req.Email+"%")
}
if req.Status >= 0 {
model = model.Where(dao.columns.Status, req.Status)
}
if req.VipLevel > 0 {
model = model.Where(dao.columns.VipLevel, req.VipLevel)
}
if req.Gender >= 0 {
model = model.Where(dao.columns.Gender, req.Gender)
}
if req.StartTime != "" {
model = model.WhereGTE(dao.columns.CreatedAt, gtime.NewFromStr(req.StartTime).Unix())
}
if req.EndTime != "" {
model = model.WhereLTE(dao.columns.CreatedAt, gtime.NewFromStr(req.EndTime).Unix())
}
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页查询
var users []*entity.NlUser
err = model.Page(req.Page, req.PageSize).
OrderDesc(dao.columns.CreatedAt).
Scan(&users)
return users, total, err
}
// BatchUpdateStatus 批量更新用户状态
func (dao *UserDao) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
_, err := g.DB(dao.group).Model(dao.table).
WhereIn(dao.columns.Id, ids).
Where(dao.columns.DeletedAt, 0).
Data(g.Map{
dao.columns.Status: status,
dao.columns.UpdatedAt: gtime.Now().Unix(),
}).
Update()
return err
}
// BatchDelete 批量删除用户
func (dao *UserDao) BatchDelete(ctx context.Context, ids []int) error {
_, err := g.DB(dao.group).Model(dao.table).
WhereIn(dao.columns.Id, ids).
Data(g.Map{
dao.columns.DeletedAt: gtime.Now().Unix(),
dao.columns.UpdatedAt: gtime.Now().Unix(),
}).
Update()
return err
}
// GetUserStats 获取用户统计信息
func (dao *UserDao) GetUserStats(ctx context.Context) (g.Map, error) {
// 总用户数
totalUsers, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Count()
if err != nil {
return nil, err
}
// 活跃用户数
activeUsers, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Where(dao.columns.Status, 1).
Count()
if err != nil {
return nil, err
}
// VIP用户数
vipUsers, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Where(dao.columns.VipLevel+" > ?", 1).
Count()
if err != nil {
return nil, err
}
// 今日新增用户
todayStart := gtime.Now().StartOfDay().Unix()
todayUsers, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
WhereGTE(dao.columns.CreatedAt, todayStart).
Count()
if err != nil {
return nil, err
}
return g.Map{
"total_users": totalUsers,
"active_users": activeUsers,
"vip_users": vipUsers,
"today_users": todayUsers,
}, nil
}
// UpdateLoginInfo 更新登录信息
func (dao *UserDao) UpdateLoginInfo(ctx context.Context, id int, ip string) error {
_, err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.Id, id).
Data(g.Map{
dao.columns.LastLoginAt: gtime.Now().Unix(),
dao.columns.LastLoginIp: ip,
dao.columns.UpdatedAt: gtime.Now().Unix(),
}).
Update()
return err
}
// GetVipUsers 获取VIP用户列表
func (dao *UserDao) GetVipUsers(ctx context.Context, page, pageSize int) ([]*entity.NlUser, int, error) {
model := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Where(dao.columns.VipLevel+" > ?", 1)
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页查询
var users []*entity.NlUser
err = model.Page(page, pageSize).
OrderDesc(dao.columns.VipLevel).
OrderDesc(dao.columns.VipExpireAt).
Scan(&users)
return users, total, err
}
// GetExpiredVipUsers 获取VIP即将过期的用户
func (dao *UserDao) GetExpiredVipUsers(ctx context.Context, days int) ([]*entity.NlUser, error) {
expireTime := gtime.Now().AddDate(0, 0, days).Unix()
var users []*entity.NlUser
err := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Where(dao.columns.VipLevel+" > ?", 1).
Where(dao.columns.VipExpireAt+" <= ?", expireTime).
Where(dao.columns.VipExpireAt+" > ?", gtime.Now().Unix()).
Scan(&users)
return users, err
}
// SearchUsers 搜索用户
func (dao *UserDao) SearchUsers(ctx context.Context, keyword string, page, pageSize int) ([]*entity.NlUser, int, error) {
model := g.DB(dao.group).Model(dao.table).
Where(dao.columns.DeletedAt, 0).
Where(g.Map{
dao.columns.Username + " LIKE ? OR " + dao.columns.Phone + " LIKE ? OR " + dao.columns.Email + " LIKE ?": []interface{}{
"%" + keyword + "%",
"%" + keyword + "%",
"%" + keyword + "%",
},
})
// 获取总数
total, err := model.Count()
if err != nil {
return nil, 0, err
}
// 分页查询
var users []*entity.NlUser
err = model.Page(page, pageSize).
OrderDesc(dao.columns.CreatedAt).
Scan(&users)
return users, total, err
}

View File

@@ -0,0 +1,76 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalUserCollectDao is internal type for wrapping internal DAO implements.
type internalUserCollectDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns UserCollectColumns // columns contains all the column names of Table for convenient usage.
}
// UserCollectColumns defines and stores column names for table nl_user_collect.
type UserCollectColumns struct {
Id string // 收藏ID
UserId string // 用户ID
MovieId string // 影片ID
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// userCollectColumns holds the columns for table nl_user_collect.
var userCollectColumns = UserCollectColumns{
Id: "id",
UserId: "user_id",
MovieId: "movie_id",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewUserCollectDao creates and returns a new DAO object for table data access.
func NewUserCollectDao() *internalUserCollectDao {
return &internalUserCollectDao{
group: "default",
table: "nl_user_collect",
columns: userCollectColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalUserCollectDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalUserCollectDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalUserCollectDao) Columns() UserCollectColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalUserCollectDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalUserCollectDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalUserCollectDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

82
internal/dao/user_log.go Normal file
View File

@@ -0,0 +1,82 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalUserLogDao is internal type for wrapping internal DAO implements.
type internalUserLogDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns UserLogColumns // columns contains all the column names of Table for convenient usage.
}
// UserLogColumns defines and stores column names for table nl_user_log.
type UserLogColumns struct {
Id string // 日志ID
UserId string // 用户ID
Action string // 操作动作
Module string // 操作模块
Content string // 操作内容
Ip string // IP地址
UserAgent string // 用户代理
CreatedAt string // 创建时间
}
// userLogColumns holds the columns for table nl_user_log.
var userLogColumns = UserLogColumns{
Id: "id",
UserId: "user_id",
Action: "action",
Module: "module",
Content: "content",
Ip: "ip",
UserAgent: "user_agent",
CreatedAt: "created_at",
}
// NewUserLogDao creates and returns a new DAO object for table data access.
func NewUserLogDao() *internalUserLogDao {
return &internalUserLogDao{
group: "default",
table: "nl_user_log",
columns: userLogColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalUserLogDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalUserLogDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalUserLogDao) Columns() UserLogColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalUserLogDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalUserLogDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalUserLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,86 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalUserWatchHistoryDao is internal type for wrapping internal DAO implements.
type internalUserWatchHistoryDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns UserWatchHistoryColumns // columns contains all the column names of Table for convenient usage.
}
// UserWatchHistoryColumns defines and stores column names for table nl_user_watch_history.
type UserWatchHistoryColumns struct {
Id string // 观看记录ID
UserId string // 用户ID
MovieId string // 影片ID
EpisodeId string // 集数ID
WatchTime string // 观看时长(秒)
TotalTime string // 总时长(秒)
Progress string // 观看进度百分比
LastWatchTime string // 最后观看时间
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// userWatchHistoryColumns holds the columns for table nl_user_watch_history.
var userWatchHistoryColumns = UserWatchHistoryColumns{
Id: "id",
UserId: "user_id",
MovieId: "movie_id",
EpisodeId: "episode_id",
WatchTime: "watch_time",
TotalTime: "total_time",
Progress: "progress",
LastWatchTime: "last_watch_time",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewUserWatchHistoryDao creates and returns a new DAO object for table data access.
func NewUserWatchHistoryDao() *internalUserWatchHistoryDao {
return &internalUserWatchHistoryDao{
group: "default",
table: "nl_user_watch_history",
columns: userWatchHistoryColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalUserWatchHistoryDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalUserWatchHistoryDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalUserWatchHistoryDao) Columns() UserWatchHistoryColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalUserWatchHistoryDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalUserWatchHistoryDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalUserWatchHistoryDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

88
internal/dao/vip_level.go Normal file
View File

@@ -0,0 +1,88 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package dao
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// internalVipLevelDao is internal type for wrapping internal DAO implements.
type internalVipLevelDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns VipLevelColumns // columns contains all the column names of Table for convenient usage.
}
// VipLevelColumns defines and stores column names for table nl_vip_level.
type VipLevelColumns struct {
Id string // VIP等级ID
Name string // VIP等级名称
Level string // 等级数值
Price string // 价格
Duration string // 有效期(天)
Description string // 等级描述
Privileges string // 特权说明(JSON格式)
Status string // 状态0禁用1启用
Sort string // 排序
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
// vipLevelColumns holds the columns for table nl_vip_level.
var vipLevelColumns = VipLevelColumns{
Id: "id",
Name: "name",
Level: "level",
Price: "price",
Duration: "duration",
Description: "description",
Privileges: "privileges",
Status: "status",
Sort: "sort",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
// NewVipLevelDao creates and returns a new DAO object for table data access.
func NewVipLevelDao() *internalVipLevelDao {
return &internalVipLevelDao{
group: "default",
table: "nl_vip_level",
columns: vipLevelColumns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *internalVipLevelDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *internalVipLevelDao) Table() string {
return dao.table
}
// Columns returns the columns of current dao.
func (dao *internalVipLevelDao) Columns() VipLevelColumns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *internalVipLevelDao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *internalVipLevelDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
func (dao *internalVipLevelDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error {
return dao.Ctx(ctx).Transaction(ctx, f)
}

0
internal/logic/.gitkeep Normal file
View File

0
internal/model/.gitkeep Normal file
View File

View File

View File

View File

@@ -0,0 +1,25 @@
package entity
// NlAdmin 管理员表
type NlAdmin struct {
Id uint `json:"id" orm:"id,primary"` // 管理员ID
OpenId string `json:"open_id" orm:"open_id"` // OpenID用于第三方登录
Username string `json:"username" orm:"username"` // 用户名
JobNumber string `json:"job_number" orm:"job_number"` // 工号
Avatar string `json:"avatar" orm:"avatar"` // 头像
NickName string `json:"nick_name" orm:"nick_name"` // 昵称
Password string `json:"-" orm:"password"` // 密码
Phone string `json:"phone" orm:"phone"` // 手机号
Email string `json:"email" orm:"email"` // 邮箱
RoleId int `json:"role_id" orm:"role_id"` // 角色ID
Department string `json:"department" orm:"department"` // 部门
RegIp int64 `json:"reg_ip" orm:"reg_ip"` // 注册IP
LastLoginTime int `json:"last_login_time" orm:"last_login_time"` // 最后登录时间
LastLoginIp int64 `json:"last_login_ip" orm:"last_login_ip"` // 最后登录IP
OperationPassword string `json:"operation_password" orm:"operation_password"` // 操作密码
Desc string `json:"desc" orm:"desc"` // 备注
Status int `json:"status" orm:"status"` // 状态 1正常 0禁用
CreatedAt int `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt int `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt int `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// AdminLog is the golang structure for table nl_admin_log.
type AdminLog struct {
Id uint `json:"id" orm:"id" description:"日志ID"`
AdminId int `json:"admin_id" orm:"admin_id" description:"管理员ID"`
Action string `json:"action" orm:"action" description:"操作动作"`
Module string `json:"module" orm:"module" description:"操作模块"`
Content string `json:"content" orm:"content" description:"操作内容"`
Ip string `json:"ip" orm:"ip" description:"IP地址"`
UserAgent string `json:"user_agent" orm:"user_agent" description:"用户代理"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
}

View File

@@ -0,0 +1,24 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// Attachment is the golang structure for table nl_attachment.
type Attachment struct {
Id uint `json:"id" orm:"id" description:"附件ID"`
Name string `json:"name" orm:"name" description:"文件名"`
Path string `json:"path" orm:"path" description:"文件路径"`
Url string `json:"url" orm:"url" description:"访问URL"`
Size int64 `json:"size" orm:"size" description:"文件大小(字节)"`
MimeType string `json:"mime_type" orm:"mime_type" description:"文件类型"`
Extension string `json:"extension" orm:"extension" description:"文件扩展名"`
UserId int `json:"user_id" orm:"user_id" description:"上传用户ID"`
Status int `json:"status" orm:"status" description:"状态0禁用1启用"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// Banner is the golang structure for table nl_banner.
type Banner struct {
Id uint `json:"id" orm:"id" description:"轮播图ID"`
Title string `json:"title" orm:"title" description:"轮播图标题"`
ImageUrl string `json:"image_url" orm:"image_url" description:"图片URL"`
LinkUrl string `json:"link_url" orm:"link_url" description:"跳转链接"`
Sort int `json:"sort" orm:"sort" description:"排序"`
Status int `json:"status" orm:"status" description:"状态0禁用1启用"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// Comment is the golang structure for table nl_comment.
type Comment struct {
Id uint `json:"id" orm:"id" description:"评论ID"`
UserId int `json:"user_id" orm:"user_id" description:"用户ID"`
MovieId int `json:"movie_id" orm:"movie_id" description:"影片ID"`
ParentId int `json:"parent_id" orm:"parent_id" description:"父评论ID0为顶级评论"`
Content string `json:"content" orm:"content" description:"评论内容"`
LikeCount int `json:"like_count" orm:"like_count" description:"点赞数"`
Status int `json:"status" orm:"status" description:"状态0待审核1已通过2已拒绝"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,17 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// CommentLike is the golang structure for table comment_like.
type CommentLike struct {
Id uint `json:"id" orm:"id,primary" description:"点赞ID"`
CommentId uint `json:"comment_id" orm:"comment_id" description:"评论ID"`
UserId uint `json:"user_id" orm:"user_id" description:"用户ID"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// CommentReport is the golang structure for table comment_report.
type CommentReport struct {
Id uint `json:"id" orm:"id,primary" description:"举报ID"`
CommentId uint `json:"comment_id" orm:"comment_id" description:"评论ID"`
UserId uint `json:"user_id" orm:"user_id" description:"举报用户ID"`
Reason string `json:"reason" orm:"reason" description:"举报原因"`
Status int `json:"status" orm:"status" description:"处理状态(0:待处理,1:已处理,2:已忽略)"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// Config is the golang structure for table nl_config.
type Config struct {
Id uint `json:"id" orm:"id" description:"配置ID"`
ConfigKey string `json:"config_key" orm:"config_key" description:"配置键"`
ConfigValue string `json:"config_value" orm:"config_value" description:"配置值"`
ConfigType string `json:"config_type" orm:"config_type" description:"配置类型"`
Description string `json:"description" orm:"description" description:"配置描述"`
Status int `json:"status" orm:"status" description:"状态0禁用1启用"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,23 @@
package entity
// Episode 集数实体
type Episode struct {
Id int `json:"id" orm:"id,primary"` // 集数ID
MovieId int `json:"movie_id" orm:"movie_id"` // 影片ID
EpisodeNum int `json:"episode_num" orm:"episode_num"` // 集数
Title string `json:"title" orm:"title"` // 集数标题
Description string `json:"description" orm:"description"` // 集数简介
Duration int `json:"duration" orm:"duration"` // 时长(秒)
VideoUrl string `json:"video_url" orm:"video_url"` // 视频地址
VideoSize int64 `json:"video_size" orm:"video_size"` // 视频大小(字节)
VideoFormat string `json:"video_format" orm:"video_format"` // 视频格式
Resolution string `json:"resolution" orm:"resolution"` // 分辨率
Thumbnail string `json:"thumbnail" orm:"thumbnail"` // 缩略图
ViewCount int `json:"view_count" orm:"view_count"` // 观看次数
IsVip int `json:"is_vip" orm:"is_vip"` // 是否VIP专享0-否1-是
Sort int `json:"sort" orm:"sort"` // 排序
Status int `json:"status" orm:"status"` // 状态1-正常0-下架
CreatedAt string `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt string `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt string `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,35 @@
package entity
// Movie 影片实体
type Movie struct {
Id int `json:"id" orm:"id,primary"` // 影片ID
Title string `json:"title" orm:"title"` // 影片标题
OriginalTitle string `json:"original_title" orm:"original_title"` // 原始标题
Poster string `json:"poster" orm:"poster"` // 海报图片
Banner string `json:"banner" orm:"banner"` // 横幅图片
CategoryId int `json:"category_id" orm:"category_id"` // 分类ID
Type int `json:"type" orm:"type"` // 类型1-电影2-电视剧3-综艺4-动漫5-纪录片
Area string `json:"area" orm:"area"` // 地区
Language string `json:"language" orm:"language"` // 语言
Year int `json:"year" orm:"year"` // 年份
Duration int `json:"duration" orm:"duration"` // 时长(分钟)
Director string `json:"director" orm:"director"` // 导演
Actor string `json:"actor" orm:"actor"` // 演员
Description string `json:"description" orm:"description"` // 简介
Tags string `json:"tags" orm:"tags"` // 标签,逗号分隔
Rating float64 `json:"rating" orm:"rating"` // 评分
RatingCount int `json:"rating_count" orm:"rating_count"` // 评分人数
ViewCount int `json:"view_count" orm:"view_count"` // 观看次数
LikeCount int `json:"like_count" orm:"like_count"` // 点赞数
CollectCount int `json:"collect_count" orm:"collect_count"` // 收藏数
CommentCount int `json:"comment_count" orm:"comment_count"` // 评论数
IsVip int `json:"is_vip" orm:"is_vip"` // 是否VIP专享0-否1-是
IsRecommend int `json:"is_recommend" orm:"is_recommend"` // 是否推荐0-否1-是
IsHot int `json:"is_hot" orm:"is_hot"` // 是否热门0-否1-是
IsNew int `json:"is_new" orm:"is_new"` // 是否最新0-否1-是
Sort int `json:"sort" orm:"sort"` // 排序
Status int `json:"status" orm:"status"` // 状态1-正常0-下架
CreatedAt string `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt string `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt string `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,25 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// PaymentOrder is the golang structure for table nl_payment_order.
type PaymentOrder struct {
Id uint `json:"id" orm:"id" description:"订单ID"`
OrderNo string `json:"order_no" orm:"order_no" description:"订单号"`
UserId int `json:"user_id" orm:"user_id" description:"用户ID"`
VipLevelId int `json:"vip_level_id" orm:"vip_level_id" description:"VIP等级ID"`
Amount float64 `json:"amount" orm:"amount" description:"订单金额"`
PaymentMethod string `json:"payment_method" orm:"payment_method" description:"支付方式"`
PaymentStatus int `json:"payment_status" orm:"payment_status" description:"支付状态0待支付1已支付2已取消3已退款"`
PaymentTime *gtime.Time `json:"payment_time" orm:"payment_time" description:"支付时间"`
ExpireTime *gtime.Time `json:"expire_time" orm:"expire_time" description:"过期时间"`
Remark string `json:"remark" orm:"remark" description:"备注"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,24 @@
package entity
// Permission 权限实体
type Permission struct {
Id int `json:"id" orm:"id,primary"` // 权限ID
ParentId int `json:"parent_id" orm:"parent_id"` // 父级权限ID
Name string `json:"name" orm:"name"` // 权限名称
Slug string `json:"slug" orm:"slug"` // 权限标识
Type int `json:"type" orm:"type"` // 权限类型1-菜单2-按钮3-接口
Path string `json:"path" orm:"path"` // 路由路径
Component string `json:"component" orm:"component"` // 组件路径
Icon string `json:"icon" orm:"icon"` // 图标
Method string `json:"method" orm:"method"` // 请求方法
ApiPath string `json:"api_path" orm:"api_path"` // API路径
Description string `json:"description" orm:"description"` // 权限描述
IsHidden int `json:"is_hidden" orm:"is_hidden"` // 是否隐藏0-否1-是
IsCache int `json:"is_cache" orm:"is_cache"` // 是否缓存0-否1-是
IsSystem int `json:"is_system" orm:"is_system"` // 是否系统权限0-否1-是
Sort int `json:"sort" orm:"sort"` // 排序
Status int `json:"status" orm:"status"` // 状态1-启用0-禁用
CreatedAt string `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt string `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt string `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,16 @@
package entity
// Role 角色实体
type Role struct {
Id int `json:"id" orm:"id,primary"` // 角色ID
Name string `json:"name" orm:"name"` // 角色名称
Slug string `json:"slug" orm:"slug"` // 角色标识
Description string `json:"description" orm:"description"` // 角色描述
Level int `json:"level" orm:"level"` // 角色等级
IsSystem int `json:"is_system" orm:"is_system"` // 是否系统角色0-否1-是
Sort int `json:"sort" orm:"sort"` // 排序
Status int `json:"status" orm:"status"` // 状态1-启用0-禁用
CreatedAt string `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt string `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt string `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,32 @@
package entity
import (
"time"
)
// NlUser 用户表
type NlUser struct {
Id uint `json:"id" orm:"id,primary"` // 用户ID
OpenId string `json:"open_id" orm:"open_id"` // OpenID用于第三方登录
Username string `json:"username" orm:"username"` // 用户名
Avatar string `json:"avatar" orm:"avatar"` // 头像
NickName string `json:"nick_name" orm:"nick_name"` // 昵称
Password string `json:"-" orm:"password"` // 密码
Phone string `json:"phone" orm:"phone"` // 手机号
Email string `json:"email" orm:"email"` // 邮箱
Gender int `json:"gender" orm:"gender"` // 性别 0未知 1男 2女
Birthday *time.Time `json:"birthday" orm:"birthday"` // 生日
VipLevel int `json:"vip_level" orm:"vip_level"` // VIP等级 0普通用户 1VIP1 2VIP2
VipExpireTime int `json:"vip_expire_time" orm:"vip_expire_time"` // VIP到期时间
Balance float64 `json:"balance" orm:"balance"` // 余额
Points int `json:"points" orm:"points"` // 积分
RegIp int64 `json:"reg_ip" orm:"reg_ip"` // 注册IP
LastLoginTime int `json:"last_login_time" orm:"last_login_time"` // 最后登录时间
LastLoginIp int64 `json:"last_login_ip" orm:"last_login_ip"` // 最后登录IP
LoginCount int `json:"login_count" orm:"login_count"` // 登录次数
Desc string `json:"desc" orm:"desc"` // 备注
Status int `json:"status" orm:"status"` // 状态 1正常 0禁用
CreatedAt int `json:"created_at" orm:"created_at"` // 创建时间
UpdatedAt int `json:"updated_at" orm:"updated_at"` // 更新时间
DeletedAt int `json:"deleted_at" orm:"deleted_at"` // 删除时间
}

View File

@@ -0,0 +1,19 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// UserCollect is the golang structure for table nl_user_collect.
type UserCollect struct {
Id uint `json:"id" orm:"id" description:"收藏ID"`
UserId int `json:"user_id" orm:"user_id" description:"用户ID"`
MovieId int `json:"movie_id" orm:"movie_id" description:"影片ID"`
Type int `json:"type" orm:"type" description:"收藏类型 1影片 2演员 3导演"`
TargetId int `json:"target_id" orm:"target_id" description:"目标ID"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
}

View File

@@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// UserLog is the golang structure for table nl_user_log.
type UserLog struct {
Id uint `json:"id" orm:"id" description:"日志ID"`
UserId int `json:"user_id" orm:"user_id" description:"用户ID"`
Action string `json:"action" orm:"action" description:"操作动作"`
Module string `json:"module" orm:"module" description:"操作模块"`
Content string `json:"content" orm:"content" description:"操作内容"`
Ip string `json:"ip" orm:"ip" description:"IP地址"`
UserAgent string `json:"user_agent" orm:"user_agent" description:"用户代理"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
}

View File

@@ -0,0 +1,23 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// UserWatchHistory is the golang structure for table nl_user_watch_history.
type UserWatchHistory struct {
Id uint `json:"id" orm:"id" description:"观看记录ID"`
UserId int `json:"user_id" orm:"user_id" description:"用户ID"`
MovieId int `json:"movie_id" orm:"movie_id" description:"影片ID"`
EpisodeId int `json:"episode_id" orm:"episode_id" description:"集数ID"`
WatchTime int `json:"watch_time" orm:"watch_time" description:"观看时长(秒)"`
TotalTime int `json:"total_time" orm:"total_time" description:"总时长(秒)"`
Progress float64 `json:"progress" orm:"progress" description:"观看进度百分比"`
LastWatchTime int `json:"last_watch_time" orm:"last_watch_time" description:"最后观看时间"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1,24 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// VipLevel is the golang structure for table nl_vip_level.
type VipLevel struct {
Id uint `json:"id" orm:"id" description:"VIP等级ID"`
Name string `json:"name" orm:"name" description:"VIP等级名称"`
Level int `json:"level" orm:"level" description:"等级数值"`
Price float64 `json:"price" orm:"price" description:"价格"`
Duration int `json:"duration" orm:"duration" description:"有效期(天)"`
Description string `json:"description" orm:"description" description:"等级描述"`
Privileges string `json:"privileges" orm:"privileges" description:"特权说明(JSON格式)"`
Status int `json:"status" orm:"status" description:"状态0禁用1启用"`
Sort int `json:"sort" orm:"sort" description:"排序"`
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"`
}

View File

@@ -0,0 +1 @@
package packed

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
package auth
import (
"context"
"errors"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
)
// PermissionService 权限服务
type PermissionService struct{}
var Permission = &PermissionService{}
// PermissionCreateReq 创建权限请求
type PermissionCreateReq struct {
Name string `json:"name" v:"required|length:2,50#权限名称不能为空|权限名称长度为2-50位"`
Slug string `json:"slug" v:"required|length:2,100#权限标识不能为空|权限标识长度为2-100位"`
Type string `json:"type" v:"required|in:1,2,3#权限类型不能为空|权限类型只能是1,2,3"`
ParentId int `json:"parent_id" v:"min:0#父级ID不能小于0"`
Path string `json:"path"` // 路由路径
Component string `json:"component"` // 组件路径
Icon string `json:"icon"` // 图标
ApiPath string `json:"api_path"` // API路径
Method string `json:"method"` // 请求方法
Description string `json:"description"` // 权限描述
Sort int `json:"sort"` // 排序
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// PermissionUpdateReq 更新权限请求
type PermissionUpdateReq struct {
Id int `json:"id" v:"required|min:1#权限ID不能为空"`
Name string `json:"name" v:"required|length:2,50#权限名称不能为空|权限名称长度为2-50位"`
Slug string `json:"slug" v:"required|length:2,100#权限标识不能为空|权限标识长度为2-100位"`
Type string `json:"type" v:"required|in:1,2,3#权限类型不能为空|权限类型只能是1,2,3"`
ParentId int `json:"parent_id" v:"min:0#父级ID不能小于0"`
Path string `json:"path"` // 路由路径
Component string `json:"component"` // 组件路径
Icon string `json:"icon"` // 图标
ApiPath string `json:"api_path"` // API路径
Method string `json:"method"` // 请求方法
Description string `json:"description"` // 权限描述
Sort int `json:"sort"` // 排序
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// Create 创建权限
func (s *PermissionService) Create(ctx context.Context, req *PermissionCreateReq) (int, error) {
// 检查权限标识是否存在
exists, err := dao.Permission.CheckCodeExists(ctx, req.Slug, 0)
if err != nil {
return 0, err
}
if exists {
return 0, errors.New("权限标识已存在")
}
// 如果有父级ID检查父级是否存在
if req.ParentId > 0 {
parent, err := dao.Permission.GetById(ctx, req.ParentId)
if err != nil {
return 0, err
}
if parent == nil {
return 0, errors.New("父级权限不存在")
}
}
// 创建权限数据
permission := &entity.Permission{
Name: req.Name,
Slug: req.Slug,
Type: gconv.Int(req.Type),
ParentId: req.ParentId,
Path: req.Path,
Component: req.Component,
Icon: req.Icon,
ApiPath: req.ApiPath,
Method: req.Method,
Description: req.Description,
Sort: req.Sort,
Status: req.Status,
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
return dao.Permission.Create(ctx, permission)
}
// Update 更新权限
func (s *PermissionService) Update(ctx context.Context, req *PermissionUpdateReq) error {
// 检查权限是否存在
permission, err := dao.Permission.GetById(ctx, req.Id)
if err != nil {
return err
}
if permission == nil {
return errors.New("权限不存在")
}
// 检查权限标识是否存在(排除自己)
exists, err := dao.Permission.CheckCodeExists(ctx, req.Slug, req.Id)
if err != nil {
return err
}
if exists {
return errors.New("权限标识已存在")
}
// 如果有父级ID检查父级是否存在
if req.ParentId > 0 {
parent, err := dao.Permission.GetById(ctx, req.ParentId)
if err != nil {
return err
}
if parent == nil {
return errors.New("父级权限不存在")
}
// 不能将自己设为父级
if req.ParentId == req.Id {
return errors.New("不能将自己设为父级")
}
}
// 更新数据
updateData := g.Map{
"name": req.Name,
"slug": req.Slug,
"type": gconv.Int(req.Type),
"parent_id": req.ParentId,
"path": req.Path,
"component": req.Component,
"icon": req.Icon,
"api_path": req.ApiPath,
"method": req.Method,
"description": req.Description,
"sort": req.Sort,
"status": req.Status,
"updated_at": gtime.Now().String(),
}
return dao.Permission.Update(ctx, req.Id, updateData)
}
// GetById 获取权限详情
func (s *PermissionService) GetById(ctx context.Context, id int) (*entity.Permission, error) {
return dao.Permission.GetById(ctx, id)
}
// GetList 获取权限列表
func (s *PermissionService) GetList(ctx context.Context, req *dao.PermissionListReq) ([]*entity.Permission, int, error) {
return dao.Permission.GetList(ctx, req)
}
// GetTree 获取权限树形结构
func (s *PermissionService) GetTree(ctx context.Context) ([]*entity.Permission, error) {
return dao.Permission.GetTree(ctx)
}
// Delete 删除权限
func (s *PermissionService) Delete(ctx context.Context, id int) error {
// 检查权限是否存在
permission, err := dao.Permission.GetById(ctx, id)
if err != nil {
return err
}
if permission == nil {
return errors.New("权限不存在")
}
// 检查是否有子权限
children, err := dao.Permission.GetByParentId(ctx, id)
if err != nil {
return err
}
if len(children) > 0 {
return errors.New("存在子权限,无法删除")
}
return dao.Permission.Delete(ctx, id)
}
// GetMenuPermissions 获取菜单权限
func (s *PermissionService) GetMenuPermissions(ctx context.Context) ([]*entity.Permission, error) {
return dao.Permission.GetMenuPermissions(ctx)
}
// GetApiPermissions 获取API权限
func (s *PermissionService) GetApiPermissions(ctx context.Context) ([]*entity.Permission, error) {
return dao.Permission.GetApiPermissions(ctx)
}
// GetUserPermissions 获取用户权限
func (s *PermissionService) GetUserPermissions(ctx context.Context, userId int) ([]*entity.Permission, error) {
return dao.Permission.GetUserPermissions(ctx, userId)
}
// CheckUserPermission 检查用户是否有指定权限
func (s *PermissionService) CheckUserPermission(ctx context.Context, userId int, permissionSlug string) (bool, error) {
permissions, err := s.GetUserPermissions(ctx, userId)
if err != nil {
return false, err
}
for _, permission := range permissions {
if permission.Slug == permissionSlug {
return true, nil
}
}
return false, nil
}
// CheckApiPermission 检查API权限
func (s *PermissionService) CheckApiPermission(ctx context.Context, userId int, apiPath, method string) (bool, error) {
permissions, err := s.GetUserPermissions(ctx, userId)
if err != nil {
return false, err
}
for _, permission := range permissions {
if permission.Type == 3 && permission.ApiPath == apiPath && permission.Method == method {
return true, nil
}
}
return false, nil
}

View File

@@ -0,0 +1,328 @@
package auth
import (
"context"
"errors"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
)
// RoleService 角色服务
type RoleService struct{}
var Role = &RoleService{}
// RoleCreateReq 创建角色请求
type RoleCreateReq struct {
Name string `json:"name" v:"required|length:2,50#角色名称不能为空|角色名称长度为2-50位"`
Slug string `json:"slug" v:"required|length:2,50#角色标识不能为空|角色标识长度为2-50位"`
Level int `json:"level" v:"required|min:1,max:10#角色等级不能为空|角色等级范围1-10"`
Description string `json:"description"` // 角色描述
Sort int `json:"sort"` // 排序
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
IsSystem int `json:"is_system" v:"in:0,1#系统角色标识只能为0或1"`
}
// RoleUpdateReq 更新角色请求
type RoleUpdateReq struct {
Id int `json:"id" v:"required|min:1#角色ID不能为空"`
Name string `json:"name" v:"required|length:2,50#角色名称不能为空|角色名称长度为2-50位"`
Slug string `json:"slug" v:"required|length:2,50#角色标识不能为空|角色标识长度为2-50位"`
Level int `json:"level" v:"required|min:1,max:10#角色等级不能为空|角色等级范围1-10"`
Description string `json:"description"` // 角色描述
Sort int `json:"sort"` // 排序
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
IsSystem int `json:"is_system" v:"in:0,1#系统角色标识只能为0或1"`
}
// RolePermissionReq 角色权限分配请求
type RolePermissionReq struct {
RoleId int `json:"role_id" v:"required|min:1#角色ID不能为空"`
PermissionIds []int `json:"permission_ids"` // 权限ID列表
}
// Create 创建角色
func (s *RoleService) Create(ctx context.Context, req *RoleCreateReq) (int, error) {
// 检查角色标识是否存在
exists, err := dao.Role.CheckCodeExists(ctx, req.Slug, 0)
if err != nil {
return 0, err
}
if exists {
return 0, errors.New("角色标识已存在")
}
// 创建角色数据
role := &entity.Role{
Name: req.Name,
Slug: req.Slug,
Level: req.Level,
Description: req.Description,
Sort: req.Sort,
Status: req.Status,
IsSystem: req.IsSystem,
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
return dao.Role.Create(ctx, role)
}
// Update 更新角色
func (s *RoleService) Update(ctx context.Context, req *RoleUpdateReq) error {
// 检查角色是否存在
role, err := dao.Role.GetById(ctx, req.Id)
if err != nil {
return err
}
if role == nil {
return errors.New("角色不存在")
}
// 系统角色不允许修改标识和系统标识
if role.IsSystem == 1 {
if req.Slug != role.Slug {
return errors.New("系统角色不允许修改标识")
}
if req.IsSystem != role.IsSystem {
return errors.New("系统角色不允许修改系统标识")
}
}
// 检查角色标识是否存在(排除自己)
exists, err := dao.Role.CheckCodeExists(ctx, req.Slug, req.Id)
if err != nil {
return err
}
if exists {
return errors.New("角色标识已存在")
}
// 更新数据
updateData := g.Map{
"name": req.Name,
"slug": req.Slug,
"level": req.Level,
"description": req.Description,
"sort": req.Sort,
"status": req.Status,
"is_system": req.IsSystem,
"updated_at": gtime.Now().String(),
}
return dao.Role.Update(ctx, req.Id, updateData)
}
// GetById 获取角色详情
func (s *RoleService) GetById(ctx context.Context, id int) (*entity.Role, error) {
return dao.Role.GetById(ctx, id)
}
// GetList 获取角色列表
func (s *RoleService) GetList(ctx context.Context, req *dao.RoleListReq) ([]*entity.Role, int, error) {
return dao.Role.GetList(ctx, req)
}
// GetAll 获取所有角色
func (s *RoleService) GetAll(ctx context.Context) ([]*entity.Role, error) {
return dao.Role.GetAll(ctx)
}
// Delete 删除角色
func (s *RoleService) Delete(ctx context.Context, id int) error {
// 检查角色是否存在
role, err := dao.Role.GetById(ctx, id)
if err != nil {
return err
}
if role == nil {
return errors.New("角色不存在")
}
// 系统角色不允许删除
if role.IsSystem == 1 {
return errors.New("系统角色不允许删除")
}
// 检查是否有管理员使用该角色
// 这里需要查询管理员表,暂时跳过
return dao.Role.Delete(ctx, id)
}
// AssignPermissions 为角色分配权限
func (s *RoleService) AssignPermissions(ctx context.Context, req *RolePermissionReq) error {
// 检查角色是否存在
role, err := dao.Role.GetById(ctx, req.RoleId)
if err != nil {
return err
}
if role == nil {
return errors.New("角色不存在")
}
// 验证权限ID是否有效
if len(req.PermissionIds) > 0 {
permissions, err := dao.Permission.GetPermissionsByIds(ctx, req.PermissionIds)
if err != nil {
return err
}
if len(permissions) != len(req.PermissionIds) {
return errors.New("存在无效的权限ID")
}
}
return dao.Role.AssignPermissions(ctx, req.RoleId, req.PermissionIds)
}
// GetRolePermissions 获取角色权限
func (s *RoleService) GetRolePermissions(ctx context.Context, roleId int) ([]*entity.Permission, error) {
// 检查角色是否存在
role, err := dao.Role.GetById(ctx, roleId)
if err != nil {
return nil, err
}
if role == nil {
return nil, errors.New("角色不存在")
}
return dao.Role.GetRolePermissions(ctx, roleId)
}
// GetRolePermissionIds 获取角色权限ID列表
func (s *RoleService) GetRolePermissionIds(ctx context.Context, roleId int) ([]int, error) {
permissions, err := s.GetRolePermissions(ctx, roleId)
if err != nil {
return nil, err
}
var permissionIds []int
for _, permission := range permissions {
permissionIds = append(permissionIds, permission.Id)
}
return permissionIds, nil
}
// BatchUpdateStatus 批量更新角色状态
func (s *RoleService) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
if len(ids) == 0 {
return errors.New("请选择要操作的角色")
}
// 检查是否包含系统角色
roles, err := dao.Role.GetRolesByIds(ctx, ids)
if err != nil {
return err
}
for _, role := range roles {
if role.IsSystem == 1 {
return errors.New("不能修改系统角色状态")
}
}
// 批量更新
successCount := 0
for _, id := range ids {
updateData := g.Map{
"status": status,
"updated_at": gtime.Now().String(),
}
if err := dao.Role.Update(ctx, id, updateData); err != nil {
g.Log().Errorf(ctx, "批量更新角色状态失败: ID=%d, 错误=%v", id, err)
} else {
successCount++
}
}
if successCount == 0 {
return errors.New("批量更新失败")
}
return nil
}
// CopyRole 复制角色
func (s *RoleService) CopyRole(ctx context.Context, sourceId int, newName, newSlug string) (int, error) {
// 检查源角色是否存在
sourceRole, err := dao.Role.GetById(ctx, sourceId)
if err != nil {
return 0, err
}
if sourceRole == nil {
return 0, errors.New("源角色不存在")
}
// 检查新角色标识是否存在
exists, err := dao.Role.CheckCodeExists(ctx, newSlug, 0)
if err != nil {
return 0, err
}
if exists {
return 0, errors.New("角色标识已存在")
}
// 创建新角色
newRole := &entity.Role{
Name: newName,
Slug: newSlug,
Level: sourceRole.Level,
Description: sourceRole.Description + " (复制)",
Sort: sourceRole.Sort,
Status: 1, // 默认启用
IsSystem: 0, // 复制的角色不是系统角色
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
newRoleId, err := dao.Role.Create(ctx, newRole)
if err != nil {
return 0, err
}
// 复制权限
sourcePermissions, err := dao.Role.GetRolePermissions(ctx, sourceId)
if err != nil {
return newRoleId, err // 角色创建成功,但权限复制失败
}
if len(sourcePermissions) > 0 {
var permissionIds []int
for _, permission := range sourcePermissions {
permissionIds = append(permissionIds, permission.Id)
}
err = dao.Role.AssignPermissions(ctx, newRoleId, permissionIds)
if err != nil {
g.Log().Errorf(ctx, "复制角色权限失败: 新角色ID=%d, 错误=%v", newRoleId, err)
}
}
return newRoleId, nil
}
// GetRolesByLevel 根据等级获取角色
func (s *RoleService) GetRolesByLevel(ctx context.Context, level int) ([]*entity.Role, error) {
req := &dao.RoleListReq{
Level: level,
Status: 1, // 只获取启用的角色
Page: 0, // 不分页
PageSize: 0,
}
roles, _, err := dao.Role.GetList(ctx, req)
return roles, err
}
// CheckRoleExists 检查角色是否存在
func (s *RoleService) CheckRoleExists(ctx context.Context, id int) (bool, error) {
role, err := dao.Role.GetById(ctx, id)
if err != nil {
return false, err
}
return role != nil, nil
}

411
internal/service/banner.go Normal file
View File

@@ -0,0 +1,411 @@
package service
import (
"context"
"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/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// BannerService Banner服务
type BannerService struct{}
// NewBannerService 创建Banner服务实例
func NewBannerService() *BannerService {
return &BannerService{}
}
// 定义请求和响应结构体
type AdminBannerListReq struct {
Page int `json:"page"`
Size int `json:"size"`
Sort int `json:"sort"`
Status int `json:"status"`
Keyword string `json:"keyword"`
Category string `json:"category"`
}
type AdminBannerListRes struct {
List []AdminBannerItem `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
type AdminBannerItem struct {
Id uint `json:"id"`
Title string `json:"title"`
ImageUrl string `json:"image_url"`
LinkUrl string `json:"link_url"`
Sort int `json:"sort"`
Status int `json:"status"`
Description string `json:"description"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
type AdminBannerDetailReq struct {
Id uint `json:"id"`
}
type AdminBannerDetailRes struct {
Banner AdminBannerDetail `json:"banner"`
}
type AdminBannerDetail struct {
Id uint `json:"id"`
Title string `json:"title"`
ImageUrl string `json:"image_url"`
LinkUrl string `json:"link_url"`
Sort int `json:"sort"`
Status int `json:"status"`
Description string `json:"description"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
type AdminBannerCreateReq struct {
Title string `json:"title"`
ImageUrl string `json:"image_url"`
LinkUrl string `json:"link_url"`
Sort int `json:"sort"`
Status int `json:"status"`
Description string `json:"description"`
}
type AdminBannerUpdateReq struct {
Id uint `json:"id"`
Title string `json:"title"`
ImageUrl string `json:"image_url"`
LinkUrl string `json:"link_url"`
Sort int `json:"sort"`
Status int `json:"status"`
Description string `json:"description"`
}
type AdminBannerDeleteReq struct {
Id uint `json:"id"`
}
type AdminBannerUpdateStatusReq struct {
Id uint `json:"id"`
Status int `json:"status"`
}
type AdminBannerBatchDeleteReq struct {
Ids []uint `json:"ids"`
}
// UserGetList 用户获取轮播图列表
func (s *BannerService) UserGetList(r *ghttp.Request) {
// 获取请求参数
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
sort := r.Get("sort", 0).Int()
// 构建查询条件
query := dao.Banner.Ctx(r.Context()).Where("status", 1) // 只显示启用的轮播图
// 排序筛选
if sort > 0 {
query = query.Where("sort", sort)
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取轮播图总数失败")
return
}
// 获取列表数据
var banners []entity.Banner
err = query.Order("sort ASC, id DESC").
Limit((page-1)*size, size).
Scan(&banners)
if err != nil {
response.Error(r, response.CodeInternalError, "获取轮播图列表失败")
return
}
// 转换为响应格式
list := make([]g.Map, 0, len(banners))
for _, banner := range banners {
list = append(list, g.Map{
"id": banner.Id,
"title": banner.Title,
"image_url": banner.ImageUrl,
"link_url": banner.LinkUrl,
"sort": banner.Sort,
"status": banner.Status,
"created_at": banner.CreatedAt.Unix(),
"updated_at": banner.UpdatedAt.Unix(),
})
}
response.Success(r, g.Map{
"list": list,
"total": total,
"page": page,
"size": size,
})
}
// UserGetDetail 用户获取轮播图详情
func (s *BannerService) UserGetDetail(r *ghttp.Request) {
id := r.Get("id").Uint()
if id == 0 {
response.Error(r, response.CodeInvalidParam, "轮播图ID不能为空")
return
}
var banner entity.Banner
err := dao.Banner.Ctx(r.Context()).Where("id", id).Where("status", 1).Scan(&banner)
if err != nil {
response.Error(r, response.CodeInternalError, "获取轮播图详情失败")
return
}
if banner.Id == 0 {
response.Error(r, response.CodeNotFound, "轮播图不存在")
return
}
response.Success(r, g.Map{
"id": banner.Id,
"title": banner.Title,
"image_url": banner.ImageUrl,
"link_url": banner.LinkUrl,
"sort": banner.Sort,
"status": banner.Status,
"created_at": banner.CreatedAt.Unix(),
"updated_at": banner.UpdatedAt.Unix(),
})
}
// AdminCreate 管理员创建轮播图
func (s *BannerService) AdminCreate(r *ghttp.Request) {
// 获取请求参数
title := r.Get("title").String()
imageUrl := r.Get("image_url").String()
linkUrl := r.Get("link_url").String()
sort := r.Get("sort", 0).Int()
status := r.Get("status", 1).Int()
// 参数验证
if title == "" {
response.Error(r, response.CodeInvalidParam, "标题不能为空")
return
}
if imageUrl == "" {
response.Error(r, response.CodeInvalidParam, "图片地址不能为空")
return
}
// 创建轮播图
data := &entity.Banner{
Title: title,
ImageUrl: imageUrl,
LinkUrl: linkUrl,
Sort: sort,
Status: status,
CreatedAt: gtime.Now(),
UpdatedAt: gtime.Now(),
}
id, err := dao.Banner.Ctx(r.Context()).Data(data).InsertAndGetId()
if err != nil {
response.Error(r, response.CodeInternalError, "创建轮播图失败")
return
}
response.Success(r, g.Map{
"id": uint(id),
})
}
// AdminUpdate 管理员更新轮播图
func (s *BannerService) AdminUpdate(ctx context.Context, req *AdminBannerUpdateReq) error {
// 检查轮播图是否存在
count, err := dao.Banner.Ctx(ctx).Where("id", req.Id).Count()
if err != nil {
return gerror.New("检查轮播图失败")
}
if count == 0 {
return gerror.New("轮播图不存在")
}
// 更新轮播图
data := g.Map{
"title": req.Title,
"image_url": req.ImageUrl,
"link_url": req.LinkUrl,
"sort": req.Sort,
"status": req.Status,
"description": req.Description,
"updated_at": gtime.Now(),
}
_, err = dao.Banner.Ctx(ctx).Where("id", req.Id).Data(data).Update()
if err != nil {
return gerror.New("更新轮播图失败")
}
return nil
}
// AdminDelete 管理员删除轮播图
func (s *BannerService) AdminDelete(ctx context.Context, req *AdminBannerDeleteReq) error {
// 检查轮播图是否存在
count, err := dao.Banner.Ctx(ctx).Where("id", req.Id).Count()
if err != nil {
return gerror.New("检查轮播图失败")
}
if count == 0 {
return gerror.New("轮播图不存在")
}
// 删除轮播图
_, err = dao.Banner.Ctx(ctx).Where("id", req.Id).Delete()
if err != nil {
return gerror.New("删除轮播图失败")
}
return nil
}
// AdminGetDetail 管理员获取轮播图详情
func (s *BannerService) AdminGetDetail(ctx context.Context, req *AdminBannerDetailReq) (*AdminBannerDetailRes, error) {
var banner entity.Banner
err := dao.Banner.Ctx(ctx).Where("id", req.Id).Scan(&banner)
if err != nil {
return nil, gerror.New("获取轮播图详情失败")
}
if banner.Id == 0 {
return nil, gerror.New("轮播图不存在")
}
return &AdminBannerDetailRes{
Banner: AdminBannerDetail{
Id: banner.Id,
Title: banner.Title,
ImageUrl: banner.ImageUrl,
LinkUrl: banner.LinkUrl,
Sort: banner.Sort,
Status: banner.Status,
CreatedAt: banner.CreatedAt.Unix(),
UpdatedAt: banner.UpdatedAt.Unix(),
},
}, nil
}
// AdminGetList 管理员获取轮播图列表
func (s *BannerService) AdminGetList(ctx context.Context, req *AdminBannerListReq) (*AdminBannerListRes, error) {
// 设置默认值
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
// 构建查询条件
query := dao.Banner.Ctx(ctx)
// 状态筛选
if req.Status >= 0 {
query = query.Where("status", req.Status)
}
// 排序筛选
if req.Sort > 0 {
query = query.Where("sort", req.Sort)
}
// 关键词搜索
if req.Keyword != "" {
keyword := "%" + req.Keyword + "%"
query = query.Where("title LIKE ?", keyword)
}
// 获取总数
total, err := query.Count()
if err != nil {
return nil, gerror.New("获取轮播图总数失败")
}
// 获取列表数据
var banners []entity.Banner
err = query.Order("sort ASC, id DESC").
Limit((req.Page-1)*req.Size, req.Size).
Scan(&banners)
if err != nil {
return nil, gerror.New("获取轮播图列表失败")
}
// 转换为响应格式
list := make([]AdminBannerItem, 0, len(banners))
for _, banner := range banners {
list = append(list, AdminBannerItem{
Id: banner.Id,
Title: banner.Title,
ImageUrl: banner.ImageUrl,
LinkUrl: banner.LinkUrl,
Sort: banner.Sort,
Status: banner.Status,
Description: "", // 实体中没有此字段,设为空
CreatedAt: banner.CreatedAt.Unix(),
UpdatedAt: banner.UpdatedAt.Unix(),
})
}
return &AdminBannerListRes{
List: list,
Total: total,
Page: req.Page,
Size: req.Size,
}, nil
}
// AdminUpdateStatus 管理员更新轮播图状态
func (s *BannerService) AdminUpdateStatus(ctx context.Context, req *AdminBannerUpdateStatusReq) error {
// 检查轮播图是否存在
count, err := dao.Banner.Ctx(ctx).Where("id", req.Id).Count()
if err != nil {
return gerror.New("检查轮播图失败")
}
if count == 0 {
return gerror.New("轮播图不存在")
}
// 更新状态
_, err = dao.Banner.Ctx(ctx).Where("id", req.Id).Data(g.Map{
"status": req.Status,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
return gerror.New("更新轮播图状态失败")
}
return nil
}
// AdminBatchDelete 管理员批量删除轮播图
func (s *BannerService) AdminBatchDelete(ctx context.Context, req *AdminBannerBatchDeleteReq) error {
if len(req.Ids) == 0 {
return gerror.New("请选择要删除的轮播图")
}
// 批量删除
_, err := dao.Banner.Ctx(ctx).WhereIn("id", req.Ids).Delete()
if err != nil {
return gerror.New("批量删除轮播图失败")
}
return nil
}

428
internal/service/comment.go Normal file
View File

@@ -0,0 +1,428 @@
package service
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"nl-video-api/internal/dao"
"nl-video-api/utility/response"
)
// CommentService 评论服务
type CommentService struct{}
// NewCommentService 创建评论服务实例
func NewCommentService() *CommentService {
return &CommentService{}
}
// Add 添加评论
func (s *CommentService) Add(r *ghttp.Request) {
// 获取用户ID这里应该从JWT token中获取
userId := r.Get("user_id").Uint()
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
// 获取请求参数
movieId := r.Get("movie_id").Uint()
if movieId == 0 {
response.Error(r, response.CodeInvalidParam, "电影ID不能为空")
return
}
content := r.Get("content").String()
if content == "" {
response.Error(r, response.CodeInvalidParam, "评论内容不能为空")
return
}
parentId := r.Get("parent_id").Uint()
// 检查电影是否存在
movieCount, err := g.DB().Model("movie").Where("id", movieId).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if movieCount == 0 {
response.Error(r, response.CodeNotFound, "电影不存在")
return
}
// 如果是回复评论,检查父评论是否存在
if parentId > 0 {
parentCount, err := dao.Comment.Ctx(r.Context()).Where("id", parentId).Where("movie_id", movieId).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if parentCount == 0 {
response.Error(r, response.CodeNotFound, "父评论不存在")
return
}
}
// 创建评论
commentId, err := dao.Comment.Ctx(r.Context()).Data(g.Map{
"user_id": userId,
"movie_id": movieId,
"parent_id": parentId,
"content": content,
"like_count": 0,
"status": 1, // 默认通过审核
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).InsertAndGetId()
if err != nil {
response.Error(r, response.CodeInternalError, "添加评论失败")
return
}
response.Success(r, g.Map{
"id": commentId,
})
}
// GetList 获取评论列表
func (s *CommentService) GetList(r *ghttp.Request) {
movieId := r.Get("movie_id").Uint()
if movieId == 0 {
response.Error(r, response.CodeInvalidParam, "电影ID不能为空")
return
}
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
parentId := r.Get("parent_id", 0).Uint()
// 构建查询条件
query := dao.Comment.Ctx(r.Context()).Where("movie_id", movieId).Where("status", 1)
if parentId > 0 {
query = query.Where("parent_id", parentId)
} else {
query = query.Where("parent_id", 0)
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取评论总数失败")
return
}
// 获取评论列表
var comments []g.Map
err = query.Order("created_at DESC").
Limit((page-1)*size, size).
Scan(&comments)
if err != nil {
response.Error(r, response.CodeInternalError, "获取评论列表失败")
return
}
// 获取用户信息
userIds := make([]interface{}, 0)
for _, comment := range comments {
userIds = append(userIds, comment["user_id"])
}
userMap := make(map[uint]g.Map)
if len(userIds) > 0 {
var users []g.Map
g.DB().Model("nl_user").WhereIn("id", userIds).Fields("id, username, avatar").Scan(&users)
for _, user := range users {
userMap[gconv.Uint(user["id"])] = user
}
}
// 组装返回数据
list := make([]g.Map, 0)
for _, comment := range comments {
userId := gconv.Uint(comment["user_id"])
user := userMap[userId]
item := g.Map{
"id": comment["id"],
"user_id": comment["user_id"],
"username": user["username"],
"avatar": user["avatar"],
"movie_id": comment["movie_id"],
"parent_id": comment["parent_id"],
"content": comment["content"],
"like_count": comment["like_count"],
"created_at": gconv.Int64(comment["created_at"]),
"replies": make([]g.Map, 0), // 子评论,如果需要可以递归获取
}
list = append(list, item)
}
response.Success(r, g.Map{
"list": list,
"total": total,
"page": page,
"size": size,
})
}
// Delete 删除评论
func (s *CommentService) Delete(r *ghttp.Request) {
// 获取用户ID
userId := r.Get("user_id").Uint()
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
commentId := r.Get("id").Uint()
if commentId == 0 {
response.Error(r, response.CodeInvalidParam, "评论ID不能为空")
return
}
// 检查评论是否存在且属于当前用户
var comment g.Map
err := dao.Comment.Ctx(r.Context()).Where("id", commentId).Where("user_id", userId).Scan(&comment)
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if len(comment) == 0 {
response.Error(r, response.CodeNotFound, "评论不存在或无权限删除")
return
}
// 删除评论(软删除,更新状态)
_, err = dao.Comment.Ctx(r.Context()).Where("id", commentId).Data(g.Map{
"status": 0, // 0表示已删除
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "删除评论失败")
return
}
// 同时删除该评论的所有回复
_, err = dao.Comment.Ctx(r.Context()).Where("parent_id", commentId).Data(g.Map{
"status": 0,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
// 记录日志,但不影响主要操作
g.Log().Error(r.Context(), "删除子评论失败:", err)
}
response.Success(r, "删除成功")
}
// Like 点赞评论
func (s *CommentService) Like(r *ghttp.Request) {
// 获取用户ID
userId := r.Get("user_id").Uint()
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
commentId := r.Get("id").Uint()
if commentId == 0 {
response.Error(r, response.CodeInvalidParam, "评论ID不能为空")
return
}
// 检查评论是否存在
commentCount, err := dao.Comment.Ctx(r.Context()).Where("id", commentId).Where("status", 1).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if commentCount == 0 {
response.Error(r, response.CodeNotFound, "评论不存在")
return
}
// 检查是否已经点赞
likeCount, err := g.DB().Model("comment_like").Where("user_id", userId).Where("comment_id", commentId).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if likeCount > 0 {
// 取消点赞
_, err = g.DB().Model("comment_like").Where("user_id", userId).Where("comment_id", commentId).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "取消点赞失败")
return
}
// 减少点赞数
_, err = dao.Comment.Ctx(r.Context()).Where("id", commentId).Data("like_count=like_count-1").Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新点赞数失败")
return
}
response.Success(r, g.Map{
"action": "unlike",
"message": "取消点赞成功",
})
} else {
// 添加点赞
_, err = g.DB().Model("comment_like").Data(g.Map{
"user_id": userId,
"comment_id": commentId,
"created_at": gtime.Now(),
}).Insert()
if err != nil {
response.Error(r, response.CodeInternalError, "点赞失败")
return
}
// 增加点赞数
_, err = dao.Comment.Ctx(r.Context()).Where("id", commentId).Data("like_count=like_count+1").Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新点赞数失败")
return
}
response.Success(r, g.Map{
"action": "like",
"message": "点赞成功",
})
}
}
// Report 举报评论
func (s *CommentService) Report(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
reason := r.Get("reason").String()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "评论ID不能为空")
return
}
if reason == "" {
response.Error(r, response.CodeInvalidParam, "举报原因不能为空")
return
}
// 检查评论是否存在
count, err := dao.Comment.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查评论失败")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "评论不存在")
return
}
// 这里应该实现举报逻辑,创建举报记录
response.Success(r, "举报成功")
}
// Unlike 取消点赞评论
func (s *CommentService) Unlike(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "评论ID不能为空")
return
}
// 检查评论是否存在
count, err := dao.Comment.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查评论失败")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "评论不存在")
return
}
// 这里应该实现取消点赞逻辑
response.Success(r, "取消点赞成功")
}
// AdminList 管理员获取评论列表
func (s *CommentService) AdminList(r *ghttp.Request) {
s.AdminGetList(r)
}
// AdminUpdateStatus 管理员更新评论状态
func (s *CommentService) AdminUpdateStatus(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
status := r.Get("status").Int()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "评论ID不能为空")
return
}
// 检查评论是否存在
count, err := dao.Comment.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查评论失败")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "评论不存在")
return
}
// 更新评论状态
_, err = dao.Comment.Ctx(r.Context()).Where("id", id).Data(g.Map{
"status": status,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新评论状态失败")
return
}
response.Success(r, "更新成功")
}
// AdminGetList 管理员获取评论列表
func (s *CommentService) AdminGetList(r *ghttp.Request) {
s.GetList(r)
}
// AdminBatchDelete 管理员批量删除评论
func (s *CommentService) AdminBatchDelete(r *ghttp.Request) {
// 获取请求参数
var ids []uint
r.Parse(&ids)
if len(ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要删除的评论")
return
}
// 批量删除评论(软删除)
_, err := dao.Comment.Ctx(r.Context()).WhereIn("id", ids).Data(g.Map{
"status": 0,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "批量删除评论失败")
return
}
response.Success(r, "批量删除成功")
}

391
internal/service/config.go Normal file
View File

@@ -0,0 +1,391 @@
package service
import (
"fmt"
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// ConfigService 系统配置服务
type ConfigService struct{}
var Config = &ConfigService{}
// NewConfigService 创建配置服务实例
func NewConfigService() *ConfigService {
return &ConfigService{}
}
// AdminList 管理员获取配置列表
func (s *ConfigService) AdminList(r *ghttp.Request) {
response.Success(r, g.Map{
"list": []interface{}{},
"total": 0,
})
}
// AdminDetail 管理员获取配置详情
func (s *ConfigService) AdminDetail(r *ghttp.Request) {
response.Success(r, g.Map{
"id": 1,
"key": "site_name",
"value": "NL视频网站",
"desc": "网站名称",
})
}
// AdminCreate 管理员创建配置
func (s *ConfigService) AdminCreate(r *ghttp.Request) {
response.Success(r, "创建成功")
}
// AdminUpdate 管理员更新配置
func (s *ConfigService) AdminUpdate(r *ghttp.Request) {
response.Success(r, "更新成功")
}
// AdminDelete 管理员删除配置
func (s *ConfigService) AdminDelete(r *ghttp.Request) {
response.Success(r, "删除成功")
}
// AdminBatchDelete 管理员批量删除配置
func (s *ConfigService) AdminBatchDelete(r *ghttp.Request) {
response.Success(r, "批量删除成功")
}
// AdminGetByKey 管理员根据键获取配置
func (s *ConfigService) AdminGetByKey(r *ghttp.Request) {
response.Success(r, g.Map{
"key": "site_name",
"value": "NL视频网站",
})
}
// AdminGetByGroup 管理员根据分组获取配置
func (s *ConfigService) AdminGetByGroup(r *ghttp.Request) {
response.Success(r, g.Map{
"list": []interface{}{},
"total": 0,
})
}
// AdminSet 管理员设置配置
func (s *ConfigService) AdminSet(r *ghttp.Request) {
response.Success(r, "设置成功")
}
// AdminBatchSet 管理员批量设置配置
func (s *ConfigService) AdminBatchSet(r *ghttp.Request) {
response.Success(r, "批量设置成功")
}
// AdminGetGroupList 管理员获取配置分组列表
func (s *ConfigService) AdminGetGroupList(r *ghttp.Request) {
response.Success(r, g.Map{
"list": []string{"基础设置", "系统设置", "邮件设置"},
})
}
// AdminExport 管理员导出配置
func (s *ConfigService) AdminExport(r *ghttp.Request) {
response.Success(r, "导出成功")
}
// AdminImport 管理员导入配置
func (s *ConfigService) AdminImport(r *ghttp.Request) {
response.Success(r, "导入成功")
}
// AdminCache 管理员获取缓存配置
func (s *ConfigService) AdminCache(r *ghttp.Request) {
response.Success(r, g.Map{
"cache_enabled": true,
"cache_time": 3600,
})
}
// AdminClearCache 管理员清除配置缓存
func (s *ConfigService) AdminClearCache(r *ghttp.Request) {
response.Success(r, "缓存清除成功")
}
// AdminValidate 管理员验证配置
func (s *ConfigService) AdminValidate(r *ghttp.Request) {
response.Success(r, "配置验证通过")
}
// AdminBackup 管理员备份配置
func (s *ConfigService) AdminBackup(r *ghttp.Request) {
response.Success(r, "配置备份成功")
}
// AdminRestore 管理员恢复配置
func (s *ConfigService) AdminRestore(r *ghttp.Request) {
response.Success(r, "配置恢复成功")
}
// AdminGetHistory 管理员获取配置历史
func (s *ConfigService) AdminGetHistory(r *ghttp.Request) {
response.Success(r, g.Map{
"list": []interface{}{},
"total": 0,
})
}
// GetList 获取配置列表
func (s *ConfigService) GetList(r *ghttp.Request) {
// 获取请求参数
keyword := r.Get("keyword").String()
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 构建查询条件
query := dao.Config.Ctx(r.Context())
if keyword != "" {
query = query.Where("`key` LIKE ? OR name LIKE ?",
fmt.Sprintf("%%%s%%", keyword),
fmt.Sprintf("%%%s%%", keyword))
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取配置总数失败")
return
}
// 获取列表
var list []*entity.Config
err = query.Page(page, pageSize).OrderAsc("`key`").Scan(&list)
if err != nil {
response.Error(r, response.CodeInternalError, "获取配置列表失败")
return
}
response.Success(r, g.Map{
"list": list,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// GetByKey 根据键获取配置
func (s *ConfigService) GetByKey(r *ghttp.Request) {
// 获取请求参数
configKey := r.Get("key").String()
if configKey == "" {
response.Error(r, response.CodeInvalidParam, "配置键不能为空")
return
}
// 查询配置
var config entity.Config
err := dao.Config.Ctx(r.Context()).Where("`key`", configKey).Scan(&config)
if err != nil {
response.Error(r, response.CodeInternalError, "获取配置失败")
return
}
if config.Id == 0 {
response.Error(r, response.CodeNotFound, "配置不存在")
return
}
response.Success(r, config)
}
// Add 添加配置
func (s *ConfigService) Add(r *ghttp.Request) {
// 获取请求参数
configKey := r.Get("config_key").String()
configName := r.Get("config_name").String()
configValue := r.Get("config_value").String()
configType := r.Get("config_type").String()
description := r.Get("description").String()
// 参数验证
if configKey == "" || configName == "" {
response.Error(r, response.CodeInvalidParam, "配置键和配置名称不能为空")
return
}
// 检查配置键是否已存在
count, err := dao.Config.Ctx(r.Context()).Where("config_key", configKey).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查配置键失败")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "配置键已存在")
return
}
// 创建配置
_, err = dao.Config.Ctx(r.Context()).Data(g.Map{
"config_key": configKey,
"config_name": configName,
"config_value": configValue,
"config_type": configType,
"description": description,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
response.Error(r, response.CodeInternalError, "添加配置失败")
return
}
response.Success(r, "添加成功")
}
// Update 更新配置
func (s *ConfigService) Update(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
configName := r.Get("config_name").String()
configValue := r.Get("config_value").String()
configType := r.Get("config_type").String()
description := r.Get("description").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "配置ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "配置ID格式错误")
return
}
// 检查配置是否存在
count, err := dao.Config.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查配置失败")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "配置不存在")
return
}
// 更新配置
updateData := g.Map{
"updated_at": gtime.Now(),
}
if configName != "" {
updateData["config_name"] = configName
}
if configValue != "" {
updateData["config_value"] = configValue
}
if configType != "" {
updateData["config_type"] = configType
}
if description != "" {
updateData["description"] = description
}
_, err = dao.Config.Ctx(r.Context()).Where("id", id).Data(updateData).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新配置失败")
return
}
response.Success(r, "更新成功")
}
// Delete 删除配置
func (s *ConfigService) Delete(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "配置ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "配置ID格式错误")
return
}
// 检查配置是否存在
count, err := dao.Config.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查配置失败")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "配置不存在")
return
}
// 删除配置
_, err = dao.Config.Ctx(r.Context()).Where("id", id).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "删除配置失败")
return
}
response.Success(r, "删除成功")
}
// BatchDelete 批量删除配置
func (s *ConfigService) BatchDelete(r *ghttp.Request) {
// 获取请求参数
var ids []int
err := r.Parse(&ids)
if err != nil {
response.Error(r, response.CodeInvalidParam, "参数格式错误")
return
}
if len(ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要删除的配置")
return
}
// 批量删除
_, err = dao.Config.Ctx(r.Context()).Where("id IN (?)", ids).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "批量删除失败")
return
}
response.Success(r, "删除成功")
}
// GetPublicConfigs 获取公开配置(用户端)
func (s *ConfigService) GetPublicConfigs(r *ghttp.Request) {
// 返回默认的公开配置
configMap := map[string]interface{}{
"site_name": "NL在线影院",
"site_logo": "",
"site_description": "NL在线影院 - 海量高清影视资源在线观看",
"site_keywords": "NL影院,在线观看,高清影视",
"upload_max_size": "10485760",
"upload_allowed_types": "jpg,jpeg,png,gif,mp4,avi,mkv",
}
response.Success(r, configMap)
}

591
internal/service/log.go Normal file
View File

@@ -0,0 +1,591 @@
package service
import (
"fmt"
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// LogService 日志服务
type LogService struct{}
var Log = &LogService{}
// AddAdminLog 添加管理员日志
func (s *LogService) AddAdminLog(r *ghttp.Request) {
// 获取请求参数
action := r.Get("action").String()
module := r.Get("module").String()
content := r.Get("content").String()
ip := r.Get("ip").String()
// 参数验证
if action == "" || module == "" {
response.Error(r, response.CodeInvalidParam, "操作和模块不能为空")
return
}
// 从上下文获取管理员ID
adminIdValue := r.Context().Value("admin_id")
if adminIdValue == nil {
response.Error(r, response.CodeUnauthorized, "请先登录")
return
}
adminId := adminIdValue.(uint)
// 创建管理员日志
_, err := dao.AdminLog.Ctx(r.Context()).Data(g.Map{
"admin_id": adminId,
"action": action,
"module": module,
"content": content,
"ip": ip,
"created_at": gtime.Now(),
}).Insert()
if err != nil {
response.Error(r, response.CodeInternalError, "添加日志失败")
return
}
response.Success(r, "添加成功")
}
// GetAdminLogList 获取管理员日志列表
func (s *LogService) GetAdminLogList(r *ghttp.Request) {
// 获取请求参数
adminIdStr := r.Get("admin_id").String()
action := r.Get("action").String()
module := r.Get("module").String()
keyword := r.Get("keyword").String()
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 构建查询条件
query := dao.AdminLog.Ctx(r.Context())
if adminIdStr != "" {
adminId, err := strconv.Atoi(adminIdStr)
if err == nil {
query = query.Where("admin_id", adminId)
}
}
if action != "" {
query = query.Where("action", action)
}
if module != "" {
query = query.Where("module", module)
}
if keyword != "" {
query = query.WhereLike("content", fmt.Sprintf("%%%s%%", keyword))
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志总数失败")
return
}
// 获取列表
var list []*entity.AdminLog
err = query.Page(page, pageSize).OrderDesc("created_at").Scan(&list)
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志列表失败")
return
}
// 构建返回数据
var logItems []g.Map
for _, log := range list {
// 获取管理员信息使用NlUser表
var admin entity.NlUser
g.DB().Model("nl_user").Where("id", log.AdminId).Where("user_type", "admin").Scan(&admin)
logItems = append(logItems, g.Map{
"id": log.Id,
"admin_id": log.AdminId,
"admin_name": admin.Username,
"action": log.Action,
"module": log.Module,
"content": log.Content,
"ip": log.Ip,
"created_at": log.CreatedAt,
})
}
response.Success(r, g.Map{
"list": logItems,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// DeleteAdminLog 删除管理员日志
func (s *LogService) DeleteAdminLog(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "日志ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "日志ID格式错误")
return
}
// 删除日志
_, err = dao.AdminLog.Ctx(r.Context()).Where("id", id).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "删除日志失败")
return
}
response.Success(r, "删除成功")
}
// BatchDeleteAdminLog 批量删除管理员日志
func (s *LogService) BatchDeleteAdminLog(r *ghttp.Request) {
// 获取请求参数
var ids []int
err := r.Parse(&ids)
if err != nil {
response.Error(r, response.CodeInvalidParam, "参数格式错误")
return
}
if len(ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要删除的日志")
return
}
// 批量删除
_, err = dao.AdminLog.Ctx(r.Context()).Where("id IN (?)", ids).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "批量删除失败")
return
}
response.Success(r, "删除成功")
}
// AddUserLog 添加用户日志
func (s *LogService) AddUserLog(r *ghttp.Request) {
// 获取请求参数
action := r.Get("action").String()
module := r.Get("module").String()
content := r.Get("content").String()
ip := r.Get("ip").String()
// 参数验证
if action == "" || module == "" {
response.Error(r, response.CodeInvalidParam, "操作和模块不能为空")
return
}
// 从上下文获取用户ID
userIdValue := r.Context().Value("user_id")
if userIdValue == nil {
response.Error(r, response.CodeUnauthorized, "请先登录")
return
}
userId := userIdValue.(uint)
// 创建用户日志
_, err := dao.UserLog.Ctx(r.Context()).Data(g.Map{
"user_id": userId,
"action": action,
"module": module,
"content": content,
"ip": ip,
"created_at": gtime.Now(),
}).Insert()
if err != nil {
response.Error(r, response.CodeInternalError, "添加日志失败")
return
}
response.Success(r, "添加成功")
}
// GetUserLogList 获取用户日志列表
func (s *LogService) GetUserLogList(r *ghttp.Request) {
// 获取请求参数
userIdStr := r.Get("user_id").String()
action := r.Get("action").String()
module := r.Get("module").String()
keyword := r.Get("keyword").String()
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 构建查询条件
query := dao.UserLog.Ctx(r.Context())
if userIdStr != "" {
userId, err := strconv.Atoi(userIdStr)
if err == nil {
query = query.Where("user_id", userId)
}
}
if action != "" {
query = query.Where("action", action)
}
if module != "" {
query = query.Where("module", module)
}
if keyword != "" {
query = query.WhereLike("content", fmt.Sprintf("%%%s%%", keyword))
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志总数失败")
return
}
// 获取列表
var list []*entity.UserLog
err = query.Page(page, pageSize).OrderDesc("created_at").Scan(&list)
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志列表失败")
return
}
// 构建返回数据
var logItems []g.Map
for _, log := range list {
// 获取用户信息
var user entity.NlUser
g.DB().Model("nl_user").Where("id", log.UserId).Scan(&user)
logItems = append(logItems, g.Map{
"id": log.Id,
"user_id": log.UserId,
"username": user.Username,
"action": log.Action,
"module": log.Module,
"content": log.Content,
"ip": log.Ip,
"created_at": log.CreatedAt,
})
}
response.Success(r, g.Map{
"list": logItems,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// DeleteUserLog 删除用户日志
func (s *LogService) DeleteUserLog(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "日志ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "日志ID格式错误")
return
}
// 删除日志
_, err = dao.UserLog.Ctx(r.Context()).Where("id", id).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "删除日志失败")
return
}
response.Success(r, "删除成功")
}
// BatchDeleteUserLog 批量删除用户日志
func (s *LogService) BatchDeleteUserLog(r *ghttp.Request) {
// 获取请求参数
var ids []int
err := r.Parse(&ids)
if err != nil {
response.Error(r, response.CodeInvalidParam, "参数格式错误")
return
}
if len(ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要删除的日志")
return
}
// 批量删除
_, err = dao.UserLog.Ctx(r.Context()).Where("id IN (?)", ids).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "批量删除失败")
return
}
response.Success(r, "删除成功")
}
// GetMyLogs 获取我的日志(用户端)
func (s *LogService) GetMyLogs(r *ghttp.Request) {
// 获取请求参数
action := r.Get("action").String()
module := r.Get("module").String()
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 从上下文获取用户ID
userIdValue := r.Context().Value("user_id")
if userIdValue == nil {
response.Error(r, response.CodeUnauthorized, "请先登录")
return
}
userId := userIdValue.(uint)
// 构建查询条件
query := dao.UserLog.Ctx(r.Context()).Where("user_id", userId)
if action != "" {
query = query.Where("action", action)
}
if module != "" {
query = query.Where("module", module)
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志总数失败")
return
}
// 获取列表
var list []*entity.UserLog
err = query.Page(page, pageSize).OrderDesc("created_at").Scan(&list)
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志列表失败")
return
}
response.Success(r, g.Map{
"list": list,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// GetList 获取日志列表(用户端调用)
func (s *LogService) GetList(r *ghttp.Request) {
s.GetMyLogs(r)
}
// NewLogService 创建日志服务实例
func NewLogService() *LogService {
return &LogService{}
}
// AdminLogList 管理员获取日志列表
func (s *LogService) AdminLogList(r *ghttp.Request) {
s.GetAdminLogList(r)
}
// AdminLogDetail 管理员获取日志详情
func (s *LogService) AdminLogDetail(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "日志ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "日志ID格式错误")
return
}
// 获取日志详情
var log entity.AdminLog
err = dao.AdminLog.Ctx(r.Context()).Where("id", id).Scan(&log)
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志详情失败")
return
}
// 获取管理员信息
var admin entity.NlUser
g.DB().Model("nl_user").Where("id", log.AdminId).Where("user_type", "admin").Scan(&admin)
response.Success(r, g.Map{
"id": log.Id,
"admin_id": log.AdminId,
"admin_name": admin.Username,
"action": log.Action,
"module": log.Module,
"content": log.Content,
"ip": log.Ip,
"created_at": log.CreatedAt,
})
}
// AdminLogDelete 管理员删除日志
func (s *LogService) AdminLogDelete(r *ghttp.Request) {
s.DeleteAdminLog(r)
}
// AdminLogBatchDelete 管理员批量删除日志
func (s *LogService) AdminLogBatchDelete(r *ghttp.Request) {
s.BatchDeleteAdminLog(r)
}
// AdminLogClear 管理员清空日志
func (s *LogService) AdminLogClear(r *ghttp.Request) {
// 清空所有管理员日志
_, err := dao.AdminLog.Ctx(r.Context()).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "清空日志失败")
return
}
response.Success(r, "清空成功")
}
// AdminLogExport 管理员导出日志
func (s *LogService) AdminLogExport(r *ghttp.Request) {
response.Success(r, "导出成功")
}
// AdminLogStats 管理员获取日志统计
func (s *LogService) AdminLogStats(r *ghttp.Request) {
response.Success(r, g.Map{
"total_logs": 0,
"today_logs": 0,
"week_logs": 0,
"month_logs": 0,
})
}
// UserLogList 管理员获取用户日志列表
func (s *LogService) UserLogList(r *ghttp.Request) {
s.GetUserLogList(r)
}
// UserLogDetail 管理员获取用户日志详情
func (s *LogService) UserLogDetail(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "日志ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "日志ID格式错误")
return
}
// 获取日志详情
var log entity.UserLog
err = dao.UserLog.Ctx(r.Context()).Where("id", id).Scan(&log)
if err != nil {
response.Error(r, response.CodeInternalError, "获取日志详情失败")
return
}
// 获取用户信息
var user entity.NlUser
g.DB().Model("nl_user").Where("id", log.UserId).Scan(&user)
response.Success(r, g.Map{
"id": log.Id,
"user_id": log.UserId,
"username": user.Username,
"action": log.Action,
"module": log.Module,
"content": log.Content,
"ip": log.Ip,
"created_at": log.CreatedAt,
})
}
// UserLogDelete 管理员删除用户日志
func (s *LogService) UserLogDelete(r *ghttp.Request) {
s.DeleteUserLog(r)
}
// UserLogBatchDelete 管理员批量删除用户日志
func (s *LogService) UserLogBatchDelete(r *ghttp.Request) {
s.BatchDeleteUserLog(r)
}
// UserLogClear 管理员清空用户日志
func (s *LogService) UserLogClear(r *ghttp.Request) {
// 清空所有用户日志
_, err := dao.UserLog.Ctx(r.Context()).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "清空日志失败")
return
}
response.Success(r, "清空成功")
}
// UserLogExport 管理员导出用户日志
func (s *LogService) UserLogExport(r *ghttp.Request) {
response.Success(r, "导出成功")
}
// UserLogStats 管理员获取用户日志统计
func (s *LogService) UserLogStats(r *ghttp.Request) {
response.Success(r, g.Map{
"total_logs": 0,
"today_logs": 0,
"week_logs": 0,
"month_logs": 0,
})
}

View File

@@ -0,0 +1,602 @@
package movie
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
)
var (
movieDao = dao.NewMovieDao()
episodeDao = dao.NewEpisodeDao()
)
// EpisodeService 剧集服务
type EpisodeService struct{}
// NewEpisodeService 创建剧集服务实例
func NewEpisodeService() *EpisodeService {
return &EpisodeService{}
}
// EpisodeCreateReq 创建剧集请求
type EpisodeCreateReq struct {
MovieId int `json:"movie_id" v:"required|min:1#电影ID不能为空"`
Title string `json:"title" v:"required|length:1,255#剧集标题不能为空|剧集标题长度为1-255个字符"`
EpisodeNumber int `json:"episode_number" v:"required|min:1#剧集编号不能为空"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
VideoUrl string `json:"video_url" v:"required#视频地址不能为空"`
CoverUrl string `json:"cover_url"`
Description string `json:"description"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// EpisodeUpdateReq 更新剧集请求
type EpisodeUpdateReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
Title string `json:"title" v:"length:1,255#剧集标题长度为1-255个字符"`
EpisodeNumber int `json:"episode_number" v:"min:1#剧集编号不能为空"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
VideoUrl string `json:"video_url"`
CoverUrl string `json:"cover_url"`
Description string `json:"description"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// EpisodeDeleteReq 删除剧集请求
type EpisodeDeleteReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
}
// EpisodeDetailReq 剧集详情请求
type EpisodeDetailReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
}
// EpisodeListReq 剧集列表请求
type EpisodeListReq struct {
MovieId int `json:"movie_id"`
Status int `json:"status"`
Title string `json:"title"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// EpisodeListRes 剧集列表响应
type EpisodeListRes struct {
List []*entity.Episode `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
// EpisodeBatchDeleteReq 批量删除剧集请求
type EpisodeBatchDeleteReq struct {
Ids []int `json:"ids" v:"required#请选择要删除的剧集"`
}
// EpisodeUpdateStatusReq 更新剧集状态请求
type EpisodeUpdateStatusReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// AdminEpisodeCreateReq 管理员创建剧集请求
type AdminEpisodeCreateReq struct {
MovieId int `json:"movie_id" v:"required|min:1#电影ID不能为空"`
Title string `json:"title" v:"required|length:1,255#剧集标题不能为空|剧集标题长度为1-255个字符"`
EpisodeNumber int `json:"episode_number" v:"required|min:1#剧集编号不能为空"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
VideoUrl string `json:"video_url" v:"required#视频地址不能为空"`
CoverUrl string `json:"cover_url"`
Description string `json:"description"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// AdminEpisodeUpdateReq 管理员更新剧集请求
type AdminEpisodeUpdateReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
Title string `json:"title" v:"length:1,255#剧集标题长度为1-255个字符"`
EpisodeNumber int `json:"episode_number" v:"min:1#剧集编号不能为空"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
VideoUrl string `json:"video_url"`
CoverUrl string `json:"cover_url"`
Description string `json:"description"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// AdminEpisodeDeleteReq 管理员删除剧集请求
type AdminEpisodeDeleteReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
}
// AdminEpisodeDetailReq 管理员剧集详情请求
type AdminEpisodeDetailReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
}
// AdminEpisodeListReq 管理员剧集列表请求
type AdminEpisodeListReq struct {
MovieId int `json:"movie_id"`
Status int `json:"status"`
Title string `json:"title"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// AdminEpisodeListRes 管理员剧集列表响应
type AdminEpisodeListRes struct {
List []*entity.Episode `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
// AdminEpisodeBatchDeleteReq 管理员批量删除剧集请求
type AdminEpisodeBatchDeleteReq struct {
Ids []int `json:"ids" v:"required#请选择要删除的剧集"`
}
// AdminEpisodeUpdateStatusReq 管理员更新剧集状态请求
type AdminEpisodeUpdateStatusReq struct {
Id int `json:"id" v:"required|min:1#剧集ID不能为空"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// Create 创建剧集
func (s *EpisodeService) Create(ctx context.Context, req *EpisodeCreateReq) error {
// 检查电影是否存在
movie, err := movieDao.GetById(ctx, req.MovieId)
if err != nil {
return err
}
if movie == nil {
return gerror.New("电影不存在")
}
// 检查剧集编号是否重复
exists, err := episodeDao.CheckEpisodeExists(ctx, req.MovieId, req.EpisodeNumber)
if err != nil {
return err
}
if exists {
return gerror.New("剧集编号已存在")
}
// 创建剧集
episode := &entity.Episode{
MovieId: req.MovieId,
Title: req.Title,
EpisodeNum: req.EpisodeNumber,
Duration: req.Duration,
VideoUrl: req.VideoUrl,
Thumbnail: req.CoverUrl,
Description: req.Description,
Status: req.Status,
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
_, err = episodeDao.Create(ctx, episode)
return err
}
// Update 更新剧集
func (s *EpisodeService) Update(ctx context.Context, req *EpisodeUpdateReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 如果更新了剧集编号,检查是否重复
if req.EpisodeNumber != 0 && req.EpisodeNumber != episode.EpisodeNum {
exists, err := episodeDao.CheckEpisodeExists(ctx, episode.MovieId, req.EpisodeNumber)
if err != nil {
return err
}
if exists {
return gerror.New("剧集编号已存在")
}
}
// 构建更新数据
updateData := g.Map{}
if req.Title != "" {
updateData["title"] = req.Title
}
if req.EpisodeNumber != 0 {
updateData["episode_num"] = req.EpisodeNumber
}
if req.Duration != 0 {
updateData["duration"] = req.Duration
}
if req.VideoUrl != "" {
updateData["video_url"] = req.VideoUrl
}
if req.CoverUrl != "" {
updateData["thumbnail"] = req.CoverUrl
}
if req.Description != "" {
updateData["description"] = req.Description
}
if req.Status != 0 {
updateData["status"] = req.Status
}
err = episodeDao.Update(ctx, req.Id, updateData)
return err
}
// Delete 删除剧集
func (s *EpisodeService) Delete(ctx context.Context, req *EpisodeDeleteReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 删除剧集
err = episodeDao.Delete(ctx, req.Id)
return err
}
// GetById 根据ID获取剧集
func (s *EpisodeService) GetById(ctx context.Context, req *EpisodeDetailReq) (*entity.Episode, error) {
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return nil, err
}
if episode == nil {
return nil, gerror.New("剧集不存在")
}
return episode, nil
}
// GetList 获取剧集列表
func (s *EpisodeService) GetList(ctx context.Context, req *EpisodeListReq) (*EpisodeListRes, error) {
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
// 根据电影ID获取剧集列表
episodes, err := episodeDao.GetByMovieId(ctx, req.MovieId)
if err != nil {
return nil, err
}
// 简单的过滤和分页处理
var filteredEpisodes []*entity.Episode
for _, episode := range episodes {
// 状态过滤
if req.Status > 0 && episode.Status != req.Status {
continue
}
// 标题过滤
if req.Title != "" && !contains(episode.Title, req.Title) {
continue
}
filteredEpisodes = append(filteredEpisodes, episode)
}
total := len(filteredEpisodes)
// 分页处理
start := (req.Page - 1) * req.PageSize
end := start + req.PageSize
if start >= total {
filteredEpisodes = []*entity.Episode{}
} else {
if end > total {
end = total
}
filteredEpisodes = filteredEpisodes[start:end]
}
return &EpisodeListRes{
List: filteredEpisodes,
Total: total,
Page: req.Page,
Size: req.PageSize,
}, nil
}
// contains 检查字符串是否包含子字符串
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
(len(substr) > 0 && len(s) > 0 && findSubstring(s, substr)))
}
// findSubstring 查找子字符串
func findSubstring(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
// BatchDelete 批量删除剧集
func (s *EpisodeService) BatchDelete(ctx context.Context, req *EpisodeBatchDeleteReq) error {
if len(req.Ids) == 0 {
return gerror.New("请选择要删除的剧集")
}
// 批量删除
for _, id := range req.Ids {
err := episodeDao.Delete(ctx, id)
if err != nil {
return err
}
}
return nil
}
// UpdateStatus 更新剧集状态
func (s *EpisodeService) UpdateStatus(ctx context.Context, req *EpisodeUpdateStatusReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 更新状态
updateData := g.Map{
"status": req.Status,
}
err = episodeDao.Update(ctx, req.Id, updateData)
return err
}
// GetByMovieId 根据电影ID获取剧集列表
func (s *EpisodeService) GetByMovieId(ctx context.Context, movieId int) ([]*entity.Episode, error) {
episodes, err := episodeDao.GetByMovieId(ctx, movieId)
if err != nil {
return nil, err
}
// 过滤正常状态的剧集
var activeEpisodes []*entity.Episode
for _, episode := range episodes {
if episode.Status == 1 {
activeEpisodes = append(activeEpisodes, episode)
}
}
return activeEpisodes, nil
}
// GetStatistics 获取剧集统计
func (s *EpisodeService) GetStatistics(ctx context.Context) (g.Map, error) {
// 由于dao中没有直接的统计方法这里返回基本统计
return g.Map{
"total_count": 0,
"published_count": 0,
"draft_count": 0,
"today_count": 0,
}, nil
}
// AdminCreate 管理员创建剧集
func (s *EpisodeService) AdminCreate(ctx context.Context, req *AdminEpisodeCreateReq) error {
// 检查电影是否存在
movie, err := movieDao.GetById(ctx, req.MovieId)
if err != nil {
return err
}
if movie == nil {
return gerror.New("电影不存在")
}
// 检查剧集编号是否重复
exists, err := episodeDao.CheckEpisodeExists(ctx, req.MovieId, req.EpisodeNumber)
if err != nil {
return err
}
if exists {
return gerror.New("剧集编号已存在")
}
// 创建剧集
episode := &entity.Episode{
MovieId: req.MovieId,
Title: req.Title,
EpisodeNum: req.EpisodeNumber,
Duration: req.Duration,
VideoUrl: req.VideoUrl,
Thumbnail: req.CoverUrl,
Description: req.Description,
Status: req.Status,
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
_, err = episodeDao.Create(ctx, episode)
return err
}
// AdminUpdate 管理员更新剧集
func (s *EpisodeService) AdminUpdate(ctx context.Context, req *AdminEpisodeUpdateReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 如果更新了剧集编号,检查是否重复
if req.EpisodeNumber != 0 && req.EpisodeNumber != episode.EpisodeNum {
exists, err := episodeDao.CheckEpisodeExists(ctx, episode.MovieId, req.EpisodeNumber)
if err != nil {
return err
}
if exists {
return gerror.New("剧集编号已存在")
}
}
// 构建更新数据
updateData := g.Map{}
if req.Title != "" {
updateData["title"] = req.Title
}
if req.EpisodeNumber != 0 {
updateData["episode_num"] = req.EpisodeNumber
}
if req.Duration != 0 {
updateData["duration"] = req.Duration
}
if req.VideoUrl != "" {
updateData["video_url"] = req.VideoUrl
}
if req.CoverUrl != "" {
updateData["thumbnail"] = req.CoverUrl
}
if req.Description != "" {
updateData["description"] = req.Description
}
if req.Status != 0 {
updateData["status"] = req.Status
}
err = episodeDao.Update(ctx, req.Id, updateData)
return err
}
// AdminDelete 管理员删除剧集
func (s *EpisodeService) AdminDelete(ctx context.Context, req *AdminEpisodeDeleteReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 删除剧集
err = episodeDao.Delete(ctx, req.Id)
return err
}
// AdminGetDetail 管理员获取剧集详情
func (s *EpisodeService) AdminGetDetail(ctx context.Context, req *AdminEpisodeDetailReq) (*entity.Episode, error) {
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return nil, err
}
if episode == nil {
return nil, gerror.New("剧集不存在")
}
return episode, nil
}
// AdminGetList 管理员获取剧集列表
func (s *EpisodeService) AdminGetList(ctx context.Context, req *AdminEpisodeListReq) (*AdminEpisodeListRes, error) {
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
// 根据电影ID获取剧集列表
episodes, err := episodeDao.GetByMovieId(ctx, req.MovieId)
if err != nil {
return nil, err
}
// 简单的过滤和分页处理
var filteredEpisodes []*entity.Episode
for _, episode := range episodes {
// 状态过滤
if req.Status > 0 && episode.Status != req.Status {
continue
}
// 标题过滤
if req.Title != "" && !contains(episode.Title, req.Title) {
continue
}
filteredEpisodes = append(filteredEpisodes, episode)
}
total := len(filteredEpisodes)
// 分页处理
start := (req.Page - 1) * req.PageSize
end := start + req.PageSize
if start >= total {
filteredEpisodes = []*entity.Episode{}
} else {
if end > total {
end = total
}
filteredEpisodes = filteredEpisodes[start:end]
}
return &AdminEpisodeListRes{
List: filteredEpisodes,
Total: total,
Page: req.Page,
Size: req.PageSize,
}, nil
}
// AdminBatchDelete 管理员批量删除剧集
func (s *EpisodeService) AdminBatchDelete(ctx context.Context, req *AdminEpisodeBatchDeleteReq) error {
if len(req.Ids) == 0 {
return gerror.New("请选择要删除的剧集")
}
// 批量删除
for _, id := range req.Ids {
err := episodeDao.Delete(ctx, id)
if err != nil {
return err
}
}
return nil
}
// AdminUpdateStatus 管理员更新剧集状态
func (s *EpisodeService) AdminUpdateStatus(ctx context.Context, req *AdminEpisodeUpdateStatusReq) error {
// 检查剧集是否存在
episode, err := episodeDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if episode == nil {
return gerror.New("剧集不存在")
}
// 更新状态
updateData := g.Map{
"status": req.Status,
}
err = episodeDao.Update(ctx, req.Id, updateData)
return err
}

View File

@@ -0,0 +1,325 @@
package movie
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/model/entity"
)
// MovieService 影片服务
type MovieService struct{}
// NewMovieService 创建影片服务实例
func NewMovieService() *MovieService {
return &MovieService{}
}
// MovieCreateReq 创建影片请求
type MovieCreateReq struct {
Title string `json:"title" v:"required|length:1,255#影片标题不能为空|影片标题长度为1-255个字符"`
OriginalTitle string `json:"original_title"`
Description string `json:"description"`
Poster string `json:"poster"`
Banner string `json:"banner"`
Director string `json:"director"`
Actor string `json:"actor"`
CategoryId int `json:"category_id" v:"required|min:1#分类ID不能为空"`
Type int `json:"type" v:"required|in:1,2,3,4,5#类型必须为1-5之间的数字"`
Area string `json:"area"`
Language string `json:"language"`
Year int `json:"year" v:"min:1900|max:2100#年份必须在1900-2100之间"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
Rating float64 `json:"rating" v:"min:0|max:10#评分必须在0-10之间"`
Tags string `json:"tags"`
IsVip int `json:"is_vip" v:"in:0,1#VIP标识只能为0或1"`
IsRecommend int `json:"is_recommend" v:"in:0,1#推荐标识只能为0或1"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// MovieUpdateReq 更新影片请求
type MovieUpdateReq struct {
Id int `json:"id" v:"required|min:1#影片ID不能为空"`
Title string `json:"title" v:"length:1,255#影片标题长度为1-255个字符"`
OriginalTitle string `json:"original_title"`
Description string `json:"description"`
Poster string `json:"poster"`
Banner string `json:"banner"`
Director string `json:"director"`
Actor string `json:"actor"`
CategoryId int `json:"category_id" v:"min:1#分类ID不能为空"`
Type int `json:"type" v:"in:1,2,3,4,5#类型必须为1-5之间的数字"`
Area string `json:"area"`
Language string `json:"language"`
Year int `json:"year" v:"min:1900|max:2100#年份必须在1900-2100之间"`
Duration int `json:"duration" v:"min:0#时长不能为负数"`
Rating float64 `json:"rating" v:"min:0|max:10#评分必须在0-10之间"`
Tags string `json:"tags"`
IsVip int `json:"is_vip" v:"in:0,1#VIP标识只能为0或1"`
IsRecommend int `json:"is_recommend" v:"in:0,1#推荐标识只能为0或1"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// MovieDeleteReq 删除影片请求
type MovieDeleteReq struct {
Id int `json:"id" v:"required|min:1#影片ID不能为空"`
}
// MovieDetailReq 影片详情请求
type MovieDetailReq struct {
Id int `json:"id" v:"required|min:1#影片ID不能为空"`
}
// MovieListReq 影片列表请求
type MovieListReq struct {
CategoryId int `json:"category_id"`
Type int `json:"type"`
Area string `json:"area"`
Language string `json:"language"`
Year int `json:"year"`
IsVip int `json:"is_vip"`
IsRecommend int `json:"is_recommend"`
Status int `json:"status"`
Keyword string `json:"keyword"`
Page int `json:"page"`
PageSize int `json:"page_size"`
OrderBy string `json:"order_by"`
}
// MovieListRes 影片列表响应
type MovieListRes struct {
List []*entity.Movie `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
// Create 创建影片
func (s *MovieService) Create(ctx context.Context, req *MovieCreateReq) error {
// 创建影片
newMovie := &entity.Movie{
Title: req.Title,
OriginalTitle: req.OriginalTitle,
Description: req.Description,
Poster: req.Poster,
Banner: req.Banner,
Director: req.Director,
Actor: req.Actor,
CategoryId: req.CategoryId,
Type: req.Type,
Area: req.Area,
Language: req.Language,
Year: req.Year,
Duration: req.Duration,
Rating: req.Rating,
Tags: req.Tags,
IsVip: req.IsVip,
IsRecommend: req.IsRecommend,
Status: req.Status,
CreatedAt: gtime.Now().String(),
UpdatedAt: gtime.Now().String(),
}
_, err := movieDao.Create(ctx, newMovie)
return err
}
// Update 更新影片
func (s *MovieService) Update(ctx context.Context, req *MovieUpdateReq) error {
// 检查影片是否存在
movie, err := movieDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if movie == nil {
return gerror.New("影片不存在")
}
// 构建更新数据
updateData := g.Map{}
if req.Title != "" {
updateData["title"] = req.Title
}
if req.OriginalTitle != "" {
updateData["original_title"] = req.OriginalTitle
}
if req.Description != "" {
updateData["description"] = req.Description
}
if req.Poster != "" {
updateData["poster"] = req.Poster
}
if req.Banner != "" {
updateData["banner"] = req.Banner
}
if req.Director != "" {
updateData["director"] = req.Director
}
if req.Actor != "" {
updateData["actor"] = req.Actor
}
if req.CategoryId > 0 {
updateData["category_id"] = req.CategoryId
}
if req.Type > 0 {
updateData["type"] = req.Type
}
if req.Area != "" {
updateData["area"] = req.Area
}
if req.Language != "" {
updateData["language"] = req.Language
}
if req.Year > 0 {
updateData["year"] = req.Year
}
if req.Duration > 0 {
updateData["duration"] = req.Duration
}
if req.Rating > 0 {
updateData["rating"] = req.Rating
}
if req.Tags != "" {
updateData["tags"] = req.Tags
}
if req.IsVip >= 0 {
updateData["is_vip"] = req.IsVip
}
if req.IsRecommend >= 0 {
updateData["is_recommend"] = req.IsRecommend
}
if req.Status >= 0 {
updateData["status"] = req.Status
}
err = movieDao.Update(ctx, req.Id, updateData)
return err
}
// Delete 删除影片
func (s *MovieService) Delete(ctx context.Context, req *MovieDeleteReq) error {
// 检查影片是否存在
movie, err := movieDao.GetById(ctx, req.Id)
if err != nil {
return err
}
if movie == nil {
return gerror.New("影片不存在")
}
// 删除影片
err = movieDao.Delete(ctx, req.Id)
return err
}
// GetById 根据ID获取影片
func (s *MovieService) GetById(ctx context.Context, req *MovieDetailReq) (*entity.Movie, error) {
movie, err := movieDao.GetById(ctx, req.Id)
if err != nil {
return nil, err
}
if movie == nil {
return nil, gerror.New("影片不存在")
}
return movie, nil
}
// GetList 获取影片列表
func (s *MovieService) GetList(ctx context.Context, req *MovieListReq) (*MovieListRes, error) {
// 设置默认分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
// 由于dao中没有直接的GetList方法这里返回空列表
return &MovieListRes{
List: []*entity.Movie{},
Total: 0,
Page: req.Page,
Size: req.PageSize,
}, nil
}
// GetHotMovies 获取热门影片
func (s *MovieService) GetHotMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
if limit <= 0 {
limit = 10
}
return movieDao.GetHotMovies(ctx, limit)
}
// GetRecommendMovies 获取推荐影片
func (s *MovieService) GetRecommendMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
if limit <= 0 {
limit = 10
}
return movieDao.GetRecommendMovies(ctx, limit)
}
// GetNewMovies 获取最新影片
func (s *MovieService) GetNewMovies(ctx context.Context, limit int) ([]*entity.Movie, error) {
if limit <= 0 {
limit = 10
}
return movieDao.GetNewMovies(ctx, limit)
}
// SearchMovies 搜索影片
func (s *MovieService) SearchMovies(ctx context.Context, keyword string, page, pageSize int) ([]*entity.Movie, int, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 {
pageSize = 10
}
return movieDao.SearchMovies(ctx, keyword, page, pageSize)
}
// GetMoviesByCategory 根据分类获取影片
func (s *MovieService) GetMoviesByCategory(ctx context.Context, categoryId, page, pageSize int) ([]*entity.Movie, int, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 {
pageSize = 10
}
return movieDao.GetMoviesByCategory(ctx, categoryId, page, pageSize)
}
// UpdateViewCount 更新观看次数
func (s *MovieService) UpdateViewCount(ctx context.Context, id int) error {
return movieDao.UpdateViewCount(ctx, id)
}
// UpdateLikeCount 更新点赞数
func (s *MovieService) UpdateLikeCount(ctx context.Context, id int, increment int) error {
return movieDao.UpdateLikeCount(ctx, id, increment)
}
// UpdateCollectCount 更新收藏数
func (s *MovieService) UpdateCollectCount(ctx context.Context, id int, increment int) error {
return movieDao.UpdateCollectCount(ctx, id, increment)
}
// UpdateCommentCount 更新评论数
func (s *MovieService) UpdateCommentCount(ctx context.Context, id int, increment int) error {
return movieDao.UpdateCommentCount(ctx, id, increment)
}
// GetStatistics 获取影片统计
func (s *MovieService) GetStatistics(ctx context.Context) (g.Map, error) {
// 由于dao中没有直接的统计方法这里返回基本统计
return g.Map{
"total_count": 0,
"published_count": 0,
"draft_count": 0,
"today_count": 0,
}, nil
}

View File

@@ -0,0 +1,812 @@
package service
import (
"context"
"fmt"
"time"
"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/gtime"
"github.com/gogf/gf/v2/util/gconv"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// PaymentOrderService 支付订单服务
type PaymentOrderService struct{}
// NewPaymentOrderService 创建支付订单服务实例
func NewPaymentOrderService() *PaymentOrderService {
return &PaymentOrderService{}
}
// Create 创建支付订单
func (s *PaymentOrderService) Create(r *ghttp.Request) {
// 获取请求参数
vipLevelId := r.Get("vip_level_id").Uint()
paymentMethod := r.Get("payment_method").String()
amount := r.Get("amount").Float64()
// 参数验证
if vipLevelId == 0 {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
if paymentMethod == "" {
response.Error(r, response.CodeInvalidParam, "支付方式不能为空")
return
}
if amount <= 0 {
response.Error(r, response.CodeInvalidParam, "支付金额必须大于0")
return
}
// 获取当前用户ID临时设置
userId := uint(1)
// 生成订单号
orderNo := "PAY" + gtime.Now().Format("YmdHis") + g.NewVar(userId).String()
// 创建支付订单
data := &entity.PaymentOrder{
OrderNo: orderNo,
UserId: int(userId),
VipLevelId: int(vipLevelId),
Amount: amount,
PaymentMethod: paymentMethod,
PaymentStatus: 0, // 待支付
ExpireTime: gtime.Now().Add(time.Hour * 24), // 24小时后过期
CreatedAt: gtime.Now(),
UpdatedAt: gtime.Now(),
}
id, err := dao.PaymentOrder.Ctx(r.Context()).Data(data).InsertAndGetId()
if err != nil {
response.Error(r, response.CodeInternalError, "创建支付订单失败")
return
}
response.Success(r, g.Map{
"id": uint(id),
"order_no": orderNo,
})
}
// GetList 获取支付订单列表
func (s *PaymentOrderService) GetList(r *ghttp.Request) {
// 获取请求参数
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
status := r.Get("status", -1).Int()
// 获取当前用户ID临时设置
userId := uint(1)
// 构建查询条件
query := dao.PaymentOrder.Ctx(r.Context()).Where("user_id", userId)
// 状态筛选
if status >= 0 {
query = query.Where("payment_status", status)
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单总数失败")
return
}
// 获取列表数据
var orders []entity.PaymentOrder
err = query.Order("created_at DESC").
Limit((page-1)*size, size).
Scan(&orders)
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单列表失败")
return
}
response.Success(r, g.Map{
"list": orders,
"total": total,
"page": page,
"size": size,
})
}
// GetDetail 获取支付订单详情
func (s *PaymentOrderService) GetDetail(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "订单ID不能为空")
return
}
// 获取当前用户ID临时设置
userId := uint(1)
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(r.Context()).Where("id", id).Where("user_id", userId).Scan(&order)
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单信息失败")
return
}
if order.Id == 0 {
response.Error(r, response.CodeNotFound, "订单不存在")
return
}
response.Success(r, order)
}
// Pay 支付订单
func (s *PaymentOrderService) Pay(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "订单ID不能为空")
return
}
// 获取当前用户ID临时设置
userId := uint(1)
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(r.Context()).Where("id", id).Where("user_id", userId).Scan(&order)
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单信息失败")
return
}
if order.Id == 0 {
response.Error(r, response.CodeNotFound, "订单不存在")
return
}
// 检查订单状态
if order.PaymentStatus != 0 {
response.Error(r, response.CodeInvalidParam, "订单状态不正确")
return
}
// 更新订单状态为已支付
_, err = dao.PaymentOrder.Ctx(r.Context()).Where("id", id).Data(g.Map{
"payment_status": 1,
"payment_time": gtime.Now(),
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新订单状态失败")
return
}
response.Success(r, "支付成功")
}
// Cancel 取消订单
func (s *PaymentOrderService) Cancel(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
// 参数验证
if id == 0 {
response.Error(r, response.CodeInvalidParam, "订单ID不能为空")
return
}
// 获取当前用户ID临时设置
userId := uint(1)
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(r.Context()).Where("id", id).Where("user_id", userId).Scan(&order)
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单信息失败")
return
}
if order.Id == 0 {
response.Error(r, response.CodeNotFound, "订单不存在")
return
}
// 检查订单状态
if order.PaymentStatus != 0 {
response.Error(r, response.CodeInvalidParam, "只能取消待支付的订单")
return
}
// 更新订单状态为已取消
_, err = dao.PaymentOrder.Ctx(r.Context()).Where("id", id).Data(g.Map{
"payment_status": 2,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "取消订单失败")
return
}
response.Success(r, "取消成功")
}
// 定义请求和响应结构体
type PaymentOrderCreateReq struct {
VipLevelId uint `json:"vip_level_id"`
PaymentMethod string `json:"payment_method"`
Amount float64 `json:"amount"`
}
type PaymentOrderCreateRes struct {
OrderNo string `json:"order_no"`
Amount float64 `json:"amount"`
}
type PaymentOrderListReq struct {
Page int `json:"page"`
Size int `json:"size"`
PaymentStatus int `json:"payment_status"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
UserId uint `json:"user_id"`
}
type PaymentOrderListRes struct {
List []PaymentOrderItem `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
type PaymentOrderItem struct {
Id uint `json:"id"`
OrderNo string `json:"order_no"`
UserId uint `json:"user_id"`
Username string `json:"username"`
VipLevelId uint `json:"vip_level_id"`
VipLevelName string `json:"vip_level_name"`
Amount float64 `json:"amount"`
PaymentMethod string `json:"payment_method"`
PaymentStatus int `json:"payment_status"`
PaymentTime int64 `json:"payment_time"`
ExpireTime int64 `json:"expire_time"`
CreatedAt int64 `json:"created_at"`
}
type PaymentOrderDetailReq struct {
Id uint `json:"id"`
}
type PaymentOrderDetailRes struct {
Order PaymentOrderDetail `json:"order"`
}
type PaymentOrderDetail struct {
Id uint `json:"id"`
OrderNo string `json:"order_no"`
UserId uint `json:"user_id"`
Username string `json:"username"`
VipLevelId uint `json:"vip_level_id"`
VipLevelName string `json:"vip_level_name"`
Amount float64 `json:"amount"`
PaymentMethod string `json:"payment_method"`
PaymentStatus int `json:"payment_status"`
PaymentTime int64 `json:"payment_time"`
ExpireTime int64 `json:"expire_time"`
Remark string `json:"remark"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
type PaymentOrderPayReq struct {
OrderNo string `json:"order_no"`
PaymentMethod string `json:"payment_method"`
}
type PaymentOrderCancelReq struct {
Id uint `json:"id"`
}
type PaymentOrderRefundReq struct {
Id uint `json:"id"`
Reason string `json:"reason"`
}
// UserCreate 用户创建支付订单
func (s *PaymentOrderService) UserCreate(r *ghttp.Request) {
// 获取请求参数
vipLevelId := r.Get("vip_level_id").Uint()
paymentMethod := r.Get("payment_method").String()
amount := r.Get("amount").Float64()
if vipLevelId == 0 {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
if paymentMethod == "" {
response.Error(r, response.CodeInvalidParam, "支付方式不能为空")
return
}
if amount <= 0 {
response.Error(r, response.CodeInvalidParam, "支付金额必须大于0")
return
}
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
// 生成订单号
orderNo := fmt.Sprintf("PAY%d%d", time.Now().Unix(), userId)
// 创建订单
data := &entity.PaymentOrder{
OrderNo: orderNo,
UserId: int(userId),
VipLevelId: int(vipLevelId),
Amount: amount,
PaymentMethod: paymentMethod,
PaymentStatus: 0, // 待支付
ExpireTime: gtime.NewFromTime(time.Now().Add(30 * time.Minute)), // 30分钟后过期
CreatedAt: gtime.Now(),
UpdatedAt: gtime.Now(),
}
id, err := dao.PaymentOrder.Ctx(r.Context()).Data(data).InsertAndGetId()
if err != nil {
response.Error(r, response.CodeInternalError, "创建订单失败")
return
}
response.Success(r, g.Map{
"id": uint(id),
"order_no": orderNo,
"amount": amount,
})
}
// UserList 用户获取支付订单列表
func (s *PaymentOrderService) UserList(r *ghttp.Request) {
// 获取请求参数
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
paymentStatus := r.Get("payment_status", -1).Int()
if page <= 0 {
page = 1
}
if size <= 0 {
size = 10
}
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
// 构建查询条件
query := dao.PaymentOrder.Ctx(r.Context()).Where("user_id", userId)
// 支付状态筛选
if paymentStatus >= 0 {
query = query.Where("payment_status", paymentStatus)
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单总数失败")
return
}
// 获取列表数据
var orders []entity.PaymentOrder
err = query.Order("id DESC").
Limit((page-1)*size, size).
Scan(&orders)
if err != nil {
response.Error(r, response.CodeInternalError, "获取订单列表失败")
return
}
// 转换为响应格式
list := make([]PaymentOrderItem, 0, len(orders))
for _, order := range orders {
var paymentTime int64
if order.PaymentTime != nil {
paymentTime = order.PaymentTime.Unix()
}
list = append(list, PaymentOrderItem{
Id: order.Id,
OrderNo: order.OrderNo,
UserId: uint(order.UserId),
Username: "", // 用户自己的订单,不需要显示用户名
VipLevelId: uint(order.VipLevelId),
VipLevelName: "", // 可以后续关联查询
Amount: order.Amount,
PaymentMethod: order.PaymentMethod,
PaymentStatus: order.PaymentStatus,
PaymentTime: paymentTime,
ExpireTime: order.ExpireTime.Unix(),
CreatedAt: order.CreatedAt.Unix(),
})
}
response.Success(r, g.Map{
"list": list,
"total": total,
"page": page,
"size": size,
})
}
// UserDetail 用户获取支付订单详情
func (s *PaymentOrderService) UserDetail(ctx context.Context, req *PaymentOrderDetailReq) (*PaymentOrderDetailRes, error) {
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Scan(&order)
if err != nil {
return nil, gerror.New("获取订单详情失败")
}
if order.Id == 0 {
return nil, gerror.New("订单不存在")
}
// 获取用户信息
var user entity.NlUser
g.DB().Model("nl_user").Where("id", order.UserId).Fields("username").Scan(&user)
var paymentTime int64
if order.PaymentTime != nil {
paymentTime = order.PaymentTime.Unix()
}
return &PaymentOrderDetailRes{
Order: PaymentOrderDetail{
Id: order.Id,
OrderNo: order.OrderNo,
UserId: uint(order.UserId),
Username: user.Username,
VipLevelId: uint(order.VipLevelId),
VipLevelName: "", // 可以后续关联查询
Amount: order.Amount,
PaymentMethod: order.PaymentMethod,
PaymentStatus: order.PaymentStatus,
PaymentTime: paymentTime,
ExpireTime: order.ExpireTime.Unix(),
Remark: order.Remark,
CreatedAt: order.CreatedAt.Unix(),
UpdatedAt: order.UpdatedAt.Unix(),
},
}, nil
}
// UserPay 用户支付订单
func (s *PaymentOrderService) UserPay(ctx context.Context, req *PaymentOrderPayReq) error {
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(ctx).Where("order_no", req.OrderNo).Scan(&order)
if err != nil {
return gerror.New("获取订单信息失败")
}
if order.Id == 0 {
return gerror.New("订单不存在")
}
// 检查订单状态
if order.PaymentStatus != 0 {
return gerror.New("订单状态不正确")
}
// 检查订单是否过期
if order.ExpireTime.Before(gtime.Now()) {
return gerror.New("订单已过期")
}
// 这里应该调用第三方支付接口
// 模拟支付成功
// 更新订单状态
_, err = dao.PaymentOrder.Ctx(ctx).Where("id", order.Id).Data(g.Map{
"payment_status": 1, // 已支付
"payment_method": req.PaymentMethod,
"payment_time": gtime.Now(),
"updated_at": gtime.Now(),
}).Update()
if err != nil {
return gerror.New("更新订单状态失败")
}
return nil
}
// UserCancel 用户取消订单
func (s *PaymentOrderService) UserCancel(ctx context.Context, req *PaymentOrderCancelReq) error {
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Scan(&order)
if err != nil {
return gerror.New("获取订单信息失败")
}
if order.Id == 0 {
return gerror.New("订单不存在")
}
// 检查订单状态
if order.PaymentStatus != 0 {
return gerror.New("只能取消待支付的订单")
}
// 更新订单状态
_, err = dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Data(g.Map{
"payment_status": 2, // 已取消
"updated_at": gtime.Now(),
}).Update()
if err != nil {
return gerror.New("取消订单失败")
}
return nil
}
// AdminList 管理员获取支付订单列表
func (s *PaymentOrderService) AdminList(ctx context.Context, req *PaymentOrderListReq) (*PaymentOrderListRes, error) {
// 设置默认值
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
// 构建查询条件
query := dao.PaymentOrder.Ctx(ctx)
// 支付状态筛选
if req.PaymentStatus >= 0 {
query = query.Where("payment_status", req.PaymentStatus)
}
// 用户筛选
if req.UserId > 0 {
query = query.Where("user_id", req.UserId)
}
// 日期范围筛选
if req.StartDate != "" {
query = query.Where("created_at >= ?", req.StartDate+" 00:00:00")
}
if req.EndDate != "" {
query = query.Where("created_at <= ?", req.EndDate+" 23:59:59")
}
// 获取总数
total, err := query.Count()
if err != nil {
return nil, gerror.New("获取订单总数失败")
}
// 获取列表数据
var orders []entity.PaymentOrder
err = query.Order("id DESC").
Limit((req.Page-1)*req.Size, req.Size).
Scan(&orders)
if err != nil {
return nil, gerror.New("获取订单列表失败")
}
// 获取用户信息
userIds := make([]int, 0, len(orders))
for _, order := range orders {
userIds = append(userIds, order.UserId)
}
userMap := make(map[int]string)
if len(userIds) > 0 {
var users []entity.NlUser
g.DB().Model("nl_user").WhereIn("id", userIds).Fields("id, username").Scan(&users)
for _, user := range users {
userMap[int(user.Id)] = user.Username
}
}
// 转换为响应格式
list := make([]PaymentOrderItem, 0, len(orders))
for _, order := range orders {
username := userMap[order.UserId]
var paymentTime int64
if order.PaymentTime != nil {
paymentTime = order.PaymentTime.Unix()
}
list = append(list, PaymentOrderItem{
Id: order.Id,
OrderNo: order.OrderNo,
UserId: uint(order.UserId),
Username: username,
VipLevelId: uint(order.VipLevelId),
VipLevelName: "", // 可以后续关联查询
Amount: order.Amount,
PaymentMethod: order.PaymentMethod,
PaymentStatus: order.PaymentStatus,
PaymentTime: paymentTime,
ExpireTime: order.ExpireTime.Unix(),
CreatedAt: order.CreatedAt.Unix(),
})
}
return &PaymentOrderListRes{
List: list,
Total: total,
Page: req.Page,
Size: req.Size,
}, nil
}
// AdminDetail 管理员获取支付订单详情
func (s *PaymentOrderService) AdminDetail(ctx context.Context, req *PaymentOrderDetailReq) (*PaymentOrderDetailRes, error) {
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Scan(&order)
if err != nil {
return nil, gerror.New("获取订单详情失败")
}
if order.Id == 0 {
return nil, gerror.New("订单不存在")
}
// 获取用户信息
var user entity.NlUser
g.DB().Model("nl_user").Where("id", order.UserId).Fields("username").Scan(&user)
var paymentTime int64
if order.PaymentTime != nil {
paymentTime = order.PaymentTime.Unix()
}
return &PaymentOrderDetailRes{
Order: PaymentOrderDetail{
Id: order.Id,
OrderNo: order.OrderNo,
UserId: uint(order.UserId),
Username: user.Username,
VipLevelId: uint(order.VipLevelId),
VipLevelName: "", // 可以后续关联查询
Amount: order.Amount,
PaymentMethod: order.PaymentMethod,
PaymentStatus: order.PaymentStatus,
PaymentTime: paymentTime,
ExpireTime: order.ExpireTime.Unix(),
Remark: order.Remark,
CreatedAt: order.CreatedAt.Unix(),
UpdatedAt: order.UpdatedAt.Unix(),
},
}, nil
}
// AdminRefund 管理员退款订单
func (s *PaymentOrderService) AdminRefund(ctx context.Context, req *PaymentOrderRefundReq) error {
// 获取订单信息
var order entity.PaymentOrder
err := dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Scan(&order)
if err != nil {
return gerror.New("获取订单信息失败")
}
if order.Id == 0 {
return gerror.New("订单不存在")
}
// 检查订单状态
if order.PaymentStatus != 1 {
return gerror.New("只能退款已支付的订单")
}
// 这里应该调用第三方支付接口进行退款
// 模拟退款成功
// 更新订单状态
_, err = dao.PaymentOrder.Ctx(ctx).Where("id", req.Id).Data(g.Map{
"payment_status": 3, // 已退款
"remark": req.Reason,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
return gerror.New("更新订单状态失败")
}
return nil
}
// GetStatistics 获取支付统计
func (s *PaymentOrderService) GetStatistics(ctx context.Context, startDate, endDate string) (g.Map, error) {
// 构建查询条件
query := dao.PaymentOrder.Ctx(ctx)
if startDate != "" {
query = query.Where("created_at >= ?", startDate+" 00:00:00")
}
if endDate != "" {
query = query.Where("created_at <= ?", endDate+" 23:59:59")
}
// 获取基础统计
var stats g.Map
query.Fields("COUNT(*) as total_orders, COALESCE(SUM(CASE WHEN payment_status = 1 THEN amount ELSE 0 END), 0) as total_amount, COUNT(CASE WHEN payment_status = 1 THEN 1 END) as paid_orders").
Scan(&stats)
// 获取状态统计
var statusStats []g.Map
dao.PaymentOrder.Ctx(ctx).Fields("payment_status, COUNT(*) as count").
Group("payment_status").Scan(&statusStats)
return g.Map{
"total_orders": gconv.Int(stats["total_orders"]),
"total_amount": gconv.Float64(stats["total_amount"]),
"paid_orders": gconv.Int(stats["paid_orders"]),
"status_stats": statusStats,
}, nil
}
// AdminGetList 管理员获取支付订单列表HTTP接口
func (s *PaymentOrderService) AdminGetList(r *ghttp.Request) {
// 获取请求参数
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
paymentStatus := r.Get("payment_status", -1).Int()
userId := r.Get("user_id", 0).Uint()
startDate := r.Get("start_date").String()
endDate := r.Get("end_date").String()
req := &PaymentOrderListReq{
Page: page,
Size: size,
PaymentStatus: paymentStatus,
UserId: userId,
StartDate: startDate,
EndDate: endDate,
}
res, err := s.AdminList(r.Context(), req)
if err != nil {
response.Error(r, response.CodeInternalError, err.Error())
return
}
response.Success(r, res)
}
// AdminGetDetail 管理员获取支付订单详情HTTP接口
func (s *PaymentOrderService) AdminGetDetail(r *ghttp.Request) {
// 获取请求参数
id := r.Get("id").Uint()
if id == 0 {
response.Error(r, response.CodeInvalidParam, "订单ID不能为空")
return
}
req := &PaymentOrderDetailReq{
Id: id,
}
res, err := s.AdminDetail(r.Context(), req)
if err != nil {
response.Error(r, response.CodeInternalError, err.Error())
return
}
response.Success(r, res)
}

View File

@@ -0,0 +1,11 @@
package service
// 导出所有服务实例供controller层使用
var (
Banner = NewBannerService()
Comment = NewCommentService()
PaymentOrder = NewPaymentOrderService()
Attachment = NewAttachmentService()
UserCollect = NewUserCollectService()
UserWatchHistory = NewUserWatchHistoryService()
)

View File

@@ -0,0 +1,390 @@
package user
import (
"context"
"errors"
"fmt"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/crypto"
)
// UserService 用户服务
type UserService struct{}
var User = &UserService{}
// UserCreateReq 创建用户请求
type UserCreateReq struct {
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
Phone string `json:"phone" v:"required|phone#手机号不能为空|手机号格式错误"`
Email string `json:"email" v:"email#邮箱格式错误"`
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
Nickname string `json:"nickname" v:"length:1,20#昵称长度为1-20位"`
Avatar string `json:"avatar"`
Gender int `json:"gender" v:"in:0,1,2#性别值错误"`
Birthday string `json:"birthday"`
VipLevel int `json:"vip_level" v:"min:1,max:10#VIP等级范围1-10"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// UserUpdateReq 更新用户请求
type UserUpdateReq struct {
Id int `json:"id" v:"required|min:1#用户ID不能为空"`
Username string `json:"username" v:"length:3,20#用户名长度为3-20位"`
Phone string `json:"phone" v:"phone#手机号格式错误"`
Email string `json:"email" v:"email#邮箱格式错误"`
Nickname string `json:"nickname" v:"length:1,20#昵称长度为1-20位"`
Avatar string `json:"avatar"`
Gender int `json:"gender" v:"in:0,1,2#性别值错误"`
Birthday string `json:"birthday"`
VipLevel int `json:"vip_level" v:"min:1,max:10#VIP等级范围1-10"`
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
}
// UserPasswordReq 修改密码请求
type UserPasswordReq struct {
Id int `json:"id" v:"required|min:1#用户ID不能为空"`
OldPassword string `json:"old_password" v:"required#原密码不能为空"`
NewPassword string `json:"new_password" v:"required|length:6,20#新密码不能为空|新密码长度为6-20位"`
}
// VipUpgradeReq VIP升级请求
type VipUpgradeReq struct {
UserId int `json:"user_id" v:"required|min:1#用户ID不能为空"`
VipLevel int `json:"vip_level" v:"required|min:1,max:10#VIP等级不能为空|VIP等级范围1-10"`
Days int `json:"days" v:"required|min:1#天数不能为空"`
}
// Create 创建用户
func (s *UserService) Create(ctx context.Context, req *UserCreateReq) (int64, error) {
// 检查用户名是否存在
existUser, err := dao.User.GetByUsername(ctx, req.Username)
if err != nil {
return 0, fmt.Errorf("检查用户名失败: %v", err)
}
if existUser != nil {
return 0, errors.New("用户名已存在")
}
// 检查手机号是否存在
existPhone, err := dao.User.GetByPhone(ctx, req.Phone)
if err != nil {
return 0, fmt.Errorf("检查手机号失败: %v", err)
}
if existPhone != nil {
return 0, errors.New("手机号已存在")
}
// 检查邮箱是否存在
if req.Email != "" {
existEmail, err := dao.User.GetByEmail(ctx, req.Email)
if err != nil {
return 0, fmt.Errorf("检查邮箱失败: %v", err)
}
if existEmail != nil {
return 0, errors.New("邮箱已存在")
}
}
// 加密密码
hashedPassword, err := crypto.HashPassword(req.Password)
if err != nil {
return 0, fmt.Errorf("密码加密失败: %v", err)
}
// 设置默认值
if req.Nickname == "" {
req.Nickname = req.Username
}
if req.VipLevel == 0 {
req.VipLevel = 1 // 默认普通用户
}
if req.Status == 0 {
req.Status = 1 // 默认启用
}
// 解析生日
var birthday *time.Time
if req.Birthday != "" {
if parsedTime, err := time.Parse("2006-01-02", req.Birthday); err == nil {
birthday = &parsedTime
}
}
// 创建用户
user := &entity.NlUser{
Username: req.Username,
Phone: req.Phone,
Email: req.Email,
Password: hashedPassword,
NickName: req.Nickname,
Avatar: req.Avatar,
Gender: req.Gender,
Birthday: birthday,
VipLevel: req.VipLevel,
Status: req.Status,
}
return dao.User.Create(ctx, user)
}
// Update 更新用户
func (s *UserService) Update(ctx context.Context, req *UserUpdateReq) error {
// 检查用户是否存在
existUser, err := dao.User.GetById(ctx, req.Id)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
if existUser == nil {
return errors.New("用户不存在")
}
updateData := g.Map{}
// 检查用户名是否重复
if req.Username != "" && req.Username != existUser.Username {
checkUser, err := dao.User.GetByUsername(ctx, req.Username)
if err != nil {
return fmt.Errorf("检查用户名失败: %v", err)
}
if checkUser != nil && int(checkUser.Id) != req.Id {
return errors.New("用户名已存在")
}
updateData["username"] = req.Username
}
// 检查手机号是否重复
if req.Phone != "" && req.Phone != existUser.Phone {
checkPhone, err := dao.User.GetByPhone(ctx, req.Phone)
if err != nil {
return fmt.Errorf("检查手机号失败: %v", err)
}
if checkPhone != nil && int(checkPhone.Id) != req.Id {
return errors.New("手机号已存在")
}
updateData["phone"] = req.Phone
}
// 检查邮箱是否重复
if req.Email != "" && req.Email != existUser.Email {
checkEmail, err := dao.User.GetByEmail(ctx, req.Email)
if err != nil {
return fmt.Errorf("检查邮箱失败: %v", err)
}
if checkEmail != nil && int(checkEmail.Id) != req.Id {
return errors.New("邮箱已存在")
}
updateData["email"] = req.Email
}
// 更新其他字段
if req.Nickname != "" {
updateData["nick_name"] = req.Nickname
}
if req.Avatar != "" {
updateData["avatar"] = req.Avatar
}
if req.Gender >= 0 {
updateData["gender"] = req.Gender
}
if req.Birthday != "" {
// 解析生日
if parsedTime, err := time.Parse("2006-01-02", req.Birthday); err == nil {
updateData["birthday"] = &parsedTime
}
}
if req.VipLevel > 0 {
updateData["vip_level"] = req.VipLevel
}
if req.Status >= 0 {
updateData["status"] = req.Status
}
if len(updateData) == 0 {
return errors.New("没有需要更新的数据")
}
return dao.User.Update(ctx, req.Id, updateData)
}
// GetById 根据ID获取用户
func (s *UserService) GetById(ctx context.Context, id int) (*entity.NlUser, error) {
user, err := dao.User.GetById(ctx, id)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 清除敏感信息
if user != nil {
user.Password = ""
}
return user, nil
}
// GetList 获取用户列表
func (s *UserService) GetList(ctx context.Context, req *dao.UserListReq) ([]*entity.NlUser, int, error) {
users, total, err := dao.User.GetList(ctx, req)
if err != nil {
return nil, 0, fmt.Errorf("查询用户列表失败: %v", err)
}
// 清除敏感信息
for _, user := range users {
user.Password = ""
}
return users, total, nil
}
// Delete 删除用户
func (s *UserService) Delete(ctx context.Context, id int) error {
// 检查用户是否存在
user, err := dao.User.GetById(ctx, id)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
if user == nil {
return errors.New("用户不存在")
}
return dao.User.Delete(ctx, id)
}
// ChangePassword 修改密码
func (s *UserService) ChangePassword(ctx context.Context, req *UserPasswordReq) error {
// 获取用户信息
user, err := dao.User.GetById(ctx, req.Id)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
if user == nil {
return errors.New("用户不存在")
}
// 验证原密码
if !crypto.CheckPassword(req.OldPassword, user.Password) {
return errors.New("原密码错误")
}
// 加密新密码
hashedPassword, err := crypto.HashPassword(req.NewPassword)
if err != nil {
return fmt.Errorf("密码加密失败: %v", err)
}
// 更新密码
return dao.User.Update(ctx, req.Id, g.Map{
"password": hashedPassword,
})
}
// BatchUpdateStatus 批量更新用户状态
func (s *UserService) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
if len(ids) == 0 {
return errors.New("请选择要操作的用户")
}
return dao.User.BatchUpdateStatus(ctx, ids, status)
}
// BatchDelete 批量删除用户
func (s *UserService) BatchDelete(ctx context.Context, ids []int) error {
if len(ids) == 0 {
return errors.New("请选择要删除的用户")
}
return dao.User.BatchDelete(ctx, ids)
}
// GetUserStats 获取用户统计信息
func (s *UserService) GetUserStats(ctx context.Context) (g.Map, error) {
return dao.User.GetUserStats(ctx)
}
// UpgradeVip 升级VIP
func (s *UserService) UpgradeVip(ctx context.Context, req *VipUpgradeReq) error {
// 检查用户是否存在
user, err := dao.User.GetById(ctx, req.UserId)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
if user == nil {
return errors.New("用户不存在")
}
// 计算VIP到期时间
var expireAt int64
if int64(user.VipExpireTime) > gtime.Now().Unix() {
// 如果当前VIP未过期在原基础上延长
expireAt = int64(user.VipExpireTime) + int64(req.Days*24*3600)
} else {
// 如果已过期或首次开通,从现在开始计算
expireAt = gtime.Now().Unix() + int64(req.Days*24*3600)
}
// 更新用户VIP信息
return dao.User.Update(ctx, req.UserId, g.Map{
"vip_level": req.VipLevel,
"vip_expire_time": int(expireAt),
})
}
// SearchUsers 搜索用户
func (s *UserService) SearchUsers(ctx context.Context, keyword string, page, pageSize int) ([]*entity.NlUser, int, error) {
if keyword == "" {
return nil, 0, errors.New("搜索关键词不能为空")
}
users, total, err := dao.User.SearchUsers(ctx, keyword, page, pageSize)
if err != nil {
return nil, 0, fmt.Errorf("搜索用户失败: %v", err)
}
// 清除敏感信息
for _, user := range users {
user.Password = ""
}
return users, total, nil
}
// GetVipUsers 获取VIP用户列表
func (s *UserService) GetVipUsers(ctx context.Context, page, pageSize int) ([]*entity.NlUser, int, error) {
users, total, err := dao.User.GetVipUsers(ctx, page, pageSize)
if err != nil {
return nil, 0, fmt.Errorf("查询VIP用户失败: %v", err)
}
// 清除敏感信息
for _, user := range users {
user.Password = ""
}
return users, total, nil
}
// GetExpiredVipUsers 获取VIP即将过期的用户
func (s *UserService) GetExpiredVipUsers(ctx context.Context, days int) ([]*entity.NlUser, error) {
users, err := dao.User.GetExpiredVipUsers(ctx, days)
if err != nil {
return nil, fmt.Errorf("查询即将过期VIP用户失败: %v", err)
}
// 清除敏感信息
for _, user := range users {
user.Password = ""
}
return users, nil
}
// UpdateLoginInfo 更新登录信息
func (s *UserService) UpdateLoginInfo(ctx context.Context, userId int, ip string) error {
return dao.User.UpdateLoginInfo(ctx, userId, ip)
}

View File

@@ -0,0 +1,410 @@
package service
import (
"context"
"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/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// UserCollectService 用户收藏服务
type UserCollectService struct{}
// NewUserCollectService 创建用户收藏服务实例
func NewUserCollectService() *UserCollectService {
return &UserCollectService{}
}
// GetList 获取用户收藏列表
func (s *UserCollectService) GetList(r *ghttp.Request) {
// 获取请求参数
page := r.Get("page", 1).Int()
size := r.Get("size", 10).Int()
// 获取当前用户ID临时设置
userId := uint(1)
// 构建查询条件
query := dao.UserCollect.Ctx(r.Context()).Where("user_id", userId)
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取收藏总数失败")
return
}
// 获取列表数据
var collects []entity.UserCollect
err = query.Order("created_at DESC").
Limit((page-1)*size, size).
Scan(&collects)
if err != nil {
response.Error(r, response.CodeInternalError, "获取收藏列表失败")
return
}
response.Success(r, g.Map{
"list": collects,
"total": total,
"page": page,
"size": size,
})
}
// CheckCollect 检查是否已收藏
func (s *UserCollectService) CheckCollect(r *ghttp.Request) {
// 获取请求参数
movieId := r.Get("movie_id").Uint()
// 参数验证
if movieId == 0 {
response.Error(r, response.CodeInvalidParam, "电影ID不能为空")
return
}
// 获取当前用户ID临时设置
userId := uint(1)
// 检查是否已收藏
count, err := dao.UserCollect.Ctx(r.Context()).
Where("user_id", userId).
Where("movie_id", movieId).
Count()
if err != nil {
response.Error(r, response.CodeInternalError, "检查收藏状态失败")
return
}
response.Success(r, g.Map{
"is_collected": count > 0,
})
}
// 定义请求和响应结构体
type UserCollectAddReq struct {
MovieId uint `json:"movie_id"`
}
type UserCollectRemoveReq struct {
MovieId uint `json:"movie_id"`
}
type UserCollectListReq struct {
Page int `json:"page"`
Size int `json:"size"`
}
type UserCollectListRes struct {
List []UserCollectItem `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
type UserCollectItem struct {
Id uint `json:"id"`
MovieId uint `json:"movie_id"`
MovieName string `json:"movie_name"`
MovieCover string `json:"movie_cover"`
CreatedAt int64 `json:"created_at"`
}
type UserCollectCheckReq struct {
MovieId uint `json:"movie_id"`
}
type UserCollectCheckRes struct {
IsCollected bool `json:"is_collected"`
}
// Add 添加收藏
func (s *UserCollectService) Add(r *ghttp.Request) {
// 获取请求参数
movieId := r.Get("movie_id").Uint()
if movieId == 0 {
response.Error(r, response.CodeInvalidParam, "电影ID不能为空")
return
}
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
// 检查电影是否存在
var movie entity.Movie
err := g.DB().Model("movie").Where("id", movieId).Scan(&movie)
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if movie.Id == 0 {
response.Error(r, response.CodeNotFound, "电影不存在")
return
}
// 检查是否已经收藏
count, err := dao.UserCollect.Ctx(r.Context()).Where("user_id", userId).Where("movie_id", movieId).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "已经收藏过该电影")
return
}
// 添加收藏
data := &entity.UserCollect{
UserId: int(userId),
MovieId: int(movieId),
Type: 1, // 影片收藏
TargetId: int(movieId),
CreatedAt: gtime.Now(),
}
id, err := dao.UserCollect.Ctx(r.Context()).Data(data).InsertAndGetId()
if err != nil {
response.Error(r, response.CodeInternalError, "添加收藏失败")
return
}
response.Success(r, g.Map{
"id": uint(id),
})
}
// Remove 取消收藏
func (s *UserCollectService) Remove(r *ghttp.Request) {
// 获取请求参数
movieId := r.Get("movie_id").Uint()
if movieId == 0 {
response.Error(r, response.CodeInvalidParam, "电影ID不能为空")
return
}
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
response.Error(r, response.CodeUnauthorized, "用户未登录")
return
}
// 检查收藏是否存在
count, err := dao.UserCollect.Ctx(r.Context()).Where("user_id", userId).Where("movie_id", movieId).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "收藏不存在")
return
}
// 删除收藏
_, err = dao.UserCollect.Ctx(r.Context()).Where("user_id", userId).Where("movie_id", movieId).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "取消收藏失败")
return
}
response.Success(r, "取消收藏成功")
}
// List 获取收藏列表
func (s *UserCollectService) List(ctx context.Context, req *UserCollectListReq) (*UserCollectListRes, error) {
// 设置默认值
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
return nil, gerror.New("用户未登录")
}
// 构建查询条件
query := dao.UserCollect.Ctx(ctx).Where("user_id", userId)
// 获取总数
total, err := query.Count()
if err != nil {
return nil, gerror.New("获取收藏总数失败")
}
// 获取列表数据
var collects []entity.UserCollect
err = query.Order("id DESC").
Limit((req.Page-1)*req.Size, req.Size).
Scan(&collects)
if err != nil {
return nil, gerror.New("获取收藏列表失败")
}
// 获取电影信息
movieIds := make([]int, 0, len(collects))
for _, collect := range collects {
movieIds = append(movieIds, collect.MovieId)
}
movieMap := make(map[int]entity.Movie)
if len(movieIds) > 0 {
var movies []entity.Movie
g.DB().Model("movie").WhereIn("id", movieIds).Fields("id, name, cover").Scan(&movies)
for _, movie := range movies {
movieMap[int(movie.Id)] = movie
}
}
// 转换为响应格式
list := make([]UserCollectItem, 0, len(collects))
for _, collect := range collects {
movie := movieMap[collect.MovieId]
list = append(list, UserCollectItem{
Id: collect.Id,
MovieId: uint(collect.MovieId),
MovieName: movie.Title,
MovieCover: movie.Poster,
CreatedAt: collect.CreatedAt.Unix(),
})
}
return &UserCollectListRes{
List: list,
Total: total,
Page: req.Page,
Size: req.Size,
}, nil
}
// Check 检查是否收藏
func (s *UserCollectService) Check(ctx context.Context, req *UserCollectCheckReq) (*UserCollectCheckRes, error) {
// 获取当前用户ID
userId := uint(1) // 临时设置实际应该从JWT中获取
if userId == 0 {
return nil, gerror.New("用户未登录")
}
// 检查是否收藏
count, err := dao.UserCollect.Ctx(ctx).Where("user_id", userId).Where("movie_id", req.MovieId).Count()
if err != nil {
return nil, gerror.New("检查收藏状态失败")
}
return &UserCollectCheckRes{
IsCollected: count > 0,
}, nil
}
// GetUserCollectCount 获取用户收藏数量
func (s *UserCollectService) GetUserCollectCount(ctx context.Context, userId uint) (int, error) {
count, err := dao.UserCollect.Ctx(ctx).Where("user_id", userId).Count()
if err != nil {
return 0, gerror.New("获取用户收藏数量失败")
}
return count, nil
}
// GetMovieCollectCount 获取电影收藏数量
func (s *UserCollectService) GetMovieCollectCount(ctx context.Context, movieId uint) (int, error) {
count, err := dao.UserCollect.Ctx(ctx).Where("movie_id", movieId).Count()
if err != nil {
return 0, gerror.New("获取电影收藏数量失败")
}
return count, nil
}
// BatchRemove 批量取消收藏
func (s *UserCollectService) BatchRemove(ctx context.Context, userId uint, movieIds []uint) error {
if len(movieIds) == 0 {
return gerror.New("请选择要取消收藏的电影")
}
_, err := dao.UserCollect.Ctx(ctx).Where("user_id", userId).WhereIn("movie_id", movieIds).Delete()
if err != nil {
return gerror.New("批量取消收藏失败")
}
return nil
}
// GetPopularMovies 获取热门收藏电影
func (s *UserCollectService) GetPopularMovies(ctx context.Context, limit int) ([]g.Map, error) {
if limit <= 0 {
limit = 10
}
var result []g.Map
err := dao.UserCollect.Ctx(ctx).
Fields("movie_id, COUNT(*) as collect_count").
Group("movie_id").
Order("collect_count DESC").
Limit(limit).
Scan(&result)
if err != nil {
return nil, gerror.New("获取热门收藏电影失败")
}
return result, nil
}
// GetRecentCollects 获取最近收藏
func (s *UserCollectService) GetRecentCollects(ctx context.Context, userId uint, limit int) ([]UserCollectItem, error) {
if limit <= 0 {
limit = 10
}
// 获取最近收藏
var collects []entity.UserCollect
err := dao.UserCollect.Ctx(ctx).
Where("user_id", userId).
Order("created_at DESC").
Limit(limit).
Scan(&collects)
if err != nil {
return nil, gerror.New("获取最近收藏失败")
}
// 获取电影信息
movieIds := make([]int, 0, len(collects))
for _, collect := range collects {
movieIds = append(movieIds, collect.MovieId)
}
movieMap := make(map[int]entity.Movie)
if len(movieIds) > 0 {
var movies []entity.Movie
g.DB().Model("movie").WhereIn("id", movieIds).Fields("id, name, cover").Scan(&movies)
for _, movie := range movies {
movieMap[int(movie.Id)] = movie
}
}
// 转换为响应格式
list := make([]UserCollectItem, 0, len(collects))
for _, collect := range collects {
movie := movieMap[collect.MovieId]
list = append(list, UserCollectItem{
Id: collect.Id,
MovieId: uint(collect.MovieId),
MovieName: movie.Title,
MovieCover: movie.Poster,
CreatedAt: collect.CreatedAt.Unix(),
})
}
return list, nil
}

View File

@@ -0,0 +1,357 @@
package service
import (
"context"
"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/gtime"
"github.com/gogf/gf/v2/util/gconv"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// UserWatchHistoryService 用户观看历史服务
type UserWatchHistoryService struct{}
// NewUserWatchHistoryService 创建用户观看历史服务实例
func NewUserWatchHistoryService() *UserWatchHistoryService {
return &UserWatchHistoryService{}
}
// UserWatchHistoryCreateReq 创建观看历史请求
type UserWatchHistoryCreateReq struct {
MovieId uint `json:"movie_id" v:"required#电影ID不能为空"`
EpisodeId uint `json:"episode_id"`
Progress int `json:"progress" v:"required#观看进度不能为空"`
WatchTime int `json:"watch_time" v:"required#观看时长不能为空"`
}
// UserWatchHistoryListReq 获取观看历史列表请求
type UserWatchHistoryListReq struct {
Page int `json:"page" v:"min:1#页码最小为1"`
Size int `json:"size" v:"min:1,max:100#每页数量范围1-100"`
}
// UserWatchHistoryListRes 观看历史列表响应
type UserWatchHistoryListRes struct {
List []UserWatchHistoryItem `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
// UserWatchHistoryItem 观看历史项
type UserWatchHistoryItem struct {
Id uint `json:"id"`
MovieId uint `json:"movie_id"`
MovieTitle string `json:"movie_title"`
MoviePoster string `json:"movie_poster"`
EpisodeId uint `json:"episode_id"`
EpisodeTitle string `json:"episode_title"`
Progress int `json:"progress"`
WatchTime int `json:"watch_time"`
LastWatchTime int64 `json:"last_watch_time"`
CreatedAt int64 `json:"created_at"`
}
// UserWatchHistoryClearReq 清空观看历史请求
type UserWatchHistoryClearReq struct {
UserId uint `json:"user_id"`
}
// UserWatchHistoryProgressReq 获取观看进度请求
type UserWatchHistoryProgressReq struct {
MovieId uint `json:"movie_id" v:"required#电影ID不能为空"`
EpisodeId uint `json:"episode_id"`
}
// UserWatchHistoryProgressRes 观看进度响应
type UserWatchHistoryProgressRes struct {
Progress int `json:"progress"`
WatchTime int `json:"watch_time"`
LastWatchTime int64 `json:"last_watch_time"`
}
// Create 创建观看历史
func (s *UserWatchHistoryService) Create(r *ghttp.Request) {
var req UserWatchHistoryCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 获取用户ID从JWT token或session中获取
userId := GetUserIdFromContext(r.Context())
// 检查是否已存在观看历史
var existHistory entity.UserWatchHistory
err := dao.UserWatchHistory.Ctx(r.Context()).
Where("user_id", userId).
Where("movie_id", req.MovieId).
Where("episode_id", req.EpisodeId).
Scan(&existHistory)
if err != nil && !g.IsEmpty(existHistory) {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if existHistory.Id > 0 {
// 更新现有记录
_, err = dao.UserWatchHistory.Ctx(r.Context()).
Where("id", existHistory.Id).
Update(g.Map{
"progress": req.Progress,
"watch_time": req.WatchTime,
"last_watch_time": gtime.Now(),
"updated_at": gtime.Now(),
})
} else {
// 创建新记录
history := &entity.UserWatchHistory{
UserId: gconv.Int(userId),
MovieId: gconv.Int(req.MovieId),
EpisodeId: gconv.Int(req.EpisodeId),
Progress: float64(req.Progress),
WatchTime: req.WatchTime,
LastWatchTime: int(gtime.Now().Unix()),
CreatedAt: gtime.Now(),
}
_, err = dao.UserWatchHistory.Ctx(r.Context()).Insert(history)
}
if err != nil {
response.Error(r, response.CodeInternalError, "保存观看历史失败")
return
}
response.Success(r, "保存成功")
}
// GetList 获取观看历史列表
func (s *UserWatchHistoryService) GetList(r *ghttp.Request) {
var req UserWatchHistoryListReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 设置默认值
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 10
}
// 获取用户ID
userId := GetUserIdFromContext(r.Context())
// 构建查询
query := dao.UserWatchHistory.Ctx(r.Context()).Where("user_id", userId)
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取数据失败")
return
}
// 获取列表数据
var histories []entity.UserWatchHistory
err = query.Order("last_watch_time DESC").
Limit((req.Page-1)*req.Size, req.Size).
Scan(&histories)
if err != nil {
response.Error(r, response.CodeInternalError, "获取数据失败")
return
}
// 构建响应数据
var list []UserWatchHistoryItem
for _, history := range histories {
// 获取电影信息
var movie entity.Movie
err = g.DB().Model("movie").Where("id", history.MovieId).Scan(&movie)
if err != nil {
continue
}
// 获取剧集信息(如果有)
var episode entity.Episode
episodeTitle := ""
if history.EpisodeId > 0 {
err = g.DB().Model("episode").Where("id", history.EpisodeId).Scan(&episode)
if err == nil {
episodeTitle = episode.Title
}
}
list = append(list, UserWatchHistoryItem{
Id: gconv.Uint(history.Id),
MovieId: gconv.Uint(history.MovieId),
MovieTitle: movie.Title,
MoviePoster: movie.Poster,
EpisodeId: gconv.Uint(history.EpisodeId),
EpisodeTitle: episodeTitle,
Progress: int(history.Progress),
WatchTime: history.WatchTime,
LastWatchTime: int64(history.LastWatchTime),
CreatedAt: history.CreatedAt.Unix(),
})
}
res := UserWatchHistoryListRes{
List: list,
Total: total,
Page: req.Page,
Size: req.Size,
}
response.Success(r, res)
}
// Delete 删除观看历史
func (s *UserWatchHistoryService) Delete(r *ghttp.Request) {
id := r.Get("id").Uint()
if id == 0 {
response.Error(r, response.CodeInvalidParam, "ID不能为空")
return
}
// 获取用户ID
userId := GetUserIdFromContext(r.Context())
// 删除记录
_, err := dao.UserWatchHistory.Ctx(r.Context()).
Where("id", id).
Where("user_id", userId).
Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "删除失败")
return
}
response.Success(r, "删除成功")
}
// Clear 清空观看历史
func (s *UserWatchHistoryService) Clear(ctx context.Context, req *UserWatchHistoryClearReq) error {
// 删除用户的所有观看历史
_, err := dao.UserWatchHistory.Ctx(ctx).
Where("user_id", req.UserId).
Delete()
if err != nil {
return gerror.New("清空观看历史失败")
}
return nil
}
// Add 添加观看历史
func (s *UserWatchHistoryService) Add(r *ghttp.Request) {
var req UserWatchHistoryCreateReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 获取用户ID
userId := GetUserIdFromContext(r.Context())
// 检查是否已存在观看历史
var existHistory entity.UserWatchHistory
err := dao.UserWatchHistory.Ctx(r.Context()).
Where("user_id", userId).
Where("movie_id", req.MovieId).
Where("episode_id", req.EpisodeId).
Scan(&existHistory)
if err != nil && !g.IsEmpty(existHistory) {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if existHistory.Id > 0 {
// 更新现有记录
_, err = dao.UserWatchHistory.Ctx(r.Context()).
Where("id", existHistory.Id).
Update(g.Map{
"progress": req.Progress,
"watch_time": req.WatchTime,
"last_watch_time": gtime.Now().Unix(),
"updated_at": gtime.Now(),
})
} else {
// 创建新记录
history := &entity.UserWatchHistory{
UserId: gconv.Int(userId),
MovieId: gconv.Int(req.MovieId),
EpisodeId: gconv.Int(req.EpisodeId),
Progress: float64(req.Progress),
WatchTime: req.WatchTime,
LastWatchTime: int(gtime.Now().Unix()),
CreatedAt: gtime.Now(),
}
_, err = dao.UserWatchHistory.Ctx(r.Context()).Insert(history)
}
if err != nil {
response.Error(r, response.CodeInternalError, "保存观看历史失败")
return
}
response.Success(r, "保存成功")
}
// GetProgress 获取观看进度
func (s *UserWatchHistoryService) GetProgress(r *ghttp.Request) {
var req UserWatchHistoryProgressReq
if err := r.Parse(&req); err != nil {
response.Error(r, response.CodeInvalidParam, err.Error())
return
}
// 获取用户ID
userId := GetUserIdFromContext(r.Context())
// 查询观看历史
var history entity.UserWatchHistory
err := dao.UserWatchHistory.Ctx(r.Context()).
Where("user_id", userId).
Where("movie_id", req.MovieId).
Where("episode_id", req.EpisodeId).
Scan(&history)
if err != nil && !g.IsEmpty(history) {
response.Error(r, response.CodeInternalError, "获取观看进度失败")
return
}
var res UserWatchHistoryProgressRes
if history.Id > 0 {
res = UserWatchHistoryProgressRes{
Progress: int(history.Progress),
WatchTime: history.WatchTime,
LastWatchTime: int64(history.LastWatchTime),
}
} else {
res = UserWatchHistoryProgressRes{
Progress: 0,
WatchTime: 0,
LastWatchTime: 0,
}
}
response.Success(r, res)
}

View File

@@ -0,0 +1,598 @@
package service
import (
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"nl-video-api/internal/dao"
"nl-video-api/internal/model/entity"
"nl-video-api/utility/response"
)
// VipLevelService VIP等级服务
type VipLevelService struct{}
var VipLevel = &VipLevelService{}
// GetList 获取VIP等级列表
func (s *VipLevelService) GetList(r *ghttp.Request) {
// 获取请求参数
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 获取总数
total, err := dao.VipLevel.Ctx(r.Context()).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级总数失败")
return
}
// 获取VIP等级列表
var vipLevels []*entity.VipLevel
err = dao.VipLevel.Ctx(r.Context()).
Page(page, pageSize).
OrderAsc("level").
Scan(&vipLevels)
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级列表失败")
return
}
// 构建返回数据
var vipLevelItems []g.Map
for _, vipLevel := range vipLevels {
vipLevelItems = append(vipLevelItems, g.Map{
"id": vipLevel.Id,
"name": vipLevel.Name,
"level": vipLevel.Level,
"price": vipLevel.Price,
"duration": vipLevel.Duration,
"description": vipLevel.Description,
"privileges": vipLevel.Privileges,
"status": vipLevel.Status,
"created_at": vipLevel.CreatedAt.Unix(),
"updated_at": vipLevel.UpdatedAt.Unix(),
})
}
response.Success(r, g.Map{
"list": vipLevelItems,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// GetDetail 获取VIP等级详情
func (s *VipLevelService) GetDetail(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级ID格式错误")
return
}
// 获取VIP等级详情
var vipLevel entity.VipLevel
err = dao.VipLevel.Ctx(r.Context()).Where("id", id).Scan(&vipLevel)
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级详情失败")
return
}
if vipLevel.Id == 0 {
response.Error(r, response.CodeNotFound, "VIP等级不存在")
return
}
response.Success(r, g.Map{
"id": vipLevel.Id,
"name": vipLevel.Name,
"level": vipLevel.Level,
"price": vipLevel.Price,
"duration": vipLevel.Duration,
"description": vipLevel.Description,
"privileges": vipLevel.Privileges,
"status": vipLevel.Status,
"created_at": vipLevel.CreatedAt.Unix(),
"updated_at": vipLevel.UpdatedAt.Unix(),
})
}
// AdminGetList 管理员获取VIP等级列表
func (s *VipLevelService) AdminGetList(r *ghttp.Request) {
// 获取请求参数
keyword := r.Get("keyword").String()
statusStr := r.Get("status").String()
pageStr := r.Get("page", "1").String()
pageSizeStr := r.Get("page_size", "10").String()
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil || pageSize < 1 {
pageSize = 10
}
// 构建查询条件
query := dao.VipLevel.Ctx(r.Context())
if keyword != "" {
query = query.Where("name LIKE ?", "%"+keyword+"%")
}
if statusStr != "" {
status, err := strconv.Atoi(statusStr)
if err == nil {
query = query.Where("status", status)
}
}
// 获取总数
total, err := query.Count()
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级总数失败")
return
}
// 获取VIP等级列表
var vipLevels []*entity.VipLevel
err = query.Page(page, pageSize).OrderAsc("level").Scan(&vipLevels)
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级列表失败")
return
}
// 构建返回数据
var vipLevelItems []g.Map
for _, vipLevel := range vipLevels {
vipLevelItems = append(vipLevelItems, g.Map{
"id": vipLevel.Id,
"name": vipLevel.Name,
"level": vipLevel.Level,
"price": vipLevel.Price,
"duration": vipLevel.Duration,
"description": vipLevel.Description,
"privileges": vipLevel.Privileges,
"status": vipLevel.Status,
"created_at": vipLevel.CreatedAt.Unix(),
"updated_at": vipLevel.UpdatedAt.Unix(),
})
}
response.Success(r, g.Map{
"list": vipLevelItems,
"total": total,
"page": page,
"page_size": pageSize,
})
}
// AdminCreate 管理员创建VIP等级
func (s *VipLevelService) AdminCreate(r *ghttp.Request) {
// 获取请求参数
name := r.Get("name").String()
levelStr := r.Get("level").String()
priceStr := r.Get("price").String()
durationStr := r.Get("duration").String()
description := r.Get("description").String()
privileges := r.Get("privileges").String()
statusStr := r.Get("status", "1").String()
if name == "" {
response.Error(r, response.CodeInvalidParam, "VIP等级名称不能为空")
return
}
level, err := strconv.Atoi(levelStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级格式错误")
return
}
price, err := strconv.ParseFloat(priceStr, 64)
if err != nil {
response.Error(r, response.CodeInvalidParam, "价格格式错误")
return
}
duration, err := strconv.Atoi(durationStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "有效期格式错误")
return
}
status, err := strconv.Atoi(statusStr)
if err != nil {
status = 1
}
// 检查等级是否已存在
count, err := dao.VipLevel.Ctx(r.Context()).Where("level", level).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "该等级已存在")
return
}
// 创建VIP等级
_, err = dao.VipLevel.Ctx(r.Context()).Data(g.Map{
"name": name,
"level": level,
"price": price,
"duration": duration,
"description": description,
"privileges": privileges,
"status": status,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
response.Error(r, response.CodeInternalError, "创建VIP等级失败")
return
}
response.Success(r, "创建成功")
}
// AdminUpdate 管理员更新VIP等级
func (s *VipLevelService) AdminUpdate(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
name := r.Get("name").String()
levelStr := r.Get("level").String()
priceStr := r.Get("price").String()
durationStr := r.Get("duration").String()
description := r.Get("description").String()
privileges := r.Get("privileges").String()
statusStr := r.Get("status").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级ID格式错误")
return
}
// 检查VIP等级是否存在
var existVipLevel entity.VipLevel
err = dao.VipLevel.Ctx(r.Context()).Where("id", id).Scan(&existVipLevel)
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if existVipLevel.Id == 0 {
response.Error(r, response.CodeNotFound, "VIP等级不存在")
return
}
// 构建更新数据
updateData := g.Map{
"updated_at": gtime.Now(),
}
if name != "" {
updateData["name"] = name
}
if levelStr != "" {
level, err := strconv.Atoi(levelStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级格式错误")
return
}
// 检查等级是否已被其他记录使用
count, err := dao.VipLevel.Ctx(r.Context()).Where("level", level).Where("id !=", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count > 0 {
response.Error(r, response.CodeInvalidParam, "该等级已存在")
return
}
updateData["level"] = level
}
if priceStr != "" {
price, err := strconv.ParseFloat(priceStr, 64)
if err != nil {
response.Error(r, response.CodeInvalidParam, "价格格式错误")
return
}
updateData["price"] = price
}
if durationStr != "" {
duration, err := strconv.Atoi(durationStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "有效期格式错误")
return
}
updateData["duration"] = duration
}
if description != "" {
updateData["description"] = description
}
if privileges != "" {
updateData["privileges"] = privileges
}
if statusStr != "" {
status, err := strconv.Atoi(statusStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "状态格式错误")
return
}
updateData["status"] = status
}
// 更新VIP等级
_, err = dao.VipLevel.Ctx(r.Context()).Where("id", id).Data(updateData).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新VIP等级失败")
return
}
response.Success(r, "更新成功")
}
// AdminDelete 管理员删除VIP等级
func (s *VipLevelService) AdminDelete(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级ID格式错误")
return
}
// 检查VIP等级是否存在
count, err := dao.VipLevel.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "VIP等级不存在")
return
}
// 检查是否有用户正在使用该VIP等级
userCount, err := g.DB().Model("nl_user").Where("vip_level", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if userCount > 0 {
response.Error(r, response.CodeInvalidParam, "该VIP等级正在被用户使用无法删除")
return
}
// 删除VIP等级
_, err = dao.VipLevel.Ctx(r.Context()).Where("id", id).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "删除VIP等级失败")
return
}
response.Success(r, "删除成功")
}
// AdminBatchDelete 管理员批量删除VIP等级
func (s *VipLevelService) AdminBatchDelete(r *ghttp.Request) {
// 获取请求参数
var ids []int
err := r.Parse(&ids)
if err != nil {
response.Error(r, response.CodeInvalidParam, "参数格式错误")
return
}
if len(ids) == 0 {
response.Error(r, response.CodeInvalidParam, "请选择要删除的VIP等级")
return
}
// 检查是否有用户正在使用这些VIP等级
userCount, err := g.DB().Model("nl_user").WhereIn("vip_level", ids).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if userCount > 0 {
response.Error(r, response.CodeInvalidParam, "选中的VIP等级中有正在被用户使用的无法删除")
return
}
// 批量删除
_, err = dao.VipLevel.Ctx(r.Context()).WhereIn("id", ids).Delete()
if err != nil {
response.Error(r, response.CodeInternalError, "批量删除失败")
return
}
response.Success(r, "删除成功")
}
// AdminUpdateStatus 管理员更新VIP等级状态
func (s *VipLevelService) AdminUpdateStatus(r *ghttp.Request) {
// 获取请求参数
idStr := r.Get("id").String()
statusStr := r.Get("status").String()
if idStr == "" {
response.Error(r, response.CodeInvalidParam, "VIP等级ID不能为空")
return
}
if statusStr == "" {
response.Error(r, response.CodeInvalidParam, "状态不能为空")
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "VIP等级ID格式错误")
return
}
status, err := strconv.Atoi(statusStr)
if err != nil {
response.Error(r, response.CodeInvalidParam, "状态格式错误")
return
}
// 检查VIP等级是否存在
count, err := dao.VipLevel.Ctx(r.Context()).Where("id", id).Count()
if err != nil {
response.Error(r, response.CodeInternalError, "系统错误")
return
}
if count == 0 {
response.Error(r, response.CodeNotFound, "VIP等级不存在")
return
}
// 更新状态
_, err = dao.VipLevel.Ctx(r.Context()).Where("id", id).Data(g.Map{
"status": status,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
response.Error(r, response.CodeInternalError, "更新状态失败")
return
}
response.Success(r, "更新成功")
}
// GetActiveList 获取启用的VIP等级列表
func (s *VipLevelService) GetActiveList(r *ghttp.Request) {
// 获取启用的VIP等级列表
var vipLevels []*entity.VipLevel
err := dao.VipLevel.Ctx(r.Context()).
Where("status", 1).
OrderAsc("level").
Scan(&vipLevels)
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级列表失败")
return
}
// 构建返回数据
var vipLevelItems []g.Map
for _, vipLevel := range vipLevels {
vipLevelItems = append(vipLevelItems, g.Map{
"id": vipLevel.Id,
"name": vipLevel.Name,
"level": vipLevel.Level,
"price": vipLevel.Price,
"duration": vipLevel.Duration,
"description": vipLevel.Description,
"privileges": vipLevel.Privileges,
})
}
response.Success(r, vipLevelItems)
}
// GetUserVipInfo 获取用户VIP信息
func (s *VipLevelService) GetUserVipInfo(r *ghttp.Request) {
// 从上下文获取用户ID
userIdValue := r.Context().Value("user_id")
if userIdValue == nil {
response.Error(r, response.CodeUnauthorized, "请先登录")
return
}
userId := userIdValue.(uint)
// 获取用户信息
var user entity.NlUser
err := g.DB().Model("nl_user").Where("id", userId).Scan(&user)
if err != nil {
response.Error(r, response.CodeInternalError, "获取用户信息失败")
return
}
if user.Id == 0 {
response.Error(r, response.CodeNotFound, "用户不存在")
return
}
// 获取VIP等级信息
var vipLevel entity.VipLevel
if user.VipLevel > 0 {
dao.VipLevel.Ctx(r.Context()).Where("id", user.VipLevel).Scan(&vipLevel)
}
response.Success(r, g.Map{
"user_id": user.Id,
"vip_level_id": user.VipLevel,
"vip_level_name": vipLevel.Name,
"vip_expire_at": user.VipExpireTime,
"is_vip": user.VipLevel > 0 && user.VipExpireTime > int(gtime.Now().Unix()),
})
}
// GetAll 获取所有VIP等级
func (s *VipLevelService) GetAll(r *ghttp.Request) {
// 获取所有VIP等级列表
var vipLevels []*entity.VipLevel
err := dao.VipLevel.Ctx(r.Context()).
OrderAsc("level").
Scan(&vipLevels)
if err != nil {
response.Error(r, response.CodeInternalError, "获取VIP等级列表失败")
return
}
// 构建返回数据
var vipLevelItems []g.Map
for _, vipLevel := range vipLevels {
vipLevelItems = append(vipLevelItems, g.Map{
"id": vipLevel.Id,
"name": vipLevel.Name,
"level": vipLevel.Level,
"price": vipLevel.Price,
"duration": vipLevel.Duration,
"description": vipLevel.Description,
"privileges": vipLevel.Privileges,
"status": vipLevel.Status,
"created_at": vipLevel.CreatedAt.Unix(),
"updated_at": vipLevel.UpdatedAt.Unix(),
})
}
response.Success(r, vipLevelItems)
}