Compare commits

...

10 Commits

Author SHA1 Message Date
3beb54c12c 🎨 优化订单逻辑,新增vip字段 2025-08-12 16:24:33 +08:00
90d4b959a1 🎨 新增用户简介字段,新增手机端讲师相关接口 2025-08-12 16:24:11 +08:00
670791d342 🎨 用户端新增文章更新和删除接口 2025-08-12 16:23:34 +08:00
b6e238d739 🐛 修复从请求头中获取用户ID方法的bug 2025-08-09 08:34:21 +08:00
2674f4f061 🎨 优化机器人相关接口 2025-08-09 08:33:49 +08:00
e4bfdc31da 🎨 新增获取已购课程接口& 2025-08-09 08:33:28 +08:00
82c6003b4a 🎨 优化app端文件上传接口 2025-07-31 02:41:39 +08:00
9c51f0cbe4 🎨 分类新增url字段 2025-07-31 02:41:09 +08:00
da1ebd72f4 🎨 优化余额支付逻辑 2025-07-31 02:40:37 +08:00
4ce59a3090 🎨 新增用户端文章接口&优化部分旧接口 2025-07-31 02:40:13 +08:00
26 changed files with 337 additions and 59 deletions

View File

@@ -3,6 +3,7 @@ package app
import (
"errors"
"fmt"
common "git.echol.cn/loser/lckt/model/common/request"
"strconv"
"time"
@@ -361,3 +362,30 @@ func (a *AppUserApi) BindPhone(context *gin.Context) {
r.OkWithDetailed(user, "绑定手机号成功", context)
}
// -----------------讲师相关---------------------
// GetTeacherList 获取讲师列表
func (a *AppUserApi) GetTeacherList(context *gin.Context) {
var p common.PageInfo
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("参数错误,获取讲师列表失败", zap.Error(err))
r.FailWithMessage("参数错误,获取讲师列表失败", context)
return
}
teachers, total, err := appUserService.GetTeacherList(p)
if err != nil {
global.GVA_LOG.Error("获取讲师列表失败", zap.Error(err))
r.FailWithMessage("获取讲师列表失败", context)
return
}
r.OkWithDetailed(
r.PageResult{
List: teachers,
Total: total,
Page: p.Page,
PageSize: p.PageSize,
}, "获取讲师列表成功", context)
}

View File

@@ -115,7 +115,7 @@ func (ArticleApi) AppById(ctx *gin.Context) {
}
userId := user_jwt.GetUserID(ctx)
article, err := articleService.APPGetArticle(id, userId)
article, err := articleService.APPGetArticle(id, int(userId))
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
r.FailWithMessage("查询失败:"+err.Error(), ctx)
@@ -123,3 +123,86 @@ func (ArticleApi) AppById(ctx *gin.Context) {
}
r.OkWithData(article, ctx)
}
// AppPush app端推送文章
func (ArticleApi) AppPush(ctx *gin.Context) {
var p article.Article
if err := ctx.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), ctx)
global.GVA_LOG.Error("参数有误!", zap.Error(err))
return
}
err := articleService.AppPush(p)
if err != nil {
global.GVA_LOG.Error("推送失败!", zap.Error(err))
r.FailWithMessage("推送失败:"+err.Error(), ctx)
return
}
r.OkWithMessage("推送成功", ctx)
}
// GetMyArticleList 获取我的文章列表
func (ArticleApi) GetMyArticleList(ctx *gin.Context) {
var p request.GetList
if err := ctx.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), ctx)
global.GVA_LOG.Error("参数有误!", zap.Error(err))
return
}
userId := user_jwt.GetUserID(ctx)
if userId == 0 {
r.FailWithMessage("用户未登录", ctx)
return
}
p.TeacherId = int(userId) // 设置当前用户ID为教师ID
list, total, err := articleService.GetMyArticleList(p)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
r.FailWithMessage("查询失败:"+err.Error(), ctx)
return
}
r.OkWithDetailed(gin.H{"list": list, "total": total}, "查询成功", ctx)
}
// GetBuyList 获取用户购买的文章列表
func (ArticleApi) GetBuyList(ctx *gin.Context) {
var p request.GetList
if err := ctx.ShouldBind(&p); err != nil {
r.FailWithMessage(err.Error(), ctx)
global.GVA_LOG.Error("参数有误!", zap.Error(err))
return
}
userId := user_jwt.GetUserID(ctx)
if userId == 0 {
r.FailWithMessage("用户未登录", ctx)
return
}
p.UserId = userId // 设置当前用户ID
list, total, err := articleService.GetBuyList(p)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
r.FailWithMessage("查询失败:"+err.Error(), ctx)
return
}
r.OkWithDetailed(gin.H{"list": list, "total": total}, "查询成功", ctx)
}
// AppDelete 删除文章
func (ArticleApi) AppDelete(ctx *gin.Context) {
id := ctx.Param("id")
if id == "" {
r.FailWithMessage("参数错误", ctx)
return
}
err := articleService.AppDelete(id)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
r.FailWithMessage("删除失败:"+err.Error(), ctx)
return
}
r.OkWithMessage("删除成功", ctx)
}

View File

@@ -189,5 +189,5 @@ func (btApi *BotApi) FindKey(c *gin.Context) {
return
}
response.OkWithDetailed(bt.Content, "获取成功", c)
response.OkWithDetailed(bt, "获取成功", c)
}

View File

@@ -32,6 +32,7 @@ func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
response.FailWithMessage("接收文件失败", c)
return
}
file, err = fileUploadAndDownloadService.UploadFile(header, noSave, classId) // 文件上传后拿到文件路径
if err != nil {
global.GVA_LOG.Error("上传文件失败!", zap.Error(err))

2
go.mod
View File

@@ -5,6 +5,7 @@ go 1.23.0
toolchain go1.23.2
require (
github.com/ArtisanCloud/PowerLibs/v3 v3.3.2
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.21
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7
@@ -64,7 +65,6 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/STARRY-S/zip v0.1.0 // indirect

View File

@@ -81,8 +81,8 @@ func Routers() *gin.Engine {
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.AllCors())
AppAuthGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
AppAuthGroup.Use(middleware.AllCors())
//AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.AllCors())
//AppAuthGroup.Use(middleware.AllCors())
AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.AllCors())
{
// 健康监测
PublicGroup.GET("/health", func(c *gin.Context) {
@@ -95,24 +95,24 @@ func Routers() *gin.Engine {
}
{
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup, AppAuthGroup) // 文件上传下载功能路由
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
}
//APP相关路由
@@ -126,7 +126,7 @@ func Routers() *gin.Engine {
InstallPlugin(PrivateGroup, PublicGroup, Router)
// 注册业务路由
initBizRouter(PrivateGroup, PublicGroup)
initBizRouter(PrivateGroup, PublicGroup, AppAuthGroup)
PluginInit(PublicGroup, customerservice.CreateCustomerServicePlug())
PluginInit(PrivateGroup, picturelibrary.CreatePictureLibraryPlug())

View File

@@ -12,6 +12,7 @@ func holder(routers ...*gin.RouterGroup) {
func initBizRouter(routers ...*gin.RouterGroup) {
privateGroup := routers[0]
publicGroup := routers[1]
appGroup := routers[2]
holder(publicGroup, privateGroup)
{
categoryRouter := router.RouterGroupApp.Category
@@ -23,7 +24,7 @@ func initBizRouter(routers ...*gin.RouterGroup) {
}
{
articleRouter := router.RouterGroupApp.Article
articleRouter.InitBotRouter(privateGroup, publicGroup)
articleRouter.InitBotRouter(privateGroup, publicGroup, appGroup)
}
{
userRouter := router.RouterGroupApp.User

View File

@@ -8,6 +8,7 @@ type Order struct {
UserId uint64 `gorm:"column:user_id;type:bigint(20) unsigned;comment:用户ID;NOT NULL" json:"user_id"`
OrderType int `gorm:"column:order_type;type:int(11);comment:订单类型 |1 课程|2 vip|;NOT NULL" json:"order_type"`
ArticleId uint `gorm:"column:article_id;type:bigint(20) unsigned;default:0;comment:文章ID;NOT NULL" json:"article_id"`
VipId uint `gorm:"column:vip_id;type:bigint(20) unsigned;default:0;comment:会员ID;NOT NULL" json:"vip_id"`
Title string `gorm:"column:title;type:varchar(255);comment:订单标题;NOT NULL" json:"title"`
Name string `gorm:"column:name;type:varchar(255);comment:名称;NOT NULL" json:"name"`
Price int64 `gorm:"column:price;type:int(11) unsigned;default:0;comment:订单价格;NOT NULL" json:"price"`

View File

@@ -15,3 +15,10 @@ type UserInfo struct {
IsVip int8 `json:"is_vip" form:"is_vip"` //是否是VIP 0 否 1 是
VipExpireTime string `json:"vip_expire_time" form:"vip_expire_time"` // VIP过期时间
}
type TeacherInfo struct {
ID uint `json:"id" form:"id"`
NickName string `json:"nick_name" form:"nick_name"`
Avatar string `json:"avatar" form:"avatar"`
Des string `json:"des" form:"des"`
}

View File

@@ -16,6 +16,7 @@ type Article struct {
IsFree int `json:"isFree" gorm:"comment:是否免费;default:0"` // 是否免费 0-否 1-是
// 分类ID
CategoryId int `json:"categoryId" gorm:"comment:分类ID"`
Status int `json:"status" gorm:"comment:状态 1-已发布 2-待审核 3-审核不通过;default:2"` // 状态 0-草稿 1-已发布 2-待审核 3-审核不通过
}
// TableName 文章表

View File

@@ -6,8 +6,10 @@ type GetList struct {
request.PageInfo
Title string `json:"title" form:"title"` // 文章标题
// 分类ID
CategoryId int `json:"categoryId" form:"categoryId"` // 分类ID
TeacherId int `json:"teacherId" form:"teacherId"` // 讲师ID
CategoryId int `json:"categoryId" form:"categoryId"` // 分类ID
TeacherId int `json:"teacherId" form:"teacherId"` // 讲师ID
Status int `json:"status" form:"status"` // 状态 0-草稿 1-已发布 2-待审核 3-审核不通过
UserId uint `json:"userId" form:"userId"` // 用户ID
}
type DeleteIds struct {

View File

@@ -1,5 +1,7 @@
package vo
import "git.echol.cn/loser/lckt/model/app/vo"
type ArticleListVo struct {
ID int `json:"id" gorm:"comment:文章ID"`
Title string `json:"title" gorm:"comment:文章标题"`
@@ -26,3 +28,8 @@ type ArticleVo struct {
IsFree int `json:"isFree" gorm:"comment:是否免费;default:0"` // 是否免费 0-否 1-是
IsBuy int `json:"isBuy" gorm:"comment:是否购买;default:0"` // 是否购买 0-否 1-是
}
type ArticleTeacherVo struct {
TeacherInfo vo.TeacherInfo
ArticleList []ArticleListVo `json:"articleList" gorm:"comment:文章列表"`
}

View File

@@ -10,6 +10,8 @@ type Bot struct {
global.GVA_MODEL
Keyword string `json:"keyword" form:"keyword" gorm:"column:keyword;comment:关键词;" binding:"required"` //关键词
Content *string `json:"content" form:"content" gorm:"column:content;comment:内容;type:text;" binding:"required"` //内容
// 关联词
RelatedWords *string `json:"related_words" form:"related_words" gorm:"column:related_words;comment:关联词;type:text;"` //关联词
}
// TableName 机器人 Bot自定义表名 bots

View File

@@ -14,6 +14,7 @@ type Category struct {
ParentId *int `json:"parentId" form:"parentId" gorm:"column:parent_id;comment:父类别ID;"` //父ID
Icon *string `json:"icon" form:"icon" gorm:"column:icon;comment:类别图标;"` //图标
IndexTrue *bool `json:"index" form:"index" gorm:"column:index;comment:是否首页显示;"` //是否首页显示
Url *string `json:"url" form:"url" gorm:"column:url;comment:类别链接;"` //链接
}
// TableName 类别 Category自定义表名 categories

View File

@@ -6,6 +6,7 @@ type User struct {
global.GVA_MODEL
NickName string `json:"nick_name" form:"nick_name" gorm:"comment:用户昵称"`
Phone string `json:"phone" form:"phone" gorm:"comment:用户手机号"`
Des string `json:"des" form:"des" gorm:"comment:用户描述"`
UnionId string `json:"union_id" form:"union_id" gorm:"type:varchar(255) comment '微信UnionId'"`
OpenId string `gorm:"column:open_id;default:'';comment:'openId'" json:"-"`
Password string `json:"password" form:"password" gorm:"comment:用户密码"`

View File

@@ -8,6 +8,7 @@ type Vip struct {
Level int `json:"level" form:"level" gorm:"comment:会员等级 1 Vip 2 Svip"` // 会员等级
Price float64 `json:"price" form:"price" gorm:"comment:会员价格"` // 会员价格
Expiration int64 `json:"expiration" form:"expiration" gorm:"comment:会员有效期"` // 会员过期时间
Des string `json:"des" form:"des" gorm:"comment:会员描述"` // 会员描述
}
// TableName 设置表名

View File

@@ -13,7 +13,8 @@ func (s *UserRouter) InitAppUserRouter(AppAuthGroup, PublicRouter *gin.RouterGro
appUserRouter.GET("/info", userApi.GetUserInfo) // 获取用户信息
//申请成为讲师
appUserRouter.POST("/applyTeacher", userApi.ApplyTeacher) // 申请成为讲师
appUserRouter.GET("/applyTeacher", userApi.GetTeacherApply) // 获取教师列表
appUserRouter.GET("/applyTeacher", userApi.GetTeacherApply) // 获取教师申请状态
appUserRouter.GET("/teachers", userApi.GetTeacherList) // 获取讲师列表
}
{
publicRouter.POST("wxLogin", userApi.WechatLogin) // 微信登录

View File

@@ -8,10 +8,11 @@ import (
type ArticleRouter struct{}
// InitBotRouter 初始化 文章 路由信息
func (s *ArticleRouter) InitBotRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
func (s *ArticleRouter) InitBotRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup, AppRouter *gin.RouterGroup) {
articleRouter := Router.Group("article").Use(middleware.OperationRecord())
articleRouterWithoutRecord := Router.Group("article")
articleRouterWithoutAuth := PublicRouter.Group("article")
appRouter := AppRouter.Group("article")
{
articleRouter.POST("", artApi.Create) // 新建文章
articleRouter.DELETE("", artApi.Delete) // 批量删除文章
@@ -27,4 +28,12 @@ func (s *ArticleRouter) InitBotRouter(Router *gin.RouterGroup, PublicRouter *gin
articleRouterWithoutAuth.GET("app/list", artApi.APPGetList) // 文章公开接口
articleRouterWithoutAuth.GET("app/:id", artApi.AppById) // 文章开放接口
}
{
// App端文章相关接口
appRouter.POST("publish", artApi.AppPush) // 获取文章列表
appRouter.PUT("publish", artApi.AppPush)
appRouter.DELETE("delete/:id", artApi.AppDelete) // 批量删除文章
appRouter.GET("my", artApi.GetMyArticleList) // 获取我的文章列表
appRouter.GET("buyList", artApi.GetBuyList) // 获取购买的文章列表
}
}

View File

@@ -6,8 +6,9 @@ import (
type FileUploadAndDownloadRouter struct{}
func (e *FileUploadAndDownloadRouter) InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) {
func (e *FileUploadAndDownloadRouter) InitFileUploadAndDownloadRouter(Router *gin.RouterGroup, APPRouter *gin.RouterGroup) {
fileUploadAndDownloadRouter := Router.Group("fileUploadAndDownload")
appFileRouter := APPRouter.Group("appFile")
{
fileUploadAndDownloadRouter.POST("upload", exaFileUploadAndDownloadApi.UploadFile) // 上传文件
fileUploadAndDownloadRouter.POST("getFileList", exaFileUploadAndDownloadApi.GetFileList) // 获取上传文件列表
@@ -19,4 +20,7 @@ func (e *FileUploadAndDownloadRouter) InitFileUploadAndDownloadRouter(Router *gi
fileUploadAndDownloadRouter.POST("removeChunk", exaFileUploadAndDownloadApi.RemoveChunk) // 删除切片
fileUploadAndDownloadRouter.POST("importURL", exaFileUploadAndDownloadApi.ImportURL) // 导入URL
}
{
appFileRouter.POST("upload", exaFileUploadAndDownloadApi.UploadFile) // APP上传文件
}
}

View File

@@ -10,7 +10,6 @@ type NoticeRouter struct{}
// InitNoticeRouter 初始化 通知 路由信息
func (s *NoticeRouter) InitNoticeRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
notRouter := Router.Group("not").Use(middleware.OperationRecord())
notRouterWithoutRecord := Router.Group("not")
notRouterWithoutAuth := PublicRouter.Group("not")
{
notRouter.POST("createNotice", notApi.CreateNotice) // 新建通知
@@ -19,10 +18,8 @@ func (s *NoticeRouter) InitNoticeRouter(Router *gin.RouterGroup, PublicRouter *g
notRouter.PUT("updateNotice", notApi.UpdateNotice) // 更新通知
}
{
notRouterWithoutRecord.GET("findNotice", notApi.FindNotice) // 根据ID获取通知
notRouterWithoutRecord.GET("getNoticeList", notApi.GetNoticeList) // 获取通知列表
}
{
notRouterWithoutAuth.GET("findNotice", notApi.FindNotice) // 根据ID获取通知
notRouterWithoutAuth.GET("getNoticeList", notApi.GetNoticeList) // 获取通知列表
notRouterWithoutAuth.GET("getNoticePublic", notApi.GetNoticePublic) // 通知开放接口
}
}

View File

@@ -2,6 +2,7 @@ package app
import (
"fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/request"
@@ -59,7 +60,7 @@ func (s *OrderService) Create(o *app.Order) (*app.Order, error) {
return nil, err
}
} else {
err := global.GVA_DB.Table("lckt_vip").Select("price").Where("id = ?", o.ArticleId).Scan(&price).Error
err := global.GVA_DB.Table("lckt_vip").Select("price").Where("id = ?", o.VipId).Scan(&price).Error
if err != nil {
global.GVA_LOG.Error("查询VIP价格失败", zap.Error(err))
return nil, err
@@ -112,16 +113,22 @@ func (s *OrderService) BalancePay(p request.BalancePay) error {
global.GVA_LOG.Error("查询用户信息失败", zap.Error(err))
return err
}
// 将user.Balance转为int64类型进行比较
balance := int64(user.Balance)
if balance < order.Price/100 { // 订单价格是以分为单位存储的
global.GVA_LOG.Error("用户余额不足", zap.Int64("balance", balance), zap.Int64("order_price", order.Price))
// 将订单价格从分转换为元(保持精度)
orderPriceInYuan := float64(order.Price) / 100.0
// 检查用户余额是否足够使用float64进行比较避免精度丢失
if user.Balance < float32(orderPriceInYuan) {
global.GVA_LOG.Error("用户余额不足",
zap.Float32("balance", user.Balance),
zap.Float64("order_price_yuan", orderPriceInYuan),
zap.Int64("order_price_cent", order.Price))
return fmt.Errorf("用户余额不足")
}
// 扣除用户余额
balance -= order.Price / 100
err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).Update("balance", balance).Error
// 扣除用户余额(保持精度)
newBalance := user.Balance - float32(orderPriceInYuan)
err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).Update("balance", newBalance).Error
if err != nil {
global.GVA_LOG.Error("扣除用户余额失败", zap.Error(err))
return err

View File

@@ -5,6 +5,7 @@ import (
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/app"
"git.echol.cn/loser/lckt/model/app/vo"
common "git.echol.cn/loser/lckt/model/common/request"
"git.echol.cn/loser/lckt/model/user"
"git.echol.cn/loser/lckt/model/user/request"
utils2 "git.echol.cn/loser/lckt/utils"
@@ -221,3 +222,25 @@ func (u *AppUserService) BindPhone(p request.BindPhoneReq) (*user.User, error) {
return &userInfo, nil
}
func (u *AppUserService) GetTeacherList(p common.PageInfo) (list []vo.TeacherInfo, total int64, err error) {
limit := p.PageSize
offset := (p.Page - 1) * p.PageSize
db := global.GVA_DB.Model(&user.User{}).Where("user_type = ?", 2)
if p.Keyword != "" {
db = db.Where("nick_name LIKE ?", "%"+p.Keyword+"%")
}
err = db.Count(&total).Error
if err != nil {
global.GVA_LOG.Error("查询教师总数失败", zap.Error(err))
return nil, 0, err
}
err = db.Limit(limit).Offset(offset).Select("id, nick_name, avatar,des").Find(&list).Error
if err != nil {
global.GVA_LOG.Error("查询教师列表失败", zap.Error(err))
return nil, 0, err
}
return
}

View File

@@ -47,7 +47,7 @@ func (s ArticleService) APPGetArticleList(pageInfo request.GetList) (list []vo.A
limit := pageInfo.PageSize
offset := pageInfo.PageSize * (pageInfo.Page - 1)
db := global.GVA_DB.Model(&article.Article{})
db := global.GVA_DB.Model(&article.Article{}).Where("status = 1") // 只查询已发布的文章
err = db.Count(&total).Error
if err != nil {
return
@@ -75,7 +75,7 @@ func (s ArticleService) APPGetArticleList(pageInfo request.GetList) (list []vo.A
return
}
func (s ArticleService) APPGetArticle(id string, userId uint) (article *vo.ArticleVo, err error) {
func (s ArticleService) APPGetArticle(id string, userId int) (article *vo.ArticleVo, err error) {
err = global.GVA_DB.Table("article").Where("id = ?", id).First(&article).Error
if err != nil {
global.GVA_LOG.Error("获取文章失败", zap.Error(err))
@@ -94,7 +94,9 @@ func (s ArticleService) APPGetArticle(id string, userId uint) (article *vo.Artic
global.GVA_LOG.Error("查询用户购买记录失败", zap.Error(err))
return nil, err
}
if count == 0 {
// 如果count = 0 或者 TeacherId 不等于 userId表示用户没有购买过该文章
if count == 0 && article.TeacherId != userId {
// 用户没有购买过隐藏Content
article.Content = ""
article.IsBuy = 0 // 设置为未购买
@@ -103,3 +105,86 @@ func (s ArticleService) APPGetArticle(id string, userId uint) (article *vo.Artic
return
}
// AppPush app端推送文章
func (s ArticleService) AppPush(req article.Article) (err error) {
req.Status = 2 // 默认状态为待审核
if req.ID != 0 {
err = global.GVA_DB.Model(&article.Article{}).Where("id = ?", req.ID).Updates(&req).Error
} else {
err = global.GVA_DB.Create(&req).Error
}
if err != nil {
global.GVA_LOG.Error("推送文章失败", zap.Error(err))
return err
}
return nil
}
func (s ArticleService) GetMyArticleList(req request.GetList) (list []article.Article, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Page - 1)
db := global.GVA_DB.Model(&article.Article{}).Where("teacher_id = ?", req.TeacherId)
if req.Title != "" {
db = db.Where("title LIKE ?", "%"+req.Title+"%")
}
if req.CategoryId != 0 {
db = db.Where("category_id = ?", req.CategoryId)
}
if req.Status != 0 {
db = db.Where("status = ?", req.Status)
}
err = db.Count(&total).Error
if err != nil {
return nil, 0, err
}
err = db.Limit(limit).Offset(offset).Find(&list).Error
return list, total, err
}
func (s ArticleService) GetBuyList(p request.GetList) (articles []article.Article, total int64, err error) {
buyIds := make([]int, 0)
// 获取用户购买的文章ID列表
err = global.GVA_DB.Model(&app.Order{}).Where("user_id = ? AND status = 2", p.UserId).Select("article_id").Find(&buyIds).Error
if err != nil {
global.GVA_LOG.Error("查询用户购买记录失败", zap.Error(err))
return nil, 0, err
}
if len(buyIds) == 0 {
// 如果没有购买记录,直接返回空列表
return []article.Article{}, 0, nil
}
db := global.GVA_DB.Model(&article.Article{}).Where("id IN ?", buyIds)
if p.Title != "" {
db = db.Where("title LIKE ?", "%"+p.Title+"%")
}
err = db.Count(&total).Error
if err != nil {
global.GVA_LOG.Error("查询购买文章总数失败", zap.Error(err))
return nil, 0, err
}
err = db.Limit(p.PageSize).Offset(p.PageSize * (p.Page - 1)).Find(&articles).Error
if err != nil {
global.GVA_LOG.Error("查询购买文章列表失败", zap.Error(err))
return nil, 0, err
}
return articles, total, nil
}
func (s ArticleService) AppDelete(id string) error {
err := global.GVA_DB.Delete(&article.Article{}, "id = ?", id).Error
if err != nil {
global.GVA_LOG.Error("删除文章失败", zap.Error(err))
return err
}
return nil
}

View File

@@ -2,8 +2,11 @@ package example
import (
"errors"
"git.echol.cn/loser/lckt/utils"
"mime/multipart"
"strconv"
"strings"
"time"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/example"
@@ -94,11 +97,15 @@ func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.ExaAtt
func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string, classId int) (file example.ExaFileUploadAndDownload, err error) {
oss := upload.NewOss()
s := strings.Split(header.Filename, ".")
if classId == 2 {
header.Filename = utils.GenerateRandomString(12) + "_" + strconv.FormatInt(time.Now().Unix(), 10) + "." + s[len(s)-1]
}
filePath, key, uploadErr := oss.UploadFile(header)
if uploadErr != nil {
return file, uploadErr
}
s := strings.Split(header.Filename, ".")
f := example.ExaFileUploadAndDownload{
Url: filePath,
Name: header.Filename,

View File

@@ -21,3 +21,16 @@ func GenerateInviteCode(userID uint) string {
}
return code
}
func GenerateRandomString(length int) string {
rand.Seed(time.Now().UnixNano())
// 拼接用户ID和随机数
data := fmt.Sprintf("%d%d", 6, rand.Intn(1000000))
hash := md5.Sum([]byte(data))
code := ""
for i := 0; i < 12; i++ {
// 取哈希的前6位每位映射到字符集
code += string(charset[int(hash[i])%len(charset)])
}
return code
}

View File

@@ -7,7 +7,6 @@ import (
"time"
"git.echol.cn/loser/lckt/global"
systemReq "git.echol.cn/loser/lckt/model/user/request"
"github.com/gin-gonic/gin"
)
@@ -54,28 +53,25 @@ func GetToken(c *gin.Context) string {
return token
}
func GetClaims(c *gin.Context) (*systemReq.CustomClaims, error) {
func GetClaims(c *gin.Context) (*request.CustomClaims, error) {
token := GetToken(c)
j := NewUserJWT()
claims, err := j.ParseToken(token)
if err != nil {
global.GVA_LOG.Error("从Gin的Context中获取从jwt解析信息失败, 请检查请求头是否存在Authorization且claims是否为规定结构")
global.GVA_LOG.Error(err.Error())
}
return claims, err
}
// GetUserID 从Gin的Context中获取从jwt解析出来的用户ID
func GetUserID(c *gin.Context) uint {
if claims, exists := c.Get("claims"); !exists {
if cl, err := GetClaims(c); err != nil {
return 0
} else {
return cl.BaseClaims.ID
}
if cl, err := GetClaims(c); err != nil {
return 0
} else {
waitUse := claims.(*systemReq.CustomClaims)
return waitUse.BaseClaims.ID
return cl.BaseClaims.ID
}
}
// GetNickName 从Gin的Context中获取从jwt解析出来的用户名
@@ -87,7 +83,7 @@ func GetNickName(c *gin.Context) string {
return cl.NickName
}
} else {
waitUse := claims.(*systemReq.CustomClaims)
waitUse := claims.(*request.CustomClaims)
return waitUse.NickName
}
}