✨ feat(schedulerefine): 新增 refine 子路由,优先执行复合操作,失败后降级至禁复合 ReAct 兜底 ReAct 升级 - ♻️ 将原有链路升级为真正的 ReAct 执行模式,进一步增强整体调度过程的可靠性 Refine 子路由 - 🧭 在 refine 主链路中新增 `route` 节点,整体流程调整为 `contract -> plan -> slice -> route -> react -> hard_check -> summary` - ⚡ 当 `route` 命中全局复合目标时,优先尝试一次调用 `SpreadEven` / `MinContextSwitch`,失败后最多重试 2 次 - 🔀 `route` 成功后直接跳过 `ReAct`;若执行失败,则自动切换至 `fallback` 模式 - 🛡️ 在 `fallback` 模式下增加后端硬约束:禁用 `SpreadEven` / `MinContextSwitch` / `BatchMove`,仅允许使用 `Move` / `Swap` 逐任务处理 - 🧠 在 `ReAct` 的 prompt 与上下文中新增 `COMPOSITE_TOOLS_ALLOWED`,显式告知当前是否允许使用复合工具 - 🧩 扩展状态字段以承载路由与降级状态:`CompositeRetryMax` / `DisableCompositeTools` / `CompositeRouteTried` / `CompositeRouteSucceeded` - 👀 增加 `route` 相关阶段日志,便于排查命中、重试、收口与降级原因 修复 - 🐛 修复 JWT Token 过期时间未按 `config.yaml` 配置生效的问题 备注 - 🚧 当前 ReAct 逐步微排链路已趋于稳定,但两个复合操作函数仍未恢复可用,后续将继续排查
104 lines
2.9 KiB
Go
104 lines
2.9 KiB
Go
package middleware
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"github.com/LoveLosita/smartflow/backend/auth"
|
||
"github.com/LoveLosita/smartflow/backend/dao"
|
||
"github.com/LoveLosita/smartflow/backend/model"
|
||
"github.com/LoveLosita/smartflow/backend/respond"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/golang-jwt/jwt/v4"
|
||
)
|
||
|
||
// extractTokenFromAuthorization 负责解析 Authorization 头中的 token。
|
||
// 职责边界:
|
||
// 1. 兼容“裸 token”和“Bearer <token>”两种传参方式。
|
||
// 2. 不负责 token 合法性校验,只做字符串提取。
|
||
// 3. 输入输出语义:header 为空或格式非法时返回空字符串。
|
||
func extractTokenFromAuthorization(header string) string {
|
||
trimmed := strings.TrimSpace(header)
|
||
if trimmed == "" {
|
||
return ""
|
||
}
|
||
|
||
parts := strings.Fields(trimmed)
|
||
if len(parts) == 2 && strings.EqualFold(parts[0], "Bearer") {
|
||
return strings.TrimSpace(parts[1])
|
||
}
|
||
|
||
if len(parts) == 1 {
|
||
return parts[0]
|
||
}
|
||
|
||
return ""
|
||
}
|
||
|
||
// JWTTokenAuth 负责 access token 的鉴权拦截。
|
||
// 职责边界:
|
||
// 1. 负责解析 token、验签、校验 token_type 与黑名单状态。
|
||
// 2. 不负责签发 token,也不负责用户登录逻辑。
|
||
// 3. 输出语义:校验通过时写入 user_id/claims 到上下文并放行;失败则中断请求。
|
||
func JWTTokenAuth(cache *dao.CacheDAO) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
tokenString := extractTokenFromAuthorization(c.GetHeader("Authorization"))
|
||
if tokenString == "" {
|
||
c.JSON(http.StatusUnauthorized, respond.MissingToken)
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
accessKey, err := auth.AccessSigningKey()
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, respond.InternalError(err))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// 1. 先验签并由 jwt 库统一校验 exp 等标准声明。
|
||
token, err := jwt.ParseWithClaims(tokenString, &model.MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||
return nil, respond.InvalidTokenSingingMethod
|
||
}
|
||
return accessKey, nil
|
||
})
|
||
if err != nil || !token.Valid {
|
||
c.JSON(http.StatusUnauthorized, respond.InvalidToken)
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// 2. 再做业务声明校验,防止 refresh token 越权访问业务接口。
|
||
claims, ok := token.Claims.(*model.MyCustomClaims)
|
||
if !ok {
|
||
c.JSON(http.StatusUnauthorized, respond.InvalidClaims)
|
||
c.Abort()
|
||
return
|
||
}
|
||
if claims.TokenType != "access_token" {
|
||
c.JSON(http.StatusUnauthorized, respond.WrongTokenType)
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// 3. 最后查黑名单,兜住“用户已登出但 token 仍未到期”的场景。
|
||
isBlack, err := cache.IsBlacklisted(claims.Jti)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, respond.InternalError(errors.New("无法验证令牌状态")))
|
||
c.Abort()
|
||
return
|
||
}
|
||
if isBlack {
|
||
c.JSON(http.StatusUnauthorized, respond.UserLoggedOut)
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
c.Set("user_id", claims.UserID)
|
||
c.Set("claims", claims)
|
||
c.Next()
|
||
}
|
||
}
|