# Conflicts:
#	yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java
master
YunaiV 2026-05-10 09:41:19 +08:00
commit 9db40c8b80
14 changed files with 187 additions and 29 deletions

View File

@ -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({

View File

@ -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()

View File

@ -98,6 +98,13 @@ public interface AiKnowledgeSegmentService {
*/ */
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
/**
*
*
* @param id
*/
void deleteKnowledgeSegment(Long id);
/** /**
* *
* *

View File

@ -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. 查询需要删除的段落

View File

@ -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");
} }
} }

View File

@ -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,

View File

@ -12,23 +12,34 @@ import org.springframework.ai.image.ImageResponse;
/** /**
* {@link DashScopeImageModel} * {@link DashScopeImageModel}
* *
* TODO @spring-ai-alibaba-dashscope1.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);

View File

@ -43,14 +43,14 @@ public class DeliveryExpressTemplateFreeDO extends BaseDO {
/** /**
* *
* *
* > * >=
*/ */
private Integer freePrice; private Integer freePrice;
/** /**
* *
* *
* > * >=
*/ */
private Integer freeCount; private Integer freeCount;

View File

@ -65,14 +65,14 @@ public class DeliveryExpressTemplateRespBO {
/** /**
* *
* *
* > * >=
*/ */
private Integer freePrice; private Integer freePrice;
/** /**
* *
* *
* > * >=
*/ */
private Integer freeCount; private Integer freeCount;
} }

View File

@ -18,14 +18,15 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
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.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -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() {

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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;
}
} }