【同步】BOOT 和 CLOUD 的功能(ai + trade)
parent
4b8346ec80
commit
e657805544
|
|
@ -83,6 +83,15 @@ public class AiKnowledgeSegmentController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除段落")
|
||||||
|
@Parameter(name = "id", description = "段落编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
|
||||||
|
public CommonResult<Boolean> deleteKnowledgeSegment(@RequestParam("id") Long id) {
|
||||||
|
segmentService.deleteKnowledgeSegment(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/split")
|
@GetMapping("/split")
|
||||||
@Operation(summary = "切片内容")
|
@Operation(summary = "切片内容")
|
||||||
@Parameters({
|
@Parameters({
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ public class AiImageServiceImpl implements AiImageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
|
@SuppressWarnings("ConstantValue")
|
||||||
public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
|
public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
|
||||||
try {
|
try {
|
||||||
// 1.1 构建请求
|
// 1.1 构建请求
|
||||||
|
|
@ -164,8 +165,8 @@ public class AiImageServiceImpl implements AiImageService {
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
|
||||||
return DashScopeImageOptions.builder()
|
return DashScopeImageOptions.builder()
|
||||||
.withModel(model.getModel()).withN(1)
|
.model(model.getModel()).n(1)
|
||||||
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
.height(draw.getHeight()).width(draw.getWidth())
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
|
||||||
return QianFanImageOptions.builder()
|
return QianFanImageOptions.builder()
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,13 @@ public interface AiKnowledgeSegmentService {
|
||||||
*/
|
*/
|
||||||
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
|
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除知识库段落
|
||||||
|
*
|
||||||
|
* @param id 段落编号
|
||||||
|
*/
|
||||||
|
void deleteKnowledgeSegment(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新索引知识库下的所有文档段落
|
* 重新索引知识库下的所有文档段落
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,19 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteKnowledgeSegment(Long id) {
|
||||||
|
// 1. 校验段落存在
|
||||||
|
AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(id);
|
||||||
|
|
||||||
|
// 2. 删除向量
|
||||||
|
VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId());
|
||||||
|
deleteVectorStore(vectorStore, segment);
|
||||||
|
|
||||||
|
// 3. 删除段落记录
|
||||||
|
segmentMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
|
public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
|
||||||
// 1. 查询需要删除的段落
|
// 1. 查询需要删除的段落
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package cn.iocoder.yudao.module.ai.util;
|
package cn.iocoder.yudao.module.ai.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
|
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
|
||||||
|
|
@ -33,6 +35,28 @@ public class AiUtils {
|
||||||
public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER";
|
public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER";
|
||||||
public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
|
public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通义千问支持多模态的模型
|
||||||
|
*
|
||||||
|
* @see <a href="https://bailian.console.aliyun.com/cn-beijing/?tab=model#/model-market/all?providers=qwen&capabilities=VU">模型广场</a>
|
||||||
|
* @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">必须开启 withMultiModel 参数</a>
|
||||||
|
*/
|
||||||
|
public static final Set<String> TONG_YI_MULTI_MODELS = SetUtils.asSet(
|
||||||
|
// qwen3.5 / 3.6 系列(统一多模态主干)
|
||||||
|
"qwen3.6-plus", "qwen3.6-flash",
|
||||||
|
"qwen3.5-plus", "qwen3.5-flash",
|
||||||
|
// qwen-vl 视觉理解
|
||||||
|
"qwen3-vl-plus", "qwen3-vl-flash",
|
||||||
|
"qwen-vl-max", "qwen-vl-plus",
|
||||||
|
"qwen2.5-vl-72b-instruct", "qwen2.5-vl-32b-instruct",
|
||||||
|
"qwen2.5-vl-7b-instruct", "qwen2.5-vl-3b-instruct",
|
||||||
|
// qvq 视觉推理
|
||||||
|
"qvq-max", "qvq-plus",
|
||||||
|
// qwen-omni 全模态
|
||||||
|
"qwen3.5-omni-plus", "qwen3.5-omni-flash",
|
||||||
|
"qwen3-omni-flash", "qwen-omni-turbo"
|
||||||
|
);
|
||||||
|
|
||||||
public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
|
public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
|
||||||
return buildChatOptions(platform, model, temperature, maxTokens, null, null);
|
return buildChatOptions(platform, model, temperature, maxTokens, null, null);
|
||||||
}
|
}
|
||||||
|
|
@ -44,9 +68,10 @@ public class AiUtils {
|
||||||
// noinspection EnhancedSwitchMigration
|
// noinspection EnhancedSwitchMigration
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case TONG_YI:
|
case TONG_YI:
|
||||||
return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens)
|
return DashScopeChatOptions.builder().model(model).temperature(temperature).maxToken(maxTokens)
|
||||||
.withEnableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置
|
.enableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置
|
||||||
.withToolCallbacks(toolCallbacks).withToolContext(toolContext).build();
|
.multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 是否多模态模型
|
||||||
|
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||||
case YI_YAN:
|
case YI_YAN:
|
||||||
return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
|
return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
|
||||||
case DEEP_SEEK:
|
case DEEP_SEEK:
|
||||||
|
|
@ -125,10 +150,13 @@ public class AiUtils {
|
||||||
|| response.getResult().getOutput() == null) {
|
|| response.getResult().getOutput() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (response.getResult().getOutput() instanceof DeepSeekAssistantMessage) {
|
AssistantMessage output = response.getResult().getOutput();
|
||||||
return ((DeepSeekAssistantMessage) (response.getResult().getOutput())).getReasoningContent();
|
// DeepSeek 通过专属 AssistantMessage 暴露 reasoningContent
|
||||||
|
if (output instanceof DeepSeekAssistantMessage) {
|
||||||
|
return ((DeepSeekAssistantMessage) output).getReasoningContent();
|
||||||
}
|
}
|
||||||
return null;
|
// 通义千问等通过 metadata 透传 reasoningContent
|
||||||
|
return MapUtil.getStr(output.getMetadata(), "reasoningContent");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -34,15 +34,15 @@ public class TongYiChatModelTests {
|
||||||
|
|
||||||
private final DashScopeChatModel chatModel = DashScopeChatModel.builder()
|
private final DashScopeChatModel chatModel = DashScopeChatModel.builder()
|
||||||
.dashScopeApi(DashScopeApi.builder()
|
.dashScopeApi(DashScopeApi.builder()
|
||||||
.apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
|
.apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key 获取密钥
|
||||||
.build())
|
.build())
|
||||||
.defaultOptions(DashScopeChatOptions.builder()
|
.defaultOptions(DashScopeChatOptions.builder()
|
||||||
// .withModel("qwen1.5-72b-chat") // 模型
|
.multiModel(true) // 注意:当使用 qwen3.6-plus 等多模态模型,需要设置为 true,可见 https://help.aliyun.com/zh/model-studio/error-code#error-url 链接
|
||||||
.withModel("qwen3-235b-a22b-thinking-2507") // 模型
|
.model("qwen3.6-plus") // 模型
|
||||||
// .withModel("deepseek-r1") // 模型(deepseek-r1)
|
// .model("deepseek-r1") // 模型(deepseek-r1)
|
||||||
// .withModel("deepseek-v3") // 模型(deepseek-v3)
|
// .model("deepseek-v3") // 模型(deepseek-v3)
|
||||||
// .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b)
|
// .model("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b)
|
||||||
// .withEnableThinking(true)
|
// .enableThinking(true)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -85,9 +85,9 @@ public class TongYiChatModelTests {
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||||
DashScopeChatOptions options = DashScopeChatOptions.builder()
|
DashScopeChatOptions options = DashScopeChatOptions.builder()
|
||||||
.withModel("qwen3-235b-a22b-thinking-2507")
|
.model("qwen3.6-plus").multiModel(true)
|
||||||
// .withModel("qwen-max-2025-01-25")
|
// .withModel("qwen-max-2025-01-25")
|
||||||
.withEnableThinking(true) // 必须设置,否则会报错
|
.enableThinking(true) // 必须设置,否则会报错
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
|
|
@ -112,8 +112,8 @@ public class TongYiChatModelTests {
|
||||||
Document document01 = new Document("abc");
|
Document document01 = new Document("abc");
|
||||||
Document document02 = new Document("sapring");
|
Document document02 = new Document("sapring");
|
||||||
RerankOptions options = DashScopeRerankOptions.builder()
|
RerankOptions options = DashScopeRerankOptions.builder()
|
||||||
.withTopN(1)
|
.topN(1)
|
||||||
.withModel("gte-rerank-v2")
|
.model("gte-rerank-v2")
|
||||||
.build();
|
.build();
|
||||||
RerankRequest rerankRequest = new RerankRequest(
|
RerankRequest rerankRequest = new RerankRequest(
|
||||||
query,
|
query,
|
||||||
|
|
|
||||||
|
|
@ -12,23 +12,34 @@ import org.springframework.ai.image.ImageResponse;
|
||||||
/**
|
/**
|
||||||
* {@link DashScopeImageModel} 集成测试类
|
* {@link DashScopeImageModel} 集成测试类
|
||||||
*
|
*
|
||||||
|
* TODO @芋艿:注:spring-ai-alibaba-dashscope(1.1.2.2)的 {@code DashScopeImageApi#resolveImagePath} 未给 {@code wan2.7-image} 加路由分支,
|
||||||
|
* 会落到默认的 {@code text2image/image-synthesis} 异步端点 + 旧版 {@code prompt} 入参,
|
||||||
|
* 而该模型实际要求 {@code multimodal-generation/generation} 同步端点 + {@code messages} 入参,
|
||||||
|
* 端点、异步头、入参结构全部对不上,所以走 SDK 直接调用必失败。
|
||||||
|
*
|
||||||
|
* 临时方案:改用 SDK 已支持的 {@code wan2.6-image}(异步)或 {@code qwen-image}(同步);
|
||||||
|
* 或在项目内同包同名覆盖 {@code DashScopeImageApi},把 {@code wan2.7*} 也路由到 {@code wan2.6-image} 那条 {@code image-generation/generation} 异步分支。
|
||||||
|
*
|
||||||
* @author fansili
|
* @author fansili
|
||||||
*/
|
*/
|
||||||
public class TongYiImagesModelTest {
|
public class TongYiImagesModelTest {
|
||||||
|
|
||||||
private final DashScopeImageModel imageModel = DashScopeImageModel.builder()
|
private final DashScopeImageModel imageModel = DashScopeImageModel.builder()
|
||||||
.dashScopeApi(DashScopeImageApi.builder()
|
.dashScopeApi(DashScopeImageApi.builder()
|
||||||
.apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
|
.apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key 获取密钥
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// TODO @芋艿:
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void imageCallTest() {
|
public void imageCallTest() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
ImageOptions options = DashScopeImageOptions.builder()
|
ImageOptions options = DashScopeImageOptions.builder()
|
||||||
.withModel("wanx-v1")
|
.model("wan2.7-image")
|
||||||
.withHeight(256).withWidth(256)
|
// .withSize("2k")
|
||||||
|
.height(768).width(768)
|
||||||
|
.n(1)
|
||||||
.build();
|
.build();
|
||||||
ImagePrompt prompt = new ImagePrompt("中国长城!", options);
|
ImagePrompt prompt = new ImagePrompt("中国长城!", options);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,14 @@ public class DeliveryExpressTemplateFreeDO extends BaseDO {
|
||||||
/**
|
/**
|
||||||
* 包邮金额,单位:分
|
* 包邮金额,单位:分
|
||||||
*
|
*
|
||||||
* 订单总金额 > 包邮金额时,才免运费
|
* 订单总金额 >= 包邮金额时,才免运费
|
||||||
*/
|
*/
|
||||||
private Integer freePrice;
|
private Integer freePrice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包邮件数
|
* 包邮件数
|
||||||
*
|
*
|
||||||
* 订单总件数 > 包邮件数时,才免运费
|
* 订单总件数 >= 包邮件数时,才免运费
|
||||||
*/
|
*/
|
||||||
private Integer freeCount;
|
private Integer freeCount;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,14 @@ public class DeliveryExpressTemplateRespBO {
|
||||||
/**
|
/**
|
||||||
* 包邮金额,单位:分
|
* 包邮金额,单位:分
|
||||||
*
|
*
|
||||||
* 订单总金额 > 包邮金额时,才免运费
|
* 订单总金额 >= 包邮金额时,才免运费
|
||||||
*/
|
*/
|
||||||
private Integer freePrice;
|
private Integer freePrice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包邮件数
|
* 包邮件数
|
||||||
*
|
*
|
||||||
* 订单总件数 > 包邮件数时,才免运费
|
* 订单总件数 >= 包邮件数时,才免运费
|
||||||
*/
|
*/
|
||||||
private Integer freeCount;
|
private Integer freeCount;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import java.util.Collections;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
@ -34,6 +35,7 @@ import java.util.Map;
|
||||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 用户")
|
@Tag(name = "管理后台 - 用户")
|
||||||
@RestController
|
@RestController
|
||||||
|
|
@ -112,6 +114,20 @@ public class UserController {
|
||||||
pageResult.getTotal()));
|
pageResult.getTotal()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
@Operation(summary = "获得用户详情列表")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true, example = "[1024]")
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:user:query')")
|
||||||
|
public CommonResult<List<UserRespVO>> getUserList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
List<AdminUserDO> list = userService.getUserList(ids);
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
|
}
|
||||||
|
// 拼接数据
|
||||||
|
Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertSet(list, AdminUserDO::getDeptId));
|
||||||
|
return success(UserConvert.INSTANCE.convertList(list, deptMap));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping({"/list-all-simple", "/simple-list"})
|
@GetMapping({"/list-all-simple", "/simple-list"})
|
||||||
@Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项")
|
@Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项")
|
||||||
public CommonResult<List<UserSimpleRespVO>> getSimpleUserList() {
|
public CommonResult<List<UserSimpleRespVO>> getSimpleUserList() {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -37,4 +39,14 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
|
||||||
OAuth2AccessTokenDO::getUserType, userType);
|
OAuth2AccessTokenDO::getUserType, userType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理删除指定过期时间之前的访问令牌
|
||||||
|
*
|
||||||
|
* @param expiresTime 最大时间
|
||||||
|
* @param limit 删除条数,防止一次删除太多
|
||||||
|
* @return 删除条数
|
||||||
|
*/
|
||||||
|
@Delete("DELETE FROM system_oauth2_access_token WHERE expires_time < #{expiresTime} LIMIT #{limit}")
|
||||||
|
Integer deleteByExpiresTimeLt(@Param("expiresTime") LocalDateTime expiresTime, @Param("limit") Integer limit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
|
public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
|
||||||
|
|
@ -19,4 +23,13 @@ public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshToken
|
||||||
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理删除指定过期时间之前的刷新令牌
|
||||||
|
*
|
||||||
|
* @param expiresTime 最大时间
|
||||||
|
* @param limit 删除条数,防止一次删除太多
|
||||||
|
* @return 删除条数
|
||||||
|
*/
|
||||||
|
@Delete("DELETE FROM system_oauth2_refresh_token WHERE expires_time < #{expiresTime} LIMIT #{limit}")
|
||||||
|
Integer deleteByExpiresTimeLt(@Param("expiresTime") LocalDateTime expiresTime, @Param("limit") Integer limit);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,19 @@ public interface OAuth2TokenService {
|
||||||
*/
|
*/
|
||||||
PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO);
|
PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期 exceedDay 天的刷新令牌
|
||||||
|
*
|
||||||
|
* @param exceedDay 过期多少天就进行清理
|
||||||
|
* @param deleteLimit 清理的间隔条数
|
||||||
|
*/
|
||||||
|
Integer cleanRefreshToken(Integer exceedDay, Integer deleteLimit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期 exceedDay 天的访问令牌
|
||||||
|
*
|
||||||
|
* @param exceedDay 过期多少天就进行清理
|
||||||
|
* @param deleteLimit 清理的间隔条数
|
||||||
|
*/
|
||||||
|
Integer cleanAccessToken(Integer exceedDay, Integer deleteLimit);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||||
oauth2AccessTokenRedisDAO.delete(accessToken);
|
oauth2AccessTokenRedisDAO.delete(accessToken);
|
||||||
// 删除刷新令牌
|
// 删除刷新令牌
|
||||||
oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
|
oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
|
||||||
|
oauth2AccessTokenRedisDAO.delete(accessTokenDO.getRefreshToken());
|
||||||
return accessTokenDO;
|
return accessTokenDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +167,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||||
oauth2AccessTokenRedisDAO.delete(accessToken.getAccessToken());
|
oauth2AccessTokenRedisDAO.delete(accessToken.getAccessToken());
|
||||||
// 删除刷新令牌
|
// 删除刷新令牌
|
||||||
oauth2RefreshTokenMapper.deleteByRefreshToken(accessToken.getRefreshToken());
|
oauth2RefreshTokenMapper.deleteByRefreshToken(accessToken.getRefreshToken());
|
||||||
|
oauth2AccessTokenRedisDAO.delete(accessToken.getRefreshToken());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,4 +243,35 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||||
return IdUtil.fastSimpleUUID();
|
return IdUtil.fastSimpleUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer cleanRefreshToken(Integer exceedDay, Integer deleteLimit) {
|
||||||
|
int count = 0;
|
||||||
|
LocalDateTime expireDate = LocalDateTime.now().minusDays(exceedDay);
|
||||||
|
// 循环删除,直到没有满足条件的数据
|
||||||
|
for (int i = 0; i < Short.MAX_VALUE; i++) {
|
||||||
|
int deleteCount = oauth2RefreshTokenMapper.deleteByExpiresTimeLt(expireDate, deleteLimit);
|
||||||
|
count += deleteCount;
|
||||||
|
// 达到删除预期条数,说明到底了
|
||||||
|
if (deleteCount < deleteLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer cleanAccessToken(Integer exceedDay, Integer deleteLimit) {
|
||||||
|
int count = 0;
|
||||||
|
LocalDateTime expireDate = LocalDateTime.now().minusDays(exceedDay);
|
||||||
|
// 循环删除,直到没有满足条件的数据
|
||||||
|
for (int i = 0; i < Short.MAX_VALUE; i++) {
|
||||||
|
int deleteCount = oauth2AccessTokenMapper.deleteByExpiresTimeLt(expireDate, deleteLimit);
|
||||||
|
count += deleteCount;
|
||||||
|
// 达到删除预期条数,说明到底了
|
||||||
|
if (deleteCount < deleteLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue