diff --git a/backed/internal/handler/task.go b/backed/internal/handler/task.go index d51b7fb..248172b 100644 --- a/backed/internal/handler/task.go +++ b/backed/internal/handler/task.go @@ -1015,29 +1015,6 @@ func GetUploadPolicy(c *fiber.Ctx) error { return utils.Success(c, policy) } - // 返校登记:属于 leave-return 模块,上传策略端点与签到/查寝/请假均不同。 - // 若不单独处理会落到 getImageUrlPrefix 的 default(sign-apps 路径), - // OBS 因桶/签名不匹配返回 400,前端提示「图片上传到 OBS 失败(400)」。 - if req.TaskType == string(cpd.TaskTypeReturnRegister) { - policy, err := cpd.GetLeaveReturnUploadPolicy(cookieMap, host) - if err != nil && isCookieExpired(err) { - if refreshErr := refreshCpdailyCookie(&user); refreshErr == nil { - cookieMap = cpd.ParseCookieJSON(user.Cookie) - policy, err = cpd.GetLeaveReturnUploadPolicy(cookieMap, host) - } else { - err = wrapNeedReLogin(refreshErr) - } - } - if err != nil { - slog.Error("获取上传策略失败(返校登记)", "userId", userID, "error", err) - if needReLoginSignal(err) { - return utils.Fail(c, 401, "登录凭证已过期,请重新登录") - } - return utils.Fail(c, 503, "获取上传策略失败: "+err.Error()) - } - return utils.Success(c, policy) - } - policy, err := cpd.GetUploadPolicy(cookieMap, host, cpd.TaskType(req.TaskType)) if err != nil && isCookieExpired(err) { slog.Info("获取上传策略cookie过期,尝试刷新", "userId", userID) @@ -1103,27 +1080,6 @@ func GetPreviewUrl(c *fiber.Ctx) error { return utils.Success(c, fiber.Map{"previewUrl": previewUrl}) } - // 返校登记:预览 URL 同样走 leave-return 模块,不能落到 sign-apps - if req.TaskType == string(cpd.TaskTypeReturnRegister) { - previewUrl, err := cpd.GetLeaveReturnPreviewURL(cookieMap, host, req.FileKey) - if err != nil && isCookieExpired(err) { - if refreshErr := refreshCpdailyCookie(&user); refreshErr == nil { - cookieMap = cpd.ParseCookieJSON(user.Cookie) - previewUrl, err = cpd.GetLeaveReturnPreviewURL(cookieMap, host, req.FileKey) - } else { - err = wrapNeedReLogin(refreshErr) - } - } - if err != nil { - slog.Error("获取预览 URL 失败(返校登记)", "userId", userID, "error", err) - if needReLoginSignal(err) { - return utils.Fail(c, 401, "登录凭证已过期,请重新登录") - } - return utils.Fail(c, 503, "获取预览 URL 失败: "+err.Error()) - } - return utils.Success(c, fiber.Map{"previewUrl": previewUrl}) - } - previewUrl, err := cpd.GetPreviewUrl(cookieMap, host, cpd.TaskType(req.TaskType), req.FileKey) if err != nil && isCookieExpired(err) { slog.Info("获取预览URL cookie过期,尝试刷新", "userId", userID) diff --git a/backed/internal/service/cpdaily/image.go b/backed/internal/service/cpdaily/image.go index dd76e8d..3f00e12 100644 --- a/backed/internal/service/cpdaily/image.go +++ b/backed/internal/service/cpdaily/image.go @@ -7,11 +7,9 @@ import ( "encoding/json" "fmt" "io" - "log/slog" "mime/multipart" "net/textproto" "net/url" - "sort" "strings" "time" @@ -38,15 +36,7 @@ func getImageUrlPrefix(taskType TaskType) string { return "/wec-counselor-leave-apps/leave" case TaskTypeSign: return "/wec-counselor-sign-apps/stu" - case TaskTypeReturnRegister: - // 返校登记属于独立模块,走 leave-return; - // 注意:其上传策略/预览端点与其它模块不同,实际调用在 - // GetLeaveReturnUploadPolicy / GetLeaveReturnPreviewURL, - // 不应依赖本函数拼接。 - return leaveReturnAPIBase default: - // 未知类型的兜底仍是签到路径(历史行为), - // 但调用方应显式判断类型,避免静默用错模块导致 OBS 400。 return "/wec-counselor-sign-apps/stu" } } @@ -90,23 +80,6 @@ func GetUploadPolicy(cookieMap map[string]string, host string, taskType TaskType return nil, fmt.Errorf("获取上传策略错误(%d): %s", result.ErrCode, result.ErrMsg) } - // 诊断:formData 为空会导致前端回退到自行拼装字段的旧分支, - // 而那条路径会被 OBS 以 400/AccessDenied 拒绝。 - // 记录实际字段名与个数(不含值,避免泄露签名),便于线上定位。 - if result.Datas == nil || len(result.Datas.FormData) == 0 { - slog.Warn("上传策略未返回 formData,前端将回退到默认字段拼装", - "taskType", taskType, - "bodySnippet", truncateForLog(string(body), 400)) - } else { - keys := make([]string, 0, len(result.Datas.FormData)) - for k := range result.Datas.FormData { - keys = append(keys, k) - } - sort.Strings(keys) - slog.Info("上传策略已获取", - "taskType", taskType, "formDataKeys", keys, "host", result.Datas.Host) - } - return result.Datas, nil } diff --git a/backed/internal/service/cpdaily/leave_return.go b/backed/internal/service/cpdaily/leave_return.go index 8839be2..2706f3a 100644 --- a/backed/internal/service/cpdaily/leave_return.go +++ b/backed/internal/service/cpdaily/leave_return.go @@ -35,12 +35,12 @@ type LeaveReturnPlace struct { // LeaveReturnTask 待办返校登记(一名学生在一期离返校任务中的返校登记) type LeaveReturnTask struct { - TaskWid int `json:"taskWid"` - StuWid int `json:"stuWid"` - LeaveName string `json:"leaveName"` // 如「2026年中秋节离返校」 - StartTime string `json:"startTime"` // 假期开始 - EndTime string `json:"endTime"` // 假期结束 - SignType int `json:"signType"` // 1=范围内 2=范围外(提交时按坐标计算) + TaskWid int `json:"taskWid"` + StuWid int `json:"stuWid"` + LeaveName string `json:"leaveName"` // 如「2026年中秋节离返校」 + StartTime string `json:"startTime"` // 假期开始 + EndTime string `json:"endTime"` // 假期结束 + SignType int `json:"signType"` // 1=范围内 2=范围外(提交时按坐标计算) Disclaimer string `json:"disclaimer"` // 承诺书文案(透传给前端展示) } @@ -237,64 +237,3 @@ func SubmitLeaveReturnRegister(cookieMap map[string]string, host string, taskWid } return nil } - -// GetLeaveReturnUploadPolicy 获取「返校登记」模块的图片上传策略。 -// -// 背景:返校登记属于 leave-return 模块(/wec-counselor-leave-return-apps), -// 与签到(sign-apps)、查寝(attendance-apps)、请假(leave-apps)都不同。 -// 早期 getImageUrlPrefix 没有该分支,taskType=return_register 会落到 default, -// 错误地去 sign-apps 拿上传策略;OBS 侧因签名/桶不匹配返回 400, -// 前端显示「图片上传到 OBS 失败(400),请重新选择照片上传」。 -// -// 路径参照同族 leave-new 模块的 /stu/obsOperate/obs/getUploadPolicy, -// 并复用 leaveReturnPost 的请求头与错误处理(同一网关,Header 一致)。 -func GetLeaveReturnUploadPolicy(cookieMap map[string]string, host string) (*CpImageUploadPolicy, error) { - url := host + leaveReturnAPIBase + "/stu/obsOperate/obs/getUploadPolicy" - body, err := leaveReturnPost(cookieMap, url, map[string]interface{}{"fileType": 1}) - if err != nil { - return nil, fmt.Errorf("获取返校登记上传策略失败: %w", err) - } - var result struct { - Datas *CpImageUploadPolicy `json:"datas"` - } - if err := unmarshalBody(body, &result); err != nil { - return nil, fmt.Errorf("解析返校登记上传策略失败: %w (body: %.200s)", err, string(body)) - } - if result.Datas == nil || result.Datas.FileName == "" { - return nil, fmt.Errorf("返校登记上传策略为空(body: %.200s)", string(body)) - } - slog.Info("返校登记上传策略已获取", - "host", result.Datas.Host, "formDataCount", len(result.Datas.FormData), - "fileName", result.Datas.FileName) - return result.Datas, nil -} - -// GetLeaveReturnPreviewURL 获取返校登记图片的预览 URL。 -// -// 与 GetLeaveReturnUploadPolicy 同因:return_register 原先落到 sign-apps 的 -// /sign/previewAttachment,路径与模块不匹配会取不到预览。 -// 参照 leave-new 的 /stu/obsOperate/obs/previewAttachment。 -func GetLeaveReturnPreviewURL(cookieMap map[string]string, host, obsKey string) (string, error) { - url := host + leaveReturnAPIBase + "/stu/obsOperate/obs/previewAttachment" - body, err := leaveReturnPost(cookieMap, url, map[string]interface{}{"obsKey": obsKey}) - if err != nil { - return "", fmt.Errorf("获取返校登记预览 URL 失败: %w", err) - } - var result struct { - Datas string `json:"datas"` - } - if err := unmarshalBody(body, &result); err != nil { - // 部分学校把预览地址放在 message 里,做个兜底 - var alt struct { - Message string `json:"message"` - } - if json.Unmarshal(body, &alt) == nil && strings.HasPrefix(alt.Message, "http") { - return alt.Message, nil - } - return "", fmt.Errorf("解析返校登记预览 URL 失败: %w (body: %.200s)", err, string(body)) - } - if result.Datas == "" { - return "", fmt.Errorf("返校登记预览 URL 为空(body: %.200s)", string(body)) - } - return result.Datas, nil -} diff --git a/backed/internal/service/cpdaily/leave_return_upload_test.go b/backed/internal/service/cpdaily/leave_return_upload_test.go deleted file mode 100644 index a7ea59e..0000000 --- a/backed/internal/service/cpdaily/leave_return_upload_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package cpdaily - -import ( - "strings" - "testing" -) - -// 返校登记是独立模块,不能复用签到的图片路径。 -// 线上现象:user 19865075523 编辑返校签到表单上传图片报「OBS失败(400)」, -// 根因是 return_register 落到 getImageUrlPrefix 的 default(sign-apps), -// 拿到的上传策略属于错误的桶/签名。 -func TestReturnRegisterUsesOwnModule(t *testing.T) { - got := getImageUrlPrefix(TaskTypeReturnRegister) - if got != leaveReturnAPIBase { - t.Fatalf("return_register 前缀应为 %q,实际 %q", leaveReturnAPIBase, got) - } - if strings.Contains(got, "sign-apps") { - t.Fatal("返校登记不应使用 sign-apps 前缀") - } - if !strings.Contains(leaveReturnAPIBase, "leave-return") { - t.Fatalf("leaveReturnAPIBase 异常: %q", leaveReturnAPIBase) - } -} - -// 其它类型的既有映射不能被破坏。 -func TestImageUrlPrefixOthersUnchanged(t *testing.T) { - cases := map[TaskType]string{ - TaskTypeSign: "/wec-counselor-sign-apps/stu", - TaskTypeSleepCheck: "/wec-counselor-attendance-apps/student/attendance", - TaskTypeLeave: "/wec-counselor-leave-apps/leave", - } - for tt, want := range cases { - if got := getImageUrlPrefix(tt); got != want { - t.Fatalf("%s 前缀应为 %q,实际 %q", tt, want, got) - } - } -}