Files
likedream/internal/service/course/course.go
T
2025-12-22 17:25:51 +08:00

290 lines
8.3 KiB
Go

package course
import (
"context"
"encoding/base64"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/xuexitong"
"likedream-goframe/internal/application/xuexitong/entity"
"likedream-goframe/internal/common/cst"
db2 "likedream-goframe/internal/common/db"
errors2 "likedream-goframe/internal/common/errors"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/sysconfig"
"likedream-goframe/internal/ent/sysuser"
"likedream-goframe/internal/framework/logger"
"likedream-goframe/internal/service/balance"
"likedream-goframe/internal/utils"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"resty.dev/v3"
)
type CourseServiceV1 struct {
}
// List 获取刷课列表
func (s CourseServiceV1) List(ctx context.Context, req *v1.CourseListReq) *utils.Response[[]*ent.CourseEntity] {
user := utils.GetUserInfoFromCtx(ctx)
query := db2.Global.CourseEntity.Query().
Where(courseentity.HasSysUserWith(sysuser.ID(user.ID)), courseentity.TypeEQ(courseentity.TypeXxt)).
Order(ent.Desc(courseentity.FieldCreateTime))
if req.Type != "" {
query.Where(courseentity.TypeEQ(req.Type))
}
if req.Username != "" {
query.Where(courseentity.UsernameContains(req.Username))
}
if req.Status != "" {
query.Where(courseentity.StatusEQ(req.Status))
}
total := query.CountX(ctx)
var data []*ent.CourseEntity
if total > 0 {
query.Limit(req.Size).Offset((req.Page - 1) * req.Size)
data = query.AllX(ctx)
// 清空密码字段
for _, item := range data {
item.Password = ""
}
}
return utils.SuccessWithTotal[[]*ent.CourseEntity](data, total)
}
// AddCourse 添加刷课
func (s CourseServiceV1) AddCourse(ctx context.Context, req *v1.AddCourseReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
// 登录学习通获取节点信息
loginClient := xuexitong.NewXuexitongLogin(ctx)
err := loginClient.Login(ctx, req.Username, req.Password, 3)
if err != nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "登录失败,请检查账号密码").WithInnerError(err))
}
client := loginClient.GetClient()
courseService := xuexitong.NewXuexitongCourse(client)
var targetCourse *entity.XueXiTCourse
// 获取课程章节列表
courseList := courseService.PullCourseList(ctx)
for _, course := range courseList {
if course.CourseID == req.CourseID {
targetCourse = &course
break
}
}
if targetCourse == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "未找到该课程"))
}
basePoint, err := decimal.NewFromString(db2.Global.SysConfig.Query().Where(sysconfig.KeyEQ(cst.ConfigRedisXxt)).OnlyX(ctx).Value)
utils.OkOrPanic(err)
if user.Edges.Agent != nil {
basePoint = basePoint.Mul(user.Edges.Agent.Rate)
}
// 先扣款
balance.BalanceService.ChangeUserBalance(ctx, user.ID, basePoint.Neg(), "学习通刷课")
// 添加课程时不获取任务点统计,由定时任务处理
db2.Global.CourseEntity.Create().
SetUserID(user.ID).
SetUsername(req.Username).
SetPassword(req.Password).
SetType(req.Type).
SetCourseName(req.CourseName).
SetCourseID(req.CourseID).
SetNeed(req.Need).
SetViolence(req.Violence).
SetNillableDesc(&req.Desc).
SetStatus(courseentity.StatusNotStart).
SetTaskCount(0).
SetSuccessCount(0).
SaveX(ctx)
return utils.SuccessWithMsg("添加成功")
}
// UpdatePassword 修改刷课密码
func (s CourseServiceV1) UpdatePassword(ctx context.Context, req *v1.UpdateCoursePasswordReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
// 检查课程是否属于当前用户
course := db2.Global.CourseEntity.Query().
Where(
courseentity.ID(req.ID),
courseentity.HasSysUserWith(sysuser.ID(user.ID)),
).
OnlyX(ctx)
if course == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "课程不存在或无权限操作"))
}
db2.Global.CourseEntity.UpdateOne(course).
SetPassword(req.Password).
SaveX(ctx)
return utils.SuccessWithMsg("密码修改成功")
}
// RestartCourse 重刷课程
func (s CourseServiceV1) RestartCourse(ctx context.Context, req *v1.RestartCourseReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
// 检查课程是否属于当前用户
course := db2.Global.CourseEntity.Query().
Where(
courseentity.ID(req.ID),
courseentity.HasSysUserWith(sysuser.ID(user.ID)),
).
OnlyX(ctx)
if course == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "课程不存在或无权限操作"))
}
// 只有完成状态的课程才能重刷
if course.Status != courseentity.StatusDone {
utils.OkOrPanic(errors2.NewBusinessError(400, "只有已完成的课程才能重刷"))
}
// 重置课程状态和计数
db2.Global.CourseEntity.UpdateOne(course).
SetStatus(courseentity.StatusNotStart).
SetTaskCount(0).
SetSuccessCount(0).
SetExamState(courseentity.ExamStateNotStart).
SaveX(ctx)
return utils.SuccessWithMsg("重刷成功")
}
// PauseCourse 暂停刷课
func (s CourseServiceV1) PauseCourse(ctx context.Context, req *v1.PauseCourseReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
// 检查课程是否属于当前用户
course := db2.Global.CourseEntity.Query().
Where(
courseentity.ID(req.ID),
courseentity.HasSysUserWith(sysuser.ID(user.ID)),
).
OnlyX(ctx)
if course == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "课程不存在或无权限操作"))
}
// 根据当前状态切换暂停/继续
var newStatus courseentity.Status
var msg string
if course.Status == courseentity.StatusPause {
// 如果当前是暂停状态,则恢复到等待状态
newStatus = courseentity.StatusWaiting
msg = "已恢复刷课"
} else {
// 否则暂停
newStatus = courseentity.StatusPause
msg = "已暂停刷课"
}
db2.Global.CourseEntity.UpdateOne(course).
SetStatus(newStatus).
SaveX(ctx)
return utils.SuccessWithMsg(msg)
}
// DeleteCourse 删除刷课
func (s CourseServiceV1) DeleteCourse(ctx context.Context, req *v1.DeleteCourseReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
// 检查课程是否属于当前用户
course := db2.Global.CourseEntity.Query().
Where(
courseentity.ID(req.ID),
courseentity.HasSysUserWith(sysuser.ID(user.ID)),
).
OnlyX(ctx)
if course == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "课程不存在或无权限操作"))
}
db2.Global.CourseEntity.DeleteOne(course).ExecX(ctx)
return utils.SuccessWithMsg("删除成功")
}
// downloadImageToBase64 下载图片并转换为 base64
func (s CourseServiceV1) downloadImageToBase64(ctx context.Context, client *resty.Client, imageURL string) string {
if imageURL == "" {
return ""
}
// 下载图片,使用登录后的 client,并设置 User-Agent
resp, err := client.R().
SetHeader("User-Agent", xuexitong.GetUA("web")).
Get(imageURL)
if err != nil {
logger.Logger.Warn("下载课程图片失败", zap.String("url", imageURL), zap.Error(err))
return ""
}
// 检查响应状态码
if resp.StatusCode() != 200 {
logger.Logger.Warn("下载课程图片失败,状态码异常",
zap.String("url", imageURL),
zap.Int("statusCode", resp.StatusCode()))
return ""
}
// 读取图片数据
imageData := resp.Bytes()
if len(imageData) == 0 {
logger.Logger.Warn("下载的课程图片为空", zap.String("url", imageURL))
return ""
}
// 转换为 base64
base64Str := base64.StdEncoding.EncodeToString(imageData)
// 根据 Content-Type 添加 Data URL 前缀
contentType := resp.Header().Get("Content-Type")
if contentType == "" {
contentType = "image/jpeg" // 默认使用 jpeg
}
return "data:" + contentType + ";base64," + base64Str
}
// QueryCourses 查询课程列表
func (s CourseServiceV1) QueryCourses(ctx context.Context, req *v1.QueryCourseReq) *utils.Response[[]entity.XueXiTCourse] {
// 创建学习通登录客户端
loginClient := xuexitong.NewXuexitongLogin(ctx)
// 登录学习通
err := loginClient.Login(ctx, req.Username, req.Password, 3)
if err != nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "登录失败,请检查账号密码").WithInnerError(err))
}
// 获取客户端
client := loginClient.GetClient()
// 创建课程服务
courseService := xuexitong.NewXuexitongCourse(client)
// 拉取课程列表
courses := courseService.PullCourseListWithImage(ctx)
return utils.SuccessWithData[[]entity.XueXiTCourse](courses)
}
func NewCourseServiceV1() *CourseServiceV1 {
return &CourseServiceV1{}
}