Files
smartmate/backend/shared/respond/respond.go
Losita 25a608eaeb Version: 0.9.82.dev.260507
后端:
1. 登录注册补齐极验行为验证与跨域入口:gateway 新增 `/user/captcha/register`,登录/注册先做 GeeTest 初始化与二次校验,再进入 user/auth RPC;补充验证码失败/初始化失败/服务不可用响应码,并新增可配置 CORS middleware 适配分域部署。
2. 容器部署配置入口收口:`bootstrap.LoadConfig` 支持 `SMARTFLOW_CONFIG_FILE` 与环境变量覆盖,`config.example.yaml` / `config.docker.yaml` 补齐 geetest 与容器内服务地址,网关新增配置列表解析,便于 compose 场景直接挂载配置启动。
3. LLM outbox 与助手时间线稳定性修正:`cmd/llm` 显式绑定 llm 自身 topic/group,避免误入 agent consumer group;agent timeline 在 Redis 热缓存未落 MySQL 时改用 `seq` 兜底临时 id,避免前端历史回放撞 key。

前端:
4. 认证页接入极验并补齐提交前校验:新增 GeeTest 脚本加载与实例封装,登录/注册面板支持 challenge 初始化、切换面板重挂载、失败提示与提交前校验,认证 API/types 同步透传 geetest 三元组。
5. 前端部署基址与网关对接收口:Axios `baseURL`、Vue Router `history base` 与 Vite `base/dev proxy` 改为读取环境变量,新增 `frontend/.env.example`,支持子路径部署、容器内反向代理和本地联调共存。
6. 助手与工作台展示细节修正:AssistantPanel 历史重建优先使用真实 timeline id、缺失时退回 `seq` 保证消息主键唯一;首页主面板改为纵向可滚动并补底部留白,避免内容截断。

仓库:
7. 整站容器化交付链路补齐并重写说明文档:新增后端/前端 Dockerfile、`.dockerignore`、前端 Nginx 代理、`docker-compose.full.yml`、`.env.full.example` 与镜像打包/导入脚本,README 改写数据库/路由/部署章节,并新增 `docs/容器化部署说明.md` 说明离线镜像分发方案。
2026-05-07 00:58:27 +08:00

424 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package respond 响应处理
// 统一API响应格式和处理逻辑
package respond
import (
"net/http"
"strings"
)
type Response struct { //响应结构体
Status string `json:"status"`
Info string `json:"info"`
}
type FinalResponse struct { //最终响应结构体
Status string `json:"status"`
Info string `json:"info"`
Data interface{} `json:"data"`
}
func (r Response) Error() string { // 实现 error 接口
return r.Info
}
// HTTPStatus 负责把项目内部响应码映射到 HTTP 状态码。
//
// 职责边界:
// 1. 只根据当前响应码判断该返回 200/400/401/500 里的哪一类;
// 2. 不负责写响应体,也不负责区分具体业务来源;
// 3. 业务侧可以继续透传 Status/InfoHTTP 层只需要调用这个方法。
func (r Response) HTTPStatus() int {
switch r.Status {
case MissingToken.Status, InvalidToken.Status, InvalidClaims.Status, InvalidRefreshToken.Status,
WrongTokenType.Status, UserLoggedOut.Status, ErrUnauthorized.Status:
return http.StatusUnauthorized
case UserTasksEmpty.Status, NoOngoingOrUpcomingSchedule.Status, TaskAlreadyDeleted.Status:
return http.StatusOK
}
if strings.HasPrefix(strings.TrimSpace(r.Status), "5") {
return http.StatusInternalServerError
}
return http.StatusBadRequest
}
func RespWithData(response Response, data interface{}) FinalResponse { //传入一个响应结构体和数据,返回一个最终响应结构体
var finalResponse FinalResponse
finalResponse.Status = response.Status
finalResponse.Info = response.Info
finalResponse.Data = data
return finalResponse
}
func InternalError(err error) Response { //服务器错误
return Response{
Status: "500",
Info: err.Error(),
}
}
var ( //请求相关的响应
Ok = Response{ //正常
Status: "10000",
Info: "success",
}
UserTasksEmpty = Response{ //用户任务为空
Status: "10001",
Info: "user tasks empty",
}
NoOngoingOrUpcomingSchedule = Response{ //没有正在进行或即将开始的日程
Status: "10002",
Info: "no ongoing or upcoming schedule",
}
WrongName = Response{ //用户名错误
Status: "40001",
Info: "wrong username",
}
WrongPwd = Response{ //密码错误
Status: "40002",
Info: "wrong password",
}
InvalidName = Response{ //用户名无效
Status: "40003",
Info: "the username already exists",
}
MissingParam = Response{ //缺少参数
Status: "40004",
Info: "missing param",
}
WrongParamType = Response{ //参数错误
Status: "40005",
Info: "wrong param type",
}
ParamTooLong = Response{ //参数过长
Status: "40006",
Info: "param too long",
}
WrongUsernameOrPwd = Response{ //用户名或密码错误
Status: "40007",
Info: "wrong username or password",
}
WrongGender = Response{ //性别错误
Status: "40008",
Info: "wrong gender",
}
MissingToken = Response{ //缺少token
Status: "40009",
Info: "missing token",
}
InvalidTokenSingingMethod = Response{ //jwt token签名方法无效
Status: "40010",
Info: "invalid signing method",
}
InvalidToken = Response{ //无效token
Status: "40011",
Info: "invalid token",
}
InvalidClaims = Response{ //无效声明
Status: "40012",
Info: "invalid claims",
}
WrongUserID = Response{ //用户ID错误
Status: "40013",
Info: "wrong userid",
}
ErrUnauthorized = Response{ //未授权,没有权限
Status: "40014",
Info: "unauthorized",
}
InvalidRefreshToken = Response{ //刷新令牌无效
Status: "40015",
Info: "invalid refresh token",
}
WrongTokenType = Response{ //无效令牌类型
Status: "40016",
Info: "wrong token type",
}
UserLoggedOut = Response{ //用户已登出
Status: "40017",
Info: "user logged out",
}
InvalidPriority = Response{ //无效优先级
Status: "40018",
Info: "invalid priority",
}
WrongCourseInfo = Response{ //课程信息错误
Status: "40019",
Info: "wrong course info",
}
UserTaskClassNotFound = Response{ //用户任务类未找到
Status: "40020",
Info: "user task class not found",
}
UserTaskClassForbidden = Response{ //用户任务类禁止访问
Status: "40021",
Info: "user task class forbidden",
}
TaskClassItemNotBelongToUser = Response{ //任务类项目不属于用户
Status: "40022",
Info: "task class item does not belong to user",
}
TimeOutOfRangeOfThisSemester = Response{ //时间超出本学期范围
Status: "40023",
Info: "time out of range of this semester",
}
CourseNotBelongToUser = Response{ //课程不属于用户
Status: "40024",
Info: "course does not belong to user",
}
CourseAlreadyEmbeddedByOtherTaskBlock = Response{ //课程已被其他任务块嵌入
Status: "40025",
Info: "course already embedded by other task block",
}
ScheduleConflict = Response{ //日程冲突
Status: "40026",
Info: "schedule conflict",
}
WrongCourseID = Response{ //课程ID错误
Status: "40027",
Info: "wrong course id",
}
CourseTimeNotMatch = Response{ //课程时间不匹配
Status: "40028",
Info: "course time not match",
}
InsertCourseTwice = Response{ //重复插入课程
Status: "40029",
Info: "insert course twice",
}
WeekOutOfRange = Response{ //周数超出范围
Status: "40030",
Info: "week out of range",
}
WrongScheduleEventID = Response{ //日程ID错误
Status: "40031",
Info: "wrong schedule_event id",
}
TargetScheduleNotHaveEmbeddedTask = Response{ //目标日程没有嵌入任务
Status: "40032",
Info: "target schedule does not have embedded task",
}
TooManyRequests = Response{ //请求过多
Status: "40033",
Info: "too many requests",
}
TaskClassItemAlreadyArranged = Response{ //任务类项目已安排
Status: "40034",
Info: "task class item already arranged",
}
TargetTaskNotEmbeddedInAnySchedule = Response{ //目标任务未嵌入任何日程
Status: "40035",
Info: "target task not embedded in any schedule",
}
TaskClassItemNotFound = Response{ //任务类项目未找到
Status: "40036",
Info: "task class item not found",
}
MissingIdempotencyKey = Response{ //缺少幂等性键
Status: "40037",
Info: "missing idempotency key",
}
RequestIsProcessing = Response{ //请求正在处理中
Status: "40038",
Info: "request is processing, please do not repeat click",
}
TaskClassNotBelongToUser = Response{ //任务类不属于用户
Status: "40039",
Info: "task class does not belong to user",
}
WrongTaskClassID = Response{ //任务类ID错误
Status: "40040",
Info: "wrong task class id",
}
InvalidSectionNumber = Response{ //无效的节次
Status: "40041",
Info: "invalid section number",
}
InvalidWeekOrDayOfWeek = Response{ //无效的周数或星期
Status: "40042",
Info: "invalid week or day_of_week",
}
InvalidSectionRange = Response{ //无效的节次范围
Status: "40043",
Info: "invalid section range, start_section should be less than or equal to end_section",
}
MissingParamForAutoScheduling = Response{ //自动排课缺少参数
Status: "40044",
Info: "missing param for auto scheduling",
}
InvalidDateRange = Response{ //无效的日期范围
Status: "40045",
Info: "invalid date range, start_date should be before or equal to end_date",
}
TaskClassModeNotAuto = Response{ //任务类模式不是自动
Status: "40046",
Info: "task class mode is not auto",
}
TimeNotEnoughForAutoScheduling = Response{ //自动排课时间不足
Status: "40047",
Info: "time not enough for auto scheduling",
}
TaskClassItemNotBelongToTaskClass = Response{ //任务类项目不属于任务类
Status: "40048",
Info: "task class item does not belong to task class",
}
TaskClassItemTryingToInsertOutOfTimeRange = Response{ //任务类项目试图插入超出时间范围
Status: "40049",
Info: "task class item trying to insert out of time range",
}
WrongTaskID = Response{ //任务ID错误
Status: "40050",
Info: "wrong task id",
}
TokenUsageExceedsLimit = Response{ //token 使用量超过限额
Status: "40051",
Info: "token usage exceeds limit",
}
TaskNotCompleted = Response{ //任务未完成,无法取消勾选
Status: "40052",
Info: "task is not completed",
}
SchedulePlanPreviewNotFound = Response{ //排程预览不存在或已过期
Status: "40053",
Info: "schedule plan preview not found",
}
ConversationNotFound = Response{ //会话不存在或不属于当前用户
Status: "40401",
Info: "conversation not found",
}
MissingConversationID = Response{ //确认/恢复请求缺少会话ID
Status: "40054",
Info: "conversation_id is required when confirm_action is present",
}
MemoryItemNotFound = Response{ //记忆条目不存在
Status: "40055",
Info: "memory item not found",
}
MemoryInvalidType = Response{ //记忆类型不合法
Status: "40056",
Info: "invalid memory type",
}
MemoryInvalidContent = Response{ //记忆内容为空或不合法
Status: "40057",
Info: "invalid memory content",
}
ScheduleStateSnapshotNotFound = Response{ //排程快照不存在或已过期
Status: "40058",
Info: "schedule state snapshot not found",
}
ScheduleStateInvalidCoordinates = Response{ //绝对时间坐标超出排程窗口范围
Status: "40059",
Info: "invalid week/day_of_week coordinates",
}
ScheduleStateTaskItemNotFound = Response{ //task_item_id 在快照中不存在
Status: "40060",
Info: "task_item_id not found in schedule state",
}
ScheduleStateEventNotFound = Response{ //embed_course_event_id 在快照课程中不存在
Status: "40061",
Info: "embed_course_event_id not found in schedule state events",
}
ScheduleStateDuplicateTaskItem = Response{ //请求中包含重复的 task_item_id
Status: "40062",
Info: "duplicate task_item_id in request",
}
TaskUpdateNoFields = Response{ //更新任务未指定任何字段
Status: "40063",
Info: "no fields to update",
}
CaptchaVerifyFailed = Response{ //人机验证未通过
Status: "40064",
Info: "人机验证未通过",
}
TaskAlreadyDeleted = Response{ //任务已删除或不存在(幂等信息码)
Status: "10003",
Info: "task already deleted or not found",
}
RouteControlInternalError = Response{ //路由控制码内部错误
Status: "50001",
Info: "route control failed",
}
ScheduleRefineOutputParseFailed = Response{ //智能微调输出二次解析失败
Status: "50002",
Info: "schedule refine output parse failed",
}
CaptchaInitFailed = Response{ //人机验证初始化失败
Status: "50003",
Info: "人机验证初始化失败",
}
CaptchaVerifyUnavailable = Response{ //人机验证服务暂不可用
Status: "50004",
Info: "人机验证服务暂不可用",
}
)