290 lines
8.3 KiB
Go
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{}
|
|
}
|