diff --git a/internal/api/v1/cptask_req.go b/internal/api/v1/cptask_req.go index d5c9adc..611bd2d 100644 --- a/internal/api/v1/cptask_req.go +++ b/internal/api/v1/cptask_req.go @@ -1,6 +1,11 @@ package v1 -import "github.com/gogf/gf/v2/frame/g" +import ( + "likedream-goframe/internal/application/cpdaily/task/formtype" + "likedream-goframe/internal/ent/cptask" + + "github.com/gogf/gf/v2/frame/g" +) type GetRecentTasksReq struct { g.Meta `path:"/task/recent" method:"get" tags:"CpTask" summary:"获取最近任务"` @@ -23,26 +28,31 @@ type CpTaskListReq struct { } type GetCpTaskFormReq struct { - g.Meta `path:"/task" method:"get" tags:"CpTask" summary:"获取任务表单"` + g.Meta `path:"/task/form/{id}" method:"get" tags:"CpTask" summary:"获取任务表单"` Id int64 `json:"id"` } -type UpdateCpTaskFormReq struct { - g.Meta `path:"/task" method:"put" tags:"CpTask" summary:"更新任务表单"` +type PutCpTaskFormReq struct { + g.Meta `path:"/task/form/{id}" method:"put" tags:"CpTask" summary:"更新任务表单"` + Id int64 `json:"id"` + Forms []*formtype.CpTaskFormItem `json:"forms" v:"required$表单信息不能为空"` +} + +type GetCpTaskExecuteReq struct { + g.Meta `path:"/task/execute/{id}" method:"get" tags:"CpTask" summary:"执行任务参数"` Id int64 `json:"id"` } -type GetCoTaskExecuteReq struct { - g.Meta `path:"/task/execute" method:"get" tags:"CpTask" summary:"执行任务参数"` - Id int64 `json:"id"` -} - -type UpdateCpTaskExecuteReq struct { - g.Meta `path:"/task/execute" method:"put" tags:"CpTask" summary:"更新执行任务参数"` - Id int64 `json:"id"` +type PostCpTaskExecuteReq struct { + g.Meta `path:"/task/execute/{id}" method:"post" tags:"CpTask" summary:"更新执行任务参数"` + Id int64 `json:"id"` + Frequency int `json:"frequency"` + NotificationTypeNone cptask.NotificationType `json:"notificationType"` + ExecuteHour int `json:"executeHour"` + ExecuteMinute int `json:"executeMinute"` } type DeleteTaskReq struct { - g.Meta `path:"/task" method:"delete" tags:"CpTask" summary:"删除任务"` + g.Meta `path:"/task/{id}" method:"delete" tags:"CpTask" summary:"删除任务"` Id int64 `json:"id"` } diff --git a/internal/application/cpdaily/login/cas.go b/internal/application/cpdaily/login/cas.go index b0049c0..ab41bb5 100644 --- a/internal/application/cpdaily/login/cas.go +++ b/internal/application/cpdaily/login/cas.go @@ -44,7 +44,11 @@ func NewCas(ctx context.Context) CpdailyLogin { func (c *CpdailyCasLogin) GetCookieJar() http.CookieJar { return c.client.CookieJar() } - +func (c *CpdailyCasLogin) GetCookieString() string { + cookieJar := c.GetCookieJar() + serializableJar := cookieJar.(*cookiejar.PersistCookieJar) + return serializableJar.Serialize() +} func (c *CpdailyCasLogin) GetClient() *resty.Client { return c.client.SetRedirectPolicy(resty.NoRedirectPolicy()) } diff --git a/internal/application/cpdaily/login/common.go b/internal/application/cpdaily/login/common.go index 41037d5..dbc415f 100644 --- a/internal/application/cpdaily/login/common.go +++ b/internal/application/cpdaily/login/common.go @@ -3,6 +3,8 @@ package login import ( "context" "fmt" + "likedream-goframe/internal/application/cookiejar" + "likedream-goframe/internal/application/httpclient" "likedream-goframe/internal/common/errors" "likedream-goframe/internal/utils" "strings" @@ -79,6 +81,35 @@ func ActivateSession(ctx context.Context, host string, client *resty.Client) { client = client.SetRedirectPolicy(resty.NoRedirectPolicy()) } +func CookieEnabled(ctx context.Context, host string, client *resty.Client) bool { + client = client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20)) + defer func() { + client = client.SetRedirectPolicy(resty.NoRedirectPolicy()) + }() + host = strings.TrimSuffix(host, "/") + sessionResponse, err := client.R(). + WithContext(ctx). + SetBody(map[string]any{}). + Post(fmt.Sprintf("%s/wec-counselor-attendance-apps/student/attendance/getStuSignInfosByWeekMonth", host)) + + utils.OkOrPanic(err) + defer sessionResponse.Body.Close() + + if !sessionResponse.IsSuccess() { + return sessionResponse.StatusCode() == 405 + } + responseContentType := sessionResponse.Header().Get("Content-Type") + return strings.Contains(responseContentType, "json") +} + +func GetClient(ctx context.Context, cookie string) *resty.Client { + cookieJar := cookiejar.NewPersistCookieJar() + cookieJar.Deserialize(cookie) + client := httpclient.Cpdaily.Clone(ctx) + client.SetCookieJar(cookieJar) + return client +} + func SendDynamicCodeByPhone(ctx context.Context, username string, host string, client *resty.Client) { host = strings.TrimSuffix(host, "/") response, err := client.R(). diff --git a/internal/application/cpdaily/login/iap.go b/internal/application/cpdaily/login/iap.go index 651d05a..a11b0b5 100644 --- a/internal/application/cpdaily/login/iap.go +++ b/internal/application/cpdaily/login/iap.go @@ -3,8 +3,6 @@ package login import ( "context" "fmt" - "github.com/samber/oops" - "github.com/tidwall/gjson" "io" "likedream-goframe/internal/application/cookiejar" "likedream-goframe/internal/application/cpdaily/school" @@ -13,6 +11,9 @@ import ( "likedream-goframe/internal/utils" "net/http" "net/url" + + "github.com/samber/oops" + "github.com/tidwall/gjson" "resty.dev/v3" ) @@ -151,7 +152,11 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School func (c *CpdailyIapLogin) GetCookieJar() http.CookieJar { return c.client.CookieJar() } - +func (c *CpdailyIapLogin) GetCookieString() string { + cookieJar := c.GetCookieJar() + serializableJar := cookieJar.(*cookiejar.PersistCookieJar) + return serializableJar.Serialize() +} func getIapValidLoginUrl(schoolDetail *school.SchoolDetailInfo) string { loginUrl := utils.GetFirstNotEmptyString(schoolDetail.WecloudUrl, schoolDetail.AmpUrl, schoolDetail.AmpUrl2, schoolDetail.AmpUrl3) diff --git a/internal/application/cpdaily/login/interface.go b/internal/application/cpdaily/login/interface.go index 9c9993e..c46a183 100644 --- a/internal/application/cpdaily/login/interface.go +++ b/internal/application/cpdaily/login/interface.go @@ -4,11 +4,13 @@ import ( "context" "likedream-goframe/internal/application/cpdaily/school" "net/http" + "resty.dev/v3" ) type CpdailyLogin interface { Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) GetCookieJar() http.CookieJar + GetCookieString() string GetClient() *resty.Client } diff --git a/internal/controller/cpdaily/cptask_ctrlV1.go b/internal/controller/cpdaily/cptask_ctrlV1.go index adde939..3a1765e 100644 --- a/internal/controller/cpdaily/cptask_ctrlV1.go +++ b/internal/controller/cpdaily/cptask_ctrlV1.go @@ -26,6 +26,29 @@ func (c CpTaskServiceV1) AddTask(ctx context.Context, req *v1.AddTaskReq) (res * return service.Global.CpTask.AddTask(ctx, req), nil } +func (c CpTaskServiceV1) TaskFormData(ctx context.Context, req *v1.GetCpTaskFormReq) (res *utils.Response[any], err error) { + defer utils.Pass(&err) + return service.Global.CpTask.TaskFormData(ctx, req), nil +} +func (c CpTaskServiceV1) PutFormData(ctx context.Context, req *v1.PutCpTaskFormReq) (res *utils.Response[any], err error) { + defer utils.Pass(&err) + return service.Global.CpTask.PutTaskFormData(ctx, req), nil +} + +func (c CpTaskServiceV1) TaskExecute(ctx context.Context, req *v1.GetCpTaskExecuteReq) (res *utils.Response[any], err error) { + defer utils.Pass(&err) + return service.Global.CpTask.TaskExecute(ctx, req), nil +} + +func (c CpTaskServiceV1) PostExecute(ctx context.Context, req *v1.PostCpTaskExecuteReq) (res *utils.Response[any], err error) { + defer utils.Pass(&err) + return service.Global.CpTask.PostExecute(ctx, req), nil +} + +func (c CpTaskServiceV1) Delete(ctx context.Context, req *v1.DeleteTaskReq) (res *utils.Response[any], err error) { + defer utils.Pass(&err) + return service.Global.CpTask.Delete(ctx, req), nil +} func NewCpTaskCtrlV1() ICpTaskCtrlV1 { return &CpTaskServiceV1{} } diff --git a/internal/controller/cpdaily/i_cptask_ctrl.go b/internal/controller/cpdaily/i_cptask_ctrl.go index ad2d949..605ce3f 100644 --- a/internal/controller/cpdaily/i_cptask_ctrl.go +++ b/internal/controller/cpdaily/i_cptask_ctrl.go @@ -11,5 +11,9 @@ type ICpTaskCtrlV1 interface { GetRecentTasks(ctx context.Context, req *v1.GetRecentTasksReq) (*utils.Response[[]string], error) AddTask(ctx context.Context, req *v1.AddTaskReq) (res *utils.Response[any], err error) List(ctx context.Context, req *v1.CpTaskListReq) (res *utils.Response[[]*ent.CpTask], err error) - TaskFormData(ctx context.Context, req *v1.TaskFormDataReq) (res *utils.Response[any], err error) + TaskFormData(ctx context.Context, req *v1.GetCpTaskFormReq) (res *utils.Response[any], err error) + PutFormData(ctx context.Context, req *v1.PutCpTaskFormReq) (res *utils.Response[any], err error) + TaskExecute(ctx context.Context, req *v1.GetCpTaskExecuteReq) (res *utils.Response[any], err error) + PostExecute(ctx context.Context, req *v1.PostCpTaskExecuteReq) (res *utils.Response[any], err error) + Delete(ctx context.Context, req *v1.DeleteTaskReq) (res *utils.Response[any], err error) } diff --git a/internal/service/cpdaily/cptask.go b/internal/service/cpdaily/cptask.go index 919a0b3..43081c0 100644 --- a/internal/service/cpdaily/cptask.go +++ b/internal/service/cpdaily/cptask.go @@ -2,8 +2,12 @@ package cpdaily import ( "context" + "encoding/json" v1 "likedream-goframe/internal/api/v1" + "likedream-goframe/internal/application/cpdaily/login" + "likedream-goframe/internal/application/cpdaily/school" "likedream-goframe/internal/application/cpdaily/task" + "likedream-goframe/internal/application/cpdaily/task/formtype" errors2 "likedream-goframe/internal/common/errors" "likedream-goframe/internal/db" "likedream-goframe/internal/ent" @@ -19,10 +23,19 @@ type CpTaskServiceV1 struct { func (v CpTaskServiceV1) GetRecentTasks(ctx context.Context, req *v1.GetRecentTasksReq) *utils.Response[[]string] { user := utils.GetUserInfoFromCtx(ctx) // 检测是否属于自己的cpuser - cpUser := db.Global.CpUser.Query().Where(cpuser.ID(req.UserId)).WithSysuser().OnlyX(ctx) - if cpUser.Edges.Sysuser == nil || cpUser.Edges.Sysuser.ID != user.ID { + cpUser := db.Global.CpUser.Query().Where(cpuser.ID(req.UserId), cpuser.HasSysuserWith(sysuser.ID(user.ID))).WithSysuser().WithSchool().OnlyX(ctx) + if cpUser.Edges.Sysuser == nil { utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) } + schoolDetail := school.GetSchoolDetail(ctx, cpUser.SchoolID) + client := login.GetClient(ctx, cpUser.Cookie) + if !login.CookieEnabled(ctx, schoolDetail.WecloudUrl, client) { + loginExecutor := login.GetCpdailyLoginBySchoolInfo(ctx, &schoolDetail) + loginExecutor.Login(ctx, &schoolDetail, cpUser.Username, cpUser.Password) + cookie := loginExecutor.GetCookieString() + db.Global.CpUser.UpdateOne(cpUser).SetCookie(cookie).ExecX(ctx) + client = login.GetClient(ctx, cookie) + } taskService := task.GetCpdailyTaskService(ctx, req.Type, cpUser.Cookie, cpUser.SchoolID) titleList := taskService.GetRecentTaskTitle(ctx) return utils.SuccessWithData[[]string](titleList) @@ -31,8 +44,8 @@ func (v CpTaskServiceV1) GetRecentTasks(ctx context.Context, req *v1.GetRecentTa func (v CpTaskServiceV1) AddTask(ctx context.Context, req *v1.AddTaskReq) *utils.Response[any] { user := utils.GetUserInfoFromCtx(ctx) // 检测是否属于自己的cpuser - cpUser := db.Global.CpUser.Query().Where(cpuser.ID(req.UserId)).WithSysuser().OnlyX(ctx) - if cpUser.Edges.Sysuser == nil || cpUser.Edges.Sysuser.ID != user.ID { + cpUser := db.Global.CpUser.Query().Where(cpuser.ID(req.UserId), cpuser.HasSysuserWith(sysuser.ID(user.ID))).WithSysuser().OnlyX(ctx) + if cpUser.Edges.Sysuser == nil { utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) } db.Global.CpTask.Create(). @@ -69,6 +82,113 @@ func (v CpTaskServiceV1) List(ctx context.Context, req *v1.CpTaskListReq) *utils return utils.SuccessWithTotal[[]*ent.CpTask](data, total) } +func (v CpTaskServiceV1) TaskFormData(ctx context.Context, req *v1.GetCpTaskFormReq) *utils.Response[any] { + user := utils.GetUserInfoFromCtx(ctx) + // 检测是否属于自己的cpTask + cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id), + cptask.HasSysUserWith(sysuser.ID(user.ID))). + WithCpUser().WithSysUser().OnlyX(ctx) + cpUser := cpTask.Edges.CpUser + if cpUser == nil { + utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) + } + if cpTask.FormData != "" { + var formData []*formtype.CpTaskFormItem + err := json.Unmarshal([]byte(cpTask.FormData), &formData) + utils.OkOrPanic(err) + return utils.SuccessWithData[any](formData) + } + schoolDetail := school.GetSchoolDetail(ctx, cpTask.Edges.CpUser.SchoolID) + client := login.GetClient(ctx, cpUser.Cookie) + if !login.CookieEnabled(ctx, schoolDetail.WecloudUrl, client) { + loginExecutor := login.GetCpdailyLoginBySchoolInfo(ctx, &schoolDetail) + loginExecutor.Login(ctx, &schoolDetail, cpUser.Username, cpUser.Password) + cookie := loginExecutor.GetCookieString() + db.Global.CpUser.UpdateOne(cpUser).SetCookie(cookie).ExecX(ctx) + client = login.GetClient(ctx, cookie) + } + taskService := task.GetCpdailyTaskService(ctx, string(cpTask.TaskType), cpUser.Cookie, cpUser.SchoolID) + formMap := taskService.QueryFormByTitle(ctx, cpTask.Title) + formDetail := taskService.ParseFormJson(ctx, formMap) + return utils.SuccessWithData[any](formDetail) +} + +func (v CpTaskServiceV1) PutTaskFormData(ctx context.Context, req *v1.PutCpTaskFormReq) *utils.Response[any] { + user := utils.GetUserInfoFromCtx(ctx) + // 检测是否属于自己的cpTask + cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id), + cptask.HasSysUserWith(sysuser.ID(user.ID))). + WithCpUser().WithSysUser().OnlyX(ctx) + cpUser := cpTask.Edges.CpUser + if cpUser == nil { + utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) + } + update := db.Global.CpTask.UpdateOne(cpTask) + marshal, _ := json.Marshal(req.Forms) + update.SetFormData(string(marshal)) + if cpTask.Step == cptask.StepForm { + update.SetStep(cptask.StepExecute) + } + update.SaveX(ctx) + return utils.SuccessWithMsg("保存成功") +} + +func (v CpTaskServiceV1) TaskExecute(ctx context.Context, req *v1.GetCpTaskExecuteReq) *utils.Response[any] { + user := utils.GetUserInfoFromCtx(ctx) + // 检测是否属于自己的cpTask + cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id), + cptask.HasSysUserWith(sysuser.ID(user.ID))). + WithCpUser().WithSysUser().OnlyX(ctx) + cpUser := cpTask.Edges.CpUser + if cpUser == nil { + utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) + } + return utils.SuccessWithData[any](cpTask) +} + +func (v CpTaskServiceV1) PostExecute(ctx context.Context, req *v1.PostCpTaskExecuteReq) *utils.Response[any] { + user := utils.GetUserInfoFromCtx(ctx) + // 检测是否属于自己的cpTask + cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id), + cptask.HasSysUserWith(sysuser.ID(user.ID))). + WithCpUser().WithSysUser().OnlyX(ctx) + cpUser := cpTask.Edges.CpUser + if cpUser == nil { + utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) + } + if cpTask.Step < cptask.StepExecute { + utils.OkOrPanic(errors2.NewBusinessError(400, "请先完成前置参数设置")) + } + pointConsume := req.Frequency - cpTask.Frequency + if pointConsume < 0 { + utils.OkOrPanic(errors2.NewBusinessError(400, "执行次数应不低于原有次数")) + } + //todo 扣积分 + db.Global.CpTask.UpdateOne(cpTask). + SetStep(cptask.StepComplete). + SetFrequency(req.Frequency). + SetTaskStatus(cptask.TaskStatusNotStart). + SetNotificationType(req.NotificationTypeNone). + SetExecuteHour(req.ExecuteHour). + SetExecuteMinute(req.ExecuteMinute). + Exec(ctx) + return utils.SuccessWithMsg("更新成功") +} + +func (v CpTaskServiceV1) Delete(ctx context.Context, req *v1.DeleteTaskReq) *utils.Response[any] { + user := utils.GetUserInfoFromCtx(ctx) + // 检测是否属于自己的cpTask + cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id), + cptask.HasSysUserWith(sysuser.ID(user.ID))). + WithCpUser().WithSysUser().OnlyX(ctx) + cpUser := cpTask.Edges.CpUser + if cpUser == nil { + utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号")) + } + db.Global.CpTask.DeleteOne(cpTask).ExecX(ctx) + return utils.SuccessWithMsg("删除成功") +} + func NewCpTaskServiceV1() *CpTaskServiceV1 { return &CpTaskServiceV1{} } diff --git a/internal/service/cpdaily/cpuser.go b/internal/service/cpdaily/cpuser.go index 4d25d92..6f84e59 100644 --- a/internal/service/cpdaily/cpuser.go +++ b/internal/service/cpdaily/cpuser.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" v1 "likedream-goframe/internal/api/v1" - "likedream-goframe/internal/application/cookiejar" "likedream-goframe/internal/application/cpdaily/login" "likedream-goframe/internal/application/cpdaily/school" "likedream-goframe/internal/db" @@ -29,13 +28,11 @@ func (v CpUserServiceV1) Add(ctx context.Context, req *v1.CpUserAddReq) *utils.R schoolDetail := school.GetSchoolDetail(ctx, req.SchoolId) loginExecutor := login.GetCpdailyLoginBySchoolInfo(ctx, &schoolDetail) loginExecutor.Login(ctx, &schoolDetail, req.Username, req.Password) - cookieJar := loginExecutor.GetCookieJar() - serializableJar := cookieJar.(*cookiejar.PersistCookieJar) db.Global.CpUser.Create(). SetUsername(req.Username). SetPassword(req.Password). SetEmail(req.Email). - SetCookie(serializableJar.Serialize()). + SetCookie(loginExecutor.GetCookieString()). SetMobileCode(utils.UUID()). SetMobileType(cpuser.MobileTypeRandom). SetSchoolID(req.SchoolId). @@ -56,13 +53,11 @@ func (v CpUserServiceV1) ConfirmAdd(ctx context.Context, req *v1.CpUserConfirmAd json.Unmarshal([]byte(schoolInfo), &sc) loginExecutor := login.GetCpdailyLoginBySchoolInfo(ctx, &sc) loginExecutor.(*login.CpdailyCasLogin).ConfirmAdd(ctx, &sc, req.Username, req.Password, req.Key, req.DynamicCode) - cookieJar := loginExecutor.GetCookieJar() - serializableJar := cookieJar.(*cookiejar.PersistCookieJar) db.Global.CpUser.Create(). SetUsername(req.Username). SetPassword(req.Password). SetEmail(req.Email). - SetCookie(serializableJar.Serialize()). + SetCookie(loginExecutor.GetCookieString()). SetMobileCode(utils.UUID()). SetMobileType(cpuser.MobileTypeRandom). SetSchoolID(sc.Id).