feat: AI 助手模型管理(CRUD、会话绑定、自动化验收)

- Flyway V6:模型表、默认种子、ai_chat_session.model_id 回填
- 管理端 /api/admin/ai/models;用户端选模与新会话绑定
- 禁用模型后会话只读(5009);chat 按会话 modelCode 调 DashScope
- 前端 AiModels 页、助手模型选择器与 localStorage
- 单测、AiModelManagementApiIT、Playwright E2E;e2e Profile 关闭注解限流
- 技术实施文档与验收报告;移除过期冲突分析文档

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
nojava
2026-05-24 03:36:31 +08:00
parent 9cf811d8c1
commit 6c8b31e38a
58 changed files with 2693 additions and 556 deletions
@@ -9,6 +9,7 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
@@ -25,6 +26,9 @@ public class RateLimitAspect {
private final StringRedisTemplate redisTemplate;
@Value("${jog.rate-limit.enabled:true}")
private boolean rateLimitEnabled;
private static final String LUA_SCRIPT =
"local current = redis.call('INCR', KEYS[1]) " +
"if current == 1 then " +
@@ -37,6 +41,9 @@ public class RateLimitAspect {
@Around("@annotation(fun.nojava.common.annotation.RateLimit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
if (!rateLimitEnabled) {
return joinPoint.proceed();
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RateLimit rateLimit = method.getAnnotation(RateLimit.class);