✨ 今日校园任务管理对接

This commit is contained in:
sugarBecomer
2025-11-01 14:42:07 +08:00
parent 5135f278ab
commit b46f4c2c98
14 changed files with 105 additions and 44 deletions
+8 -1
View File
@@ -30,4 +30,11 @@ smtpUsername = "demo@likedream.xyz"
smtpPassword = "baca122..."
# 发件人邮箱和显示名称
fromEmail = "demo@likedream.xyz"
fromName = "今日校园系统"
fromName = "今日校园系统"
[s3]
accessKey = "dzckoE1UlZe8XFItWQDI"
secretKey = "f8UZdnDohtwZk5HInT1Cw1QCztSGnFuPPqfSSjzu"
baseUrl = "http://s3.out"
bucket = "likedream"
+1 -1
View File
@@ -6,7 +6,7 @@ import (
)
type UploadImageReq struct {
g.Meta `path:"/image" method:"post" tags:"Image" summary:"上传图片"`
g.Meta `path:"/image/upload" method:"post" tags:"Image" summary:"上传图片"`
File *ghttp.UploadFile `json:"file" v:"required#请上传图片"`
}
+1 -1
View File
@@ -2,5 +2,5 @@ package v1
type UploadImageRes struct {
FileKey string `json:"key"`
FileSize int64 `json:"size"`
FileSize *int64 `json:"size"`
}
+23 -12
View File
@@ -20,11 +20,12 @@ type AddTaskReq struct {
Title string `json:"title"`
}
type CpTaskListReq struct {
g.Meta `path:"/task" method:"get" tags:"CpTask" summary:"获取cp任务列表"`
Title string `json:"title"`
Username string `json:"username"`
Page int `json:"page" d:"1"`
Size int `json:"size" d:"10"`
g.Meta `path:"/task" method:"get" tags:"CpTask" summary:"获取cp任务列表"`
TaskType cptask.TaskType `json:"type"`
Username string `json:"username"`
TaskStatus cptask.TaskStatus `json:"task_status"`
Page int `json:"page" d:"1"`
Size int `json:"size" d:"10"`
}
type GetCpTaskFormReq struct {
@@ -35,7 +36,7 @@ type GetCpTaskFormReq struct {
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$表单信息不能为空"`
Forms []*formtype.CpTaskFormItem `json:"forms" v:"required#表单信息不能为空"`
}
type GetCpTaskExecuteReq struct {
@@ -44,15 +45,25 @@ type GetCpTaskExecuteReq struct {
}
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"`
g.Meta `path:"/task/execute/{id}" method:"post" tags:"CpTask" summary:"更新执行任务参数"`
Id int64 `json:"id"`
Frequency int `json:"frequency"`
NotificationType cptask.NotificationType `json:"notification_type"`
ExecuteHour int `json:"execute_hour"`
ExecuteMinute int `json:"execute_minute"`
}
type DeleteTaskReq struct {
g.Meta `path:"/task/{id}" method:"delete" tags:"CpTask" summary:"删除任务"`
Id int64 `json:"id"`
}
type ExecuteNowReq struct {
g.Meta `path:"/task/executeNow/{id}" method:"post" tags:"CpTask" summary:"立即执行任务"`
Id int64 `json:"id"`
}
type PauseTaskReq struct {
g.Meta `path:"/task/pause/{id}" method:"post" tags:"CpTask" summary:"暂停任务"`
Id int64 `json:"id"`
}
+3 -1
View File
@@ -3,10 +3,11 @@ package filestorage
import (
"context"
"errors"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"io"
"likedream-goframe/internal/utils"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
@@ -23,6 +24,7 @@ func NewS3(accessKey, secretKey, baseUrl, bucket string) *S3 {
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
UsePathStyle: true,
BaseEndpoint: aws.String(baseUrl),
Region: "us-east-1",
})
return &S3{
@@ -49,6 +49,16 @@ func (c CpTaskServiceV1) Delete(ctx context.Context, req *v1.DeleteTaskReq) (res
defer utils.Pass(&err)
return service.Global.CpTask.Delete(ctx, req), nil
}
func (c CpTaskServiceV1) ExecuteNow(ctx context.Context, req *v1.ExecuteNowReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.CpTask.ExecuteNow(ctx, req), nil
}
func (c CpTaskServiceV1) PauseTask(ctx context.Context, req *v1.PauseTaskReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.CpTask.PauseTask(ctx, req), nil
}
func NewCpTaskCtrlV1() ICpTaskCtrlV1 {
return &CpTaskServiceV1{}
}
@@ -16,4 +16,6 @@ type ICpTaskCtrlV1 interface {
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)
ExecuteNow(ctx context.Context, req *v1.ExecuteNowReq) (res *utils.Response[any], err error)
PauseTask(ctx context.Context, req *v1.PauseTaskReq) (res *utils.Response[any], err error)
}
+9 -8
View File
@@ -2,6 +2,7 @@ package cpdaily
import (
"context"
"io"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/filestorage"
"likedream-goframe/internal/utils"
@@ -17,15 +18,12 @@ func (c CpImageCtrlV1) Upload(ctx context.Context, req *v1.UploadImageReq) (res
defer utils.Pass(&err)
file := req.File
open, err := file.Open()
utils.OkOrPanic(err,
func(err error) error {
return oops.WithContext(ctx).Wrap(err)
})
utils.OkOrPanic(err, oops.WithContext(ctx).Wrap)
key := utils.GenerateFileName("cpdaily/")
size := filestorage.GlobalS3.UploadFile(ctx, req.File.Filename, open)
size := filestorage.GlobalS3.UploadFile(ctx, key, open)
return utils.SuccessWithData[any](v1.UploadImageRes{
FileKey: key,
FileSize: *size,
FileSize: size,
}), nil
}
@@ -33,8 +31,11 @@ func (c CpImageCtrlV1) Preview(ctx context.Context, req *v1.PreviewImageReq) (re
defer utils.Pass(&err)
key := req.Key
file := filestorage.GlobalS3.GetFile(ctx, key)
g.RequestFromCtx(ctx).Response.Header().Set("Content-Type", "image/png")
g.RequestFromCtx(ctx).Response.WriteExit(file)
r := g.RequestFromCtx(ctx)
r.Response.Header().Set("Content-Type", "image/png")
// 将文件写入
io.Copy(r.Response.Writer, file)
r.Exit()
return nil, nil
}
+2 -1
View File
@@ -141,6 +141,7 @@ const (
TaskStatusDoing TaskStatus = "doing"
TaskStatusDone TaskStatus = "done"
TaskStatusFail TaskStatus = "fail"
TaskStatusPause TaskStatus = "pause"
TaskStatusUnknown TaskStatus = "unknown"
)
@@ -151,7 +152,7 @@ func (ts TaskStatus) String() string {
// TaskStatusValidator is a validator for the "task_status" field enum values. It is called by the builders before save.
func TaskStatusValidator(ts TaskStatus) error {
switch ts {
case TaskStatusNotStart, TaskStatusDoing, TaskStatusDone, TaskStatusFail, TaskStatusUnknown:
case TaskStatusNotStart, TaskStatusDoing, TaskStatusDone, TaskStatusFail, TaskStatusPause, TaskStatusUnknown:
return nil
default:
return fmt.Errorf("cptask: invalid enum value for task_status field: %q", ts)
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -53,9 +53,9 @@ var (
{Name: "task_type", Type: field.TypeEnum, Enums: []string{"collection", "sign", "sleep_check"}},
{Name: "title", Type: field.TypeString},
{Name: "frequency", Type: field.TypeInt, Default: 0},
{Name: "task_status", Type: field.TypeEnum, Enums: []string{"not_start", "doing", "done", "fail", "unknown"}},
{Name: "task_status", Type: field.TypeEnum, Enums: []string{"not_start", "doing", "done", "fail", "pause", "unknown"}},
{Name: "success_count", Type: field.TypeInt, Default: 0},
{Name: "form_data", Type: field.TypeString, Nullable: true},
{Name: "form_data", Type: field.TypeString, Nullable: true, Size: 2147483647},
{Name: "step", Type: field.TypeEnum, Enums: []string{"form", "execute", "complete"}},
{Name: "notification_type", Type: field.TypeEnum, Enums: []string{"none", "cp_user_email", "system_user_email"}},
{Name: "execute_hour", Type: field.TypeInt, Nullable: true},
+2 -2
View File
@@ -20,9 +20,9 @@ func (CpTask) Fields() []ent.Field {
field.Enum("task_type").Values("collection", "sign", "sleep_check"),
field.String("title").NotEmpty(),
field.Int("frequency").Default(0),
field.Enum("task_status").Values("not_start", "doing", "done", "fail", "unknown"),
field.Enum("task_status").Values("not_start", "doing", "done", "fail", "pause", "unknown"),
field.Int("success_count").Default(0),
field.String("form_data").Optional(),
field.Text("form_data").Optional(),
field.Enum("step").Values("form", "execute", "complete"),
field.Enum("notification_type").Values("none", "cp_user_email", "system_user_email"),
field.Int("execute_hour").Optional().Nillable(),
+3
View File
@@ -20,6 +20,9 @@ func ExceptionHandler(r *ghttp.Request) {
if r.Response.BufferLength() > 0 {
return
}
if r.Response.Header().Get("Content-Type") == "image/png" {
return
}
var (
err = r.GetError()
res = r.GetHandlerResponse()
+38 -14
View File
@@ -63,12 +63,15 @@ func (v CpTaskServiceV1) AddTask(ctx context.Context, req *v1.AddTaskReq) *utils
func (v CpTaskServiceV1) List(ctx context.Context, req *v1.CpTaskListReq) *utils.Response[[]*ent.CpTask] {
user := utils.GetUserInfoFromCtx(ctx)
query := db.Global.CpTask.Query().Where(cptask.HasSysUserWith(sysuser.ID(user.ID))).WithCpUser()
if req.Title != "" {
query.Where(cptask.TitleContains(req.Title))
if req.TaskType != "" {
query.Where(cptask.TaskTypeEQ(req.TaskType))
}
if req.Username != "" {
query.Where(cptask.HasCpUserWith(cpuser.UsernameContains(req.Username)))
}
if req.TaskStatus != "" {
query.Where(cptask.TaskStatusEQ(req.TaskStatus))
}
total := query.CountX(ctx)
var data []*ent.CpTask
if total > 0 {
@@ -137,10 +140,8 @@ func (v CpTaskServiceV1) TaskExecute(ctx context.Context, req *v1.GetCpTaskExecu
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 {
cptask.HasSysUserWith(sysuser.ID(user.ID))).OnlyX(ctx)
if cpTask == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号"))
}
return utils.SuccessWithData[any](cpTask)
@@ -152,15 +153,14 @@ func (v CpTaskServiceV1) PostExecute(ctx context.Context, req *v1.PostCpTaskExec
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 {
if cpTask == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号"))
}
if cpTask.Step < cptask.StepExecute {
if cpTask.Step == cptask.StepForm {
utils.OkOrPanic(errors2.NewBusinessError(400, "请先完成前置参数设置"))
}
pointConsume := req.Frequency - cpTask.Frequency
if pointConsume < 0 {
if req.Frequency < 1 || pointConsume < 0 {
utils.OkOrPanic(errors2.NewBusinessError(400, "执行次数应不低于原有次数"))
}
//todo 扣积分
@@ -168,10 +168,10 @@ func (v CpTaskServiceV1) PostExecute(ctx context.Context, req *v1.PostCpTaskExec
SetStep(cptask.StepComplete).
SetFrequency(req.Frequency).
SetTaskStatus(cptask.TaskStatusNotStart).
SetNotificationType(req.NotificationTypeNone).
SetNotificationType(req.NotificationType).
SetExecuteHour(req.ExecuteHour).
SetExecuteMinute(req.ExecuteMinute).
Exec(ctx)
ExecX(ctx)
return utils.SuccessWithMsg("更新成功")
}
@@ -181,14 +181,38 @@ func (v CpTaskServiceV1) Delete(ctx context.Context, req *v1.DeleteTaskReq) *uti
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 {
if cpTask == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号"))
}
db.Global.CpTask.DeleteOne(cpTask).ExecX(ctx)
return utils.SuccessWithMsg("删除成功")
}
func (v CpTaskServiceV1) ExecuteNow(ctx context.Context, req *v1.ExecuteNowReq) *utils.Response[any] {
// todo 立即执行
return utils.SuccessWithMsg("执行成功")
}
func (v CpTaskServiceV1) PauseTask(ctx context.Context, req *v1.PauseTaskReq) *utils.Response[any] {
user := utils.GetUserInfoFromCtx(ctx)
cpTask := db.Global.CpTask.Query().Where(cptask.ID(req.Id),
cptask.HasSysUserWith(sysuser.ID(user.ID))).
WithCpUser().WithSysUser().OnlyX(ctx)
if cpTask == nil {
utils.OkOrPanic(errors2.NewBusinessError(400, "非法的今日校园账号"))
}
if cpTask.TaskStatus == cptask.TaskStatusPause {
// todo 恢复任务
db.Global.CpTask.UpdateOne(cpTask).SetTaskStatus(cptask.TaskStatusNotStart).ExecX(ctx)
return utils.SuccessWithMsg("恢复成功")
} else {
// todo 暂停任务
db.Global.CpTask.UpdateOne(cpTask).SetTaskStatus(cptask.TaskStatusPause).ExecX(ctx)
return utils.SuccessWithMsg("暂停成功")
}
}
func NewCpTaskServiceV1() *CpTaskServiceV1 {
return &CpTaskServiceV1{}
}