mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-18 06:13:39 +00:00
Compare commits
51 Commits
affdc5e3a6
...
bd9ffb10a9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9ffb10a9 | ||
|
|
bb9c85ac3c | ||
|
|
70ca78d935 | ||
|
|
1af8c4ee50 | ||
|
|
b9276c5dcc | ||
|
|
d1e98a2001 | ||
|
|
baf065a294 | ||
|
|
842a39d6d2 | ||
|
|
9fba91c35f | ||
|
|
498135b7fd | ||
|
|
c3ab13ae67 | ||
|
|
1638b9dd75 | ||
|
|
4434d8346c | ||
|
|
119483df86 | ||
|
|
98f7e3ada2 | ||
|
|
50d9e0e843 | ||
|
|
2871cf7630 | ||
|
|
07cb351807 | ||
|
|
951ee6bd8a | ||
|
|
5b6605c345 | ||
|
|
5db116ec88 | ||
|
|
645c754dd0 | ||
|
|
8751bb5104 | ||
|
|
8d0c557bdb | ||
|
|
5ac785c570 | ||
|
|
60145f9291 | ||
|
|
d7b89cd1b3 | ||
|
|
5264b47c2f | ||
|
|
e9cd9e84d4 | ||
|
|
b52f7a7112 | ||
|
|
2abdf762c1 | ||
|
|
099c94e3cb | ||
|
|
047044eb06 | ||
|
|
7108727395 | ||
|
|
d3732a155d | ||
|
|
a0db91ebe6 | ||
|
|
e83d70e9c3 | ||
|
|
5eb166839a | ||
|
|
e27a6cb738 | ||
|
|
d964e86b23 | ||
|
|
86d7eab5b5 | ||
|
|
fa5ad8caf6 | ||
|
|
e5011e0dd9 | ||
|
|
5fe8bd7706 | ||
|
|
0f28e1f3f6 | ||
|
|
503f86644e | ||
|
|
579beb6833 | ||
|
|
22c0c733f6 | ||
|
|
9f4a2256b4 | ||
|
|
5a4d76ac09 | ||
|
|
838a393abc |
@@ -99,6 +99,10 @@
|
|||||||
|
|
||||||
> 💡 **小贴士**:建议将 PR 提交到 GitHub,我们会自动同步到其他代码托管平台
|
> 💡 **小贴士**:建议将 PR 提交到 GitHub,我们会自动同步到其他代码托管平台
|
||||||
|
|
||||||
|
<a href="https://openomy.com/ageerle/ruoyi-ai" target="_blank" style="display: block; width: 100%;" align="center">
|
||||||
|
<img src="https://openomy.com/svg?repo=ageerle/ruoyi-ai&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## 📄 开源协议
|
## 📄 开源协议
|
||||||
|
|
||||||
本项目采用 **MIT 开源协议**,详情请查看 [LICENSE](LICENSE) 文件。
|
本项目采用 **MIT 开源协议**,详情请查看 [LICENSE](LICENSE) 文件。
|
||||||
|
|||||||
@@ -67,4 +67,19 @@ public class ChatRequest {
|
|||||||
*/
|
*/
|
||||||
private Long uuid;
|
private Long uuid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有附件
|
||||||
|
*/
|
||||||
|
private Boolean hasAttachment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动切换模型
|
||||||
|
*/
|
||||||
|
private Boolean autoSelectModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话令牌(为避免在非Web线程中获取Request,入口处注入)
|
||||||
|
*/
|
||||||
|
private String token;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.ruoyi.core.domain.BaseEntity;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天模型对象 chat_model
|
* 聊天模型对象 chat_model
|
||||||
*
|
*
|
||||||
@@ -75,6 +76,11 @@ public class ChatModel extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ public class ChatModelBo extends BaseEntity {
|
|||||||
@NotBlank(message = "请求地址不能为空", groups = { AddGroup.class, EditGroup.class })
|
@NotBlank(message = "请求地址不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||||
private String apiHost;
|
private String apiHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密钥
|
* 密钥
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import java.io.Serializable;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天模型视图对象 chat_model
|
* 聊天模型视图对象 chat_model
|
||||||
*
|
*
|
||||||
@@ -90,10 +89,17 @@ public class ChatModelVo implements Serializable {
|
|||||||
@ExcelProperty(value = "密钥")
|
@ExcelProperty(value = "密钥")
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级(值越大优先级越高)
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "优先级")
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "备注")
|
@ExcelProperty(value = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
}
|
|
||||||
|
}
|
||||||
@@ -57,6 +57,17 @@ public interface IChatModelService {
|
|||||||
* 通过模型分类获取模型信息
|
* 通过模型分类获取模型信息
|
||||||
*/
|
*/
|
||||||
ChatModelVo selectModelByCategory(String image);
|
ChatModelVo selectModelByCategory(String image);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过模型分类获取优先级最高的模型信息
|
||||||
|
*/
|
||||||
|
ChatModelVo selectModelByCategoryWithHighestPriority(String category);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在同一分类下,查找优先级小于当前优先级的最高优先级模型(用于降级)。
|
||||||
|
*/
|
||||||
|
ChatModelVo selectFallbackModelByCategoryAndLessPriority(String category, Integer currentPriority);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取ppt模型信息
|
* 获取ppt模型信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -46,4 +46,11 @@ public interface IPromptTemplateService {
|
|||||||
* 校验并批量删除提示词模板信息
|
* 校验并批量删除提示词模板信息
|
||||||
*/
|
*/
|
||||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类查询提示词模板
|
||||||
|
*
|
||||||
|
* @param category 分类
|
||||||
|
*/
|
||||||
|
PromptTemplateVo queryByCategory(String category);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,33 @@ public class ChatModelServiceImpl implements IChatModelService {
|
|||||||
public ChatModelVo selectModelByCategory(String category) {
|
public ChatModelVo selectModelByCategory(String category) {
|
||||||
return baseMapper.selectVoOne(Wrappers.<ChatModel>lambdaQuery().eq(ChatModel::getCategory, category));
|
return baseMapper.selectVoOne(Wrappers.<ChatModel>lambdaQuery().eq(ChatModel::getCategory, category));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过模型分类获取优先级最高的模型信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ChatModelVo selectModelByCategoryWithHighestPriority(String category) {
|
||||||
|
return baseMapper.selectVoOne(
|
||||||
|
Wrappers.<ChatModel>lambdaQuery()
|
||||||
|
.eq(ChatModel::getCategory, category)
|
||||||
|
.orderByDesc(ChatModel::getPriority)
|
||||||
|
.last("LIMIT 1")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在同一分类下,查找优先级小于当前优先级的最高优先级模型(用于降级)。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ChatModelVo selectFallbackModelByCategoryAndLessPriority(String category, Integer currentPriority) {
|
||||||
|
return baseMapper.selectVoOne(
|
||||||
|
Wrappers.<ChatModel>lambdaQuery()
|
||||||
|
.eq(ChatModel::getCategory, category)
|
||||||
|
.lt(ChatModel::getPriority, currentPriority)
|
||||||
|
.orderByDesc(ChatModel::getPriority)
|
||||||
|
.last("LIMIT 1")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChatModel getPPT() {
|
public ChatModel getPPT() {
|
||||||
|
|||||||
@@ -109,4 +109,13 @@ public class PromptTemplateServiceImpl implements IPromptTemplateService {
|
|||||||
}
|
}
|
||||||
return baseMapper.deleteBatchIds(ids) > 0;
|
return baseMapper.deleteBatchIds(ids) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PromptTemplateVo queryByCategory(String category) {
|
||||||
|
LambdaQueryWrapper<PromptTemplate> queryWrapper = Wrappers.lambdaQuery(PromptTemplate.class);
|
||||||
|
queryWrapper.eq(PromptTemplate::getCategory, category);
|
||||||
|
queryWrapper.orderByDesc(PromptTemplate::getUpdateTime);
|
||||||
|
queryWrapper.last("limit 1");
|
||||||
|
return baseMapper.selectVoOne(queryWrapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public interface IKnowledgeInfoService {
|
|||||||
/**
|
/**
|
||||||
* 查询知识库列表
|
* 查询知识库列表
|
||||||
*/
|
*/
|
||||||
TableDataInfo<KnowledgeInfoVo> queryPageListByRole(PageQuery pageQuery);
|
TableDataInfo<KnowledgeInfoVo> queryPageListByRole(KnowledgeInfoBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询知识库列表
|
* 查询知识库列表
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class VectorStoreServiceImpl implements VectorStoreService {
|
|||||||
|
|
||||||
private final ConfigService configService;
|
private final ConfigService configService;
|
||||||
|
|
||||||
private EmbeddingStore<TextSegment> embeddingStore;
|
// private EmbeddingStore<TextSegment> embeddingStore;
|
||||||
private WeaviateClient client;
|
private WeaviateClient client;
|
||||||
|
|
||||||
|
|
||||||
@@ -82,14 +82,14 @@ public class VectorStoreServiceImpl implements VectorStoreService {
|
|||||||
log.info("Schema 创建成功: {}", className);
|
log.info("Schema 创建成功: {}", className);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
embeddingStore = WeaviateEmbeddingStore.builder()
|
// embeddingStore = WeaviateEmbeddingStore.builder()
|
||||||
.scheme(protocol)
|
// .scheme(protocol)
|
||||||
.host(host)
|
// .host(host)
|
||||||
.objectClass(className)
|
// .objectClass(className)
|
||||||
.scheme(protocol)
|
// .scheme(protocol)
|
||||||
.avoidDups(true)
|
// .avoidDups(true)
|
||||||
.consistencyLevel("ALL")
|
// .consistencyLevel("ALL")
|
||||||
.build();
|
// .build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -148,7 +148,7 @@ public class VectorStoreServiceImpl implements VectorStoreService {
|
|||||||
String graphQLQuery = String.format(
|
String graphQLQuery = String.format(
|
||||||
"{\n" +
|
"{\n" +
|
||||||
" Get {\n" +
|
" Get {\n" +
|
||||||
" %s(nearVector: {vector: [%s], certainty: %f} limit: %d) {\n" +
|
" %s(nearVector: {vector: [%s]} limit: %d) {\n" +
|
||||||
" text\n" +
|
" text\n" +
|
||||||
" fid\n" +
|
" fid\n" +
|
||||||
" kid\n" +
|
" kid\n" +
|
||||||
|
|||||||
@@ -16,6 +16,15 @@ import java.util.List;
|
|||||||
public interface ISysDictTypeService {
|
public interface ISysDictTypeService {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all dictionary types based on the specified conditions
|
||||||
|
*
|
||||||
|
* @param dictType The business object containing query conditions for dictionary types
|
||||||
|
* @return TableDataInfo containing a list of SysDictTypeVo objects that match the query criteria
|
||||||
|
*/
|
||||||
|
TableDataInfo<SysDictTypeVo> selectAll(SysDictTypeBo dictType);
|
||||||
|
|
||||||
TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery);
|
TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.ruoyi.common.core.constant.CacheConstants;
|
import org.ruoyi.common.core.constant.CacheConstants;
|
||||||
import org.ruoyi.common.core.constant.CacheNames;
|
import org.ruoyi.common.core.constant.CacheNames;
|
||||||
|
import org.ruoyi.common.core.constant.HttpStatus;
|
||||||
import org.ruoyi.common.core.exception.ServiceException;
|
import org.ruoyi.common.core.exception.ServiceException;
|
||||||
import org.ruoyi.common.core.service.DictService;
|
import org.ruoyi.common.core.service.DictService;
|
||||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
import org.ruoyi.common.core.utils.MapstructUtils;
|
||||||
@@ -50,6 +51,18 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
|||||||
private final SysDictTypeMapper baseMapper;
|
private final SysDictTypeMapper baseMapper;
|
||||||
private final SysDictDataMapper dictDataMapper;
|
private final SysDictDataMapper dictDataMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<SysDictTypeVo> selectAll(SysDictTypeBo dictType) {
|
||||||
|
LambdaQueryWrapper<SysDictType> lqw = buildQueryWrapper(dictType);
|
||||||
|
// 2. 查询所有数据(不分页)
|
||||||
|
List<SysDictTypeVo> list = baseMapper.selectVoList(lqw);
|
||||||
|
TableDataInfo<SysDictTypeVo> rspData = new TableDataInfo<>();
|
||||||
|
rspData.setCode(HttpStatus.SUCCESS); // 200
|
||||||
|
rspData.setMsg("查询成功");
|
||||||
|
rspData.setRows(list);
|
||||||
|
rspData.setTotal(list.size()); // 总数为列表大小
|
||||||
|
return rspData;
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) {
|
public TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) {
|
||||||
LambdaQueryWrapper<SysDictType> lqw = buildQueryWrapper(dictType);
|
LambdaQueryWrapper<SysDictType> lqw = buildQueryWrapper(dictType);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import jakarta.validation.constraints.NotNull;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.ruoyi.chat.config.KnowledgeRoleConfig;
|
import org.ruoyi.chat.config.KnowledgeRoleConfig;
|
||||||
import org.ruoyi.common.core.domain.R;
|
import org.ruoyi.common.core.domain.R;
|
||||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
import org.ruoyi.common.core.validate.AddGroup;
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||||
import org.ruoyi.common.log.annotation.Log;
|
import org.ruoyi.common.log.annotation.Log;
|
||||||
@@ -31,6 +30,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库管理
|
* 知识库管理
|
||||||
@@ -60,7 +60,9 @@ public class KnowledgeController extends BaseController {
|
|||||||
if (!StpUtil.isLogin()) {
|
if (!StpUtil.isLogin()) {
|
||||||
throw new SecurityException("请先去登录!");
|
throw new SecurityException("请先去登录!");
|
||||||
}
|
}
|
||||||
bo.setUid(LoginHelper.getUserId());
|
if (!Objects.equals(LoginHelper.getUserId(), 1L)) {
|
||||||
|
bo.setUid(LoginHelper.getUserId());
|
||||||
|
}
|
||||||
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,14 +74,16 @@ public class KnowledgeController extends BaseController {
|
|||||||
if (!StpUtil.isLogin()) {
|
if (!StpUtil.isLogin()) {
|
||||||
throw new SecurityException("请先去登录!");
|
throw new SecurityException("请先去登录!");
|
||||||
}
|
}
|
||||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
|
||||||
|
|
||||||
// 管理员跳过权限
|
// 管理员跳过权限
|
||||||
if (loginUser.getUserId().equals(1L) || !knowledgeRoleConfig.getEnable()) {
|
if (Objects.equals(LoginHelper.getUserId(), 1L)) {
|
||||||
|
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||||
|
} else if (!knowledgeRoleConfig.getEnable()) {
|
||||||
bo.setUid(LoginHelper.getUserId());
|
bo.setUid(LoginHelper.getUserId());
|
||||||
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||||
} else {
|
} else {
|
||||||
return knowledgeInfoService.queryPageListByRole(pageQuery);
|
bo.setUid(LoginHelper.getUserId());
|
||||||
|
return knowledgeInfoService.queryPageListByRole(bo, pageQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.ruoyi.chat.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示词模板分类
|
||||||
|
*
|
||||||
|
* @author evo
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum promptTemplateEnum {
|
||||||
|
CHAT(1, "chat"),
|
||||||
|
VECTOR(2, "vector"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -21,12 +23,18 @@ import java.util.Objects;
|
|||||||
public class FastGPTSSEEventSourceListener extends EventSourceListener {
|
public class FastGPTSSEEventSourceListener extends EventSourceListener {
|
||||||
|
|
||||||
private SseEmitter emitter;
|
private SseEmitter emitter;
|
||||||
|
private Long sessionId;
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public FastGPTSSEEventSourceListener(SseEmitter emitter) {
|
public FastGPTSSEEventSourceListener(SseEmitter emitter) {
|
||||||
this.emitter = emitter;
|
this.emitter = emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FastGPTSSEEventSourceListener(SseEmitter emitter, Long sessionId) {
|
||||||
|
this.emitter = emitter;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(EventSource eventSource, Response response) {
|
public void onOpen(EventSource eventSource, Response response) {
|
||||||
log.info("FastGPT sse连接成功");
|
log.info("FastGPT sse连接成功");
|
||||||
@@ -40,6 +48,7 @@ public class FastGPTSSEEventSourceListener extends EventSourceListener {
|
|||||||
if ("flowResponses".equals(type)){
|
if ("flowResponses".equals(type)){
|
||||||
emitter.send(data);
|
emitter.send(data);
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
} else {
|
} else {
|
||||||
emitter.send(data);
|
emitter.send(data);
|
||||||
}
|
}
|
||||||
@@ -57,13 +66,20 @@ public class FastGPTSSEEventSourceListener extends EventSourceListener {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void onFailure(EventSource eventSource, Throwable t, Response response) {
|
public void onFailure(EventSource eventSource, Throwable t, Response response) {
|
||||||
if (Objects.isNull(response)) {
|
if (Objects.isNull(response)) {
|
||||||
|
SSEUtil.sendErrorEvent(emitter, t != null ? t.getMessage() : "SSE连接失败");
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ResponseBody body = response.body();
|
ResponseBody body = response.body();
|
||||||
if (Objects.nonNull(body)) {
|
if (Objects.nonNull(body)) {
|
||||||
log.error("FastGPT sse连接异常data:{},异常:{}", body.string(), t);
|
String msg = body.string();
|
||||||
|
log.error("FastGPT sse连接异常data:{},异常:{}", msg, t);
|
||||||
|
SSEUtil.sendErrorEvent(emitter, msg);
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
} else {
|
} else {
|
||||||
log.error("FastGPT sse连接异常data:{},异常:{}", response, t);
|
log.error("FastGPT sse连接异常data:{},异常:{}", response, t);
|
||||||
|
SSEUtil.sendErrorEvent(emitter, String.valueOf(response));
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
}
|
}
|
||||||
eventSource.cancel();
|
eventSource.cancel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import org.ruoyi.common.core.utils.SpringUtils;
|
|||||||
import org.ruoyi.common.core.utils.StringUtils;
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -44,12 +46,15 @@ public class SSEEventSourceListener extends EventSourceListener {
|
|||||||
|
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
|
private boolean retryEnabled;
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public SSEEventSourceListener(SseEmitter emitter,Long userId,Long sessionId, String token) {
|
public SSEEventSourceListener(SseEmitter emitter,Long userId,Long sessionId, String token, boolean retryEnabled) {
|
||||||
this.emitter = emitter;
|
this.emitter = emitter;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
|
this.retryEnabled = retryEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -77,6 +82,8 @@ public class SSEEventSourceListener extends EventSourceListener {
|
|||||||
if ("[DONE]".equals(data)) {
|
if ("[DONE]".equals(data)) {
|
||||||
//成功响应
|
//成功响应
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
|
// 清理失败回调(以 emitter 为键)
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
// 扣除费用
|
// 扣除费用
|
||||||
ChatRequest chatRequest = new ChatRequest();
|
ChatRequest chatRequest = new ChatRequest();
|
||||||
// 设置对话角色
|
// 设置对话角色
|
||||||
@@ -113,19 +120,38 @@ public class SSEEventSourceListener extends EventSourceListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onClosed(EventSource eventSource) {
|
public void onClosed(EventSource eventSource) {
|
||||||
log.info("OpenAI关闭sse连接...");
|
log.info("OpenAI关闭sse连接...");
|
||||||
|
// 清理失败回调
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(EventSource eventSource, Throwable t, Response response) {
|
public void onFailure(EventSource eventSource, Throwable t, Response response) {
|
||||||
if (Objects.isNull(response)) {
|
if (Objects.isNull(response)) {
|
||||||
|
// 透传错误到前端
|
||||||
|
SSEUtil.sendErrorEvent(emitter, t != null ? t.getMessage() : "SSE连接失败");
|
||||||
|
if (retryEnabled) {
|
||||||
|
// 通知重试(以 emitter 为键)
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
|
} else {
|
||||||
|
emitter.complete();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ResponseBody body = response.body();
|
ResponseBody body = response.body();
|
||||||
if (Objects.nonNull(body)) {
|
if (Objects.nonNull(body)) {
|
||||||
log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t);
|
String msg = body.string();
|
||||||
|
log.error("OpenAI sse连接异常data:{},异常:{}", msg, t);
|
||||||
|
SSEUtil.sendErrorEvent(emitter, msg);
|
||||||
} else {
|
} else {
|
||||||
log.error("OpenAI sse连接异常data:{},异常:{}", response, t);
|
log.error("OpenAI sse连接异常data:{},异常:{}", response, t);
|
||||||
|
SSEUtil.sendErrorEvent(emitter, String.valueOf(response));
|
||||||
|
}
|
||||||
|
if (retryEnabled) {
|
||||||
|
// 通知重试
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
|
} else {
|
||||||
|
emitter.complete();
|
||||||
}
|
}
|
||||||
eventSource.cancel();
|
eventSource.cancel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扣子聊天管理
|
* 扣子聊天管理
|
||||||
@@ -53,19 +55,25 @@ public class CozeServiceImpl implements IChatService {
|
|||||||
Flowable<ChatEvent> resp = coze.chat().stream(req);
|
Flowable<ChatEvent> resp = coze.chat().stream(req);
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(10);
|
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
resp.blockingForEach(
|
try {
|
||||||
event -> {
|
resp.blockingForEach(
|
||||||
if (ChatEventType.CONVERSATION_MESSAGE_DELTA.equals(event.getEvent())) {
|
event -> {
|
||||||
emitter.send(event.getMessage().getContent());
|
if (ChatEventType.CONVERSATION_MESSAGE_DELTA.equals(event.getEvent())) {
|
||||||
log.info("coze: {}", event.getMessage().getContent());
|
emitter.send(event.getMessage().getContent());
|
||||||
|
log.info("coze: {}", event.getMessage().getContent());
|
||||||
|
}
|
||||||
|
if (ChatEventType.CONVERSATION_CHAT_COMPLETED.equals(event.getEvent())) {
|
||||||
|
emitter.complete();
|
||||||
|
log.info("Token usage: {}", event.getChat().getUsage().getTokenCount());
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ChatEventType.CONVERSATION_CHAT_COMPLETED.equals(event.getEvent())) {
|
);
|
||||||
emitter.complete();
|
} catch (Exception ex) {
|
||||||
log.info("Token usage: {}", event.getChat().getUsage().getTokenCount());
|
ChatServiceHelper.onStreamError(emitter, ex.getMessage());
|
||||||
}
|
} finally {
|
||||||
}
|
coze.shutdownExecutor();
|
||||||
);
|
}
|
||||||
coze.shutdownExecutor();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import lombok.SneakyThrows;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.ruoyi.chat.enums.ChatModeType;
|
import org.ruoyi.chat.enums.ChatModeType;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
import org.ruoyi.common.chat.request.ChatRequest;
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
import org.ruoyi.domain.vo.ChatModelVo;
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
import org.ruoyi.service.IChatModelService;
|
import org.ruoyi.service.IChatModelService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deepseek
|
* deepseek
|
||||||
*/
|
*/
|
||||||
@@ -57,11 +57,14 @@ public class DeepSeekChatImpl implements IChatService {
|
|||||||
@Override
|
@Override
|
||||||
public void onError(Throwable error) {
|
public void onError(Throwable error) {
|
||||||
System.err.println("错误: " + error.getMessage());
|
System.err.println("错误: " + error.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, error.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("deepseek请求失败:{}", e.getMessage());
|
log.error("deepseek请求失败:{}", e.getMessage());
|
||||||
|
// 同步异常直接通知失败
|
||||||
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ import org.ruoyi.service.IChatSessionService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dify 聊天管理
|
* dify 聊天管理
|
||||||
@@ -112,20 +114,24 @@ public class DifyServiceImpl implements IChatService {
|
|||||||
chatRequestResponse.setSessionId(chatRequest.getSessionId());
|
chatRequestResponse.setSessionId(chatRequest.getSessionId());
|
||||||
chatRequestResponse.setPrompt(respMessage.toString());
|
chatRequestResponse.setPrompt(respMessage.toString());
|
||||||
chatCostService.deductToken(chatRequestResponse);
|
chatCostService.deductToken(chatRequestResponse);
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorEvent event) {
|
public void onError(ErrorEvent event) {
|
||||||
System.err.println("错误: " + event.getMessage());
|
System.err.println("错误: " + event.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, event.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onException(Throwable throwable) {
|
public void onException(Throwable throwable) {
|
||||||
System.err.println("异常: " + throwable.getMessage());
|
System.err.println("异常: " + throwable.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, throwable.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("dify请求失败:{}", e.getMessage());
|
log.error("dify请求失败:{}", e.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class FastGPTServiceImpl implements IChatService {
|
|||||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
|
OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
|
||||||
List<Message> messages = chatRequest.getMessages();
|
List<Message> messages = chatRequest.getMessages();
|
||||||
FastGPTSSEEventSourceListener listener = new FastGPTSSEEventSourceListener(emitter);
|
FastGPTSSEEventSourceListener listener = new FastGPTSSEEventSourceListener(emitter, chatRequest.getSessionId());
|
||||||
FastGPTChatCompletion completion = FastGPTChatCompletion
|
FastGPTChatCompletion completion = FastGPTChatCompletion
|
||||||
.builder()
|
.builder()
|
||||||
.messages(messages)
|
.messages(messages)
|
||||||
@@ -41,7 +41,12 @@ public class FastGPTServiceImpl implements IChatService {
|
|||||||
.detail(true)
|
.detail(true)
|
||||||
.stream(true)
|
.stream(true)
|
||||||
.build();
|
.build();
|
||||||
openAiStreamClient.streamChatCompletion(completion, listener);
|
try {
|
||||||
|
openAiStreamClient.streamChatCompletion(completion, listener);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
org.ruoyi.chat.support.RetryNotifier.notifyFailure(chatRequest.getSessionId());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片识别模型
|
* 图片识别模型
|
||||||
@@ -128,10 +129,10 @@ public class ImageServiceImpl implements IChatService {
|
|||||||
OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
|
OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
|
||||||
List<Message> messages = chatRequest.getMessages();
|
List<Message> messages = chatRequest.getMessages();
|
||||||
|
|
||||||
// 获取会话token
|
// 获取会话token(从入口透传,避免非Web线程取值报错)
|
||||||
String token = StpUtil.getTokenValue();
|
String token = chatRequest.getToken();
|
||||||
// 创建 SSE 事件源监听器
|
// 创建 SSE 事件源监听器
|
||||||
SSEEventSourceListener listener = new SSEEventSourceListener(emitter, chatRequest.getUserId(), chatRequest.getSessionId(), token);
|
SSEEventSourceListener listener = ChatServiceHelper.createOpenAiListener(emitter, chatRequest);
|
||||||
|
|
||||||
// 构建聊天完成请求
|
// 构建聊天完成请求
|
||||||
ChatCompletion completion = ChatCompletion
|
ChatCompletion completion = ChatCompletion
|
||||||
@@ -142,7 +143,12 @@ public class ImageServiceImpl implements IChatService {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 发起流式聊天完成请求
|
// 发起流式聊天完成请求
|
||||||
openAiStreamClient.streamChatCompletion(completion, listener);
|
try {
|
||||||
|
openAiStreamClient.streamChatCompletion(completion, listener);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ChatServiceHelper.onStreamError(emitter, ex.getMessage());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,13 +67,14 @@ public class OllamaServiceImpl implements IChatService {
|
|||||||
try {
|
try {
|
||||||
emitter.send(substr);
|
emitter.send(substr);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
SSEUtil.sendErrorEvent(emitter, e.getMessage());
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
api.chat(requestModel, streamHandler);
|
api.chat(requestModel, streamHandler);
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
SSEUtil.sendErrorEvent(emitter, e.getMessage());
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package org.ruoyi.chat.service.chat.impl;
|
package org.ruoyi.chat.service.chat.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
import io.modelcontextprotocol.client.McpSyncClient;
|
import io.modelcontextprotocol.client.McpSyncClient;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.ruoyi.chat.config.ChatConfig;
|
import org.ruoyi.chat.config.ChatConfig;
|
||||||
import org.ruoyi.chat.enums.ChatModeType;
|
import org.ruoyi.chat.enums.ChatModeType;
|
||||||
import org.ruoyi.chat.listener.SSEEventSourceListener;
|
import org.ruoyi.chat.listener.SSEEventSourceListener;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
|
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
|
||||||
import org.ruoyi.common.chat.entity.chat.Message;
|
import org.ruoyi.common.chat.entity.chat.Message;
|
||||||
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
|
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
|
||||||
@@ -57,15 +57,19 @@ public class OpenAIServiceImpl implements IChatService {
|
|||||||
Message userMessage = Message.builder().content("工具返回信息:"+toolString).role(Message.Role.USER).build();
|
Message userMessage = Message.builder().content("工具返回信息:"+toolString).role(Message.Role.USER).build();
|
||||||
messages.add(userMessage);
|
messages.add(userMessage);
|
||||||
}
|
}
|
||||||
String token = StpUtil.getTokenValue();
|
SSEEventSourceListener listener = ChatServiceHelper.createOpenAiListener(emitter, chatRequest);
|
||||||
SSEEventSourceListener listener = new SSEEventSourceListener(emitter,chatRequest.getUserId(),chatRequest.getSessionId(), token);
|
|
||||||
ChatCompletion completion = ChatCompletion
|
ChatCompletion completion = ChatCompletion
|
||||||
.builder()
|
.builder()
|
||||||
.messages(messages)
|
.messages(messages)
|
||||||
.model(chatRequest.getModel())
|
.model(chatRequest.getModel())
|
||||||
.stream(true)
|
.stream(true)
|
||||||
.build();
|
.build();
|
||||||
openAiStreamClient.streamChatCompletion(completion, listener);
|
try {
|
||||||
|
openAiStreamClient.streamChatCompletion(completion, listener);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ChatServiceHelper.onStreamError(emitter, ex.getMessage());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.ruoyi.service.IChatModelService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,15 +52,18 @@ public class QianWenAiChatServiceImpl implements IChatService {
|
|||||||
public void onCompleteResponse(ChatResponse completeResponse) {
|
public void onCompleteResponse(ChatResponse completeResponse) {
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
log.info("消息结束,完整消息ID: {}", completeResponse);
|
log.info("消息结束,完整消息ID: {}", completeResponse);
|
||||||
|
org.ruoyi.chat.support.RetryNotifier.clear(emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable error) {
|
public void onError(Throwable error) {
|
||||||
error.printStackTrace();
|
error.printStackTrace();
|
||||||
|
ChatServiceHelper.onStreamError(emitter, error.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("千问请求失败:{}", e.getMessage());
|
log.error("千问请求失败:{}", e.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package org.ruoyi.chat.service.chat.impl;
|
package org.ruoyi.chat.service.chat.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
|
import org.ruoyi.chat.enums.promptTemplateEnum;
|
||||||
import org.ruoyi.chat.factory.ChatServiceFactory;
|
import org.ruoyi.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
import org.ruoyi.chat.service.chat.ISseService;
|
import org.ruoyi.chat.service.chat.ISseService;
|
||||||
|
import org.ruoyi.chat.support.ChatRetryHelper;
|
||||||
|
import org.ruoyi.chat.support.RetryNotifier;
|
||||||
import org.ruoyi.chat.util.SSEUtil;
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
||||||
import org.ruoyi.common.chat.entity.chat.Message;
|
import org.ruoyi.common.chat.entity.chat.Message;
|
||||||
@@ -25,9 +29,11 @@ import org.ruoyi.domain.bo.ChatSessionBo;
|
|||||||
import org.ruoyi.domain.bo.QueryVectorBo;
|
import org.ruoyi.domain.bo.QueryVectorBo;
|
||||||
import org.ruoyi.domain.vo.ChatModelVo;
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
import org.ruoyi.domain.vo.KnowledgeInfoVo;
|
import org.ruoyi.domain.vo.KnowledgeInfoVo;
|
||||||
|
import org.ruoyi.domain.vo.PromptTemplateVo;
|
||||||
import org.ruoyi.service.IChatModelService;
|
import org.ruoyi.service.IChatModelService;
|
||||||
import org.ruoyi.service.IChatSessionService;
|
import org.ruoyi.service.IChatSessionService;
|
||||||
import org.ruoyi.service.IKnowledgeInfoService;
|
import org.ruoyi.service.IKnowledgeInfoService;
|
||||||
|
import org.ruoyi.service.IPromptTemplateService;
|
||||||
import org.ruoyi.service.VectorStoreService;
|
import org.ruoyi.service.VectorStoreService;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
@@ -43,8 +49,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ageer
|
* @author ageer
|
||||||
@@ -70,19 +76,28 @@ public class SseServiceImpl implements ISseService {
|
|||||||
|
|
||||||
private ChatModelVo chatModelVo;
|
private ChatModelVo chatModelVo;
|
||||||
|
|
||||||
|
// 提示词模板服务
|
||||||
|
private final IPromptTemplateService promptTemplateService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
|
public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
|
||||||
SseEmitter sseEmitter = new SseEmitter(0L);
|
SseEmitter sseEmitter = new SseEmitter(0L);
|
||||||
try {
|
try {
|
||||||
|
// 记录当前会话令牌,供异步线程使用
|
||||||
|
try {
|
||||||
|
chatRequest.setToken(StpUtil.getTokenValue());
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 保底:无token场景下忽略
|
||||||
|
}
|
||||||
// 构建消息列表
|
// 构建消息列表
|
||||||
buildChatMessageList(chatRequest);
|
buildChatMessageList(chatRequest);
|
||||||
// 设置对话角色
|
// 设置对话角色
|
||||||
chatRequest.setRole(Message.Role.USER.getName());
|
chatRequest.setRole(Message.Role.USER.getName());
|
||||||
|
|
||||||
if(LoginHelper.isLogin()){
|
if (LoginHelper.isLogin()) {
|
||||||
|
|
||||||
// 设置用户id
|
// 设置用户id
|
||||||
chatRequest.setUserId(LoginHelper.getUserId());
|
chatRequest.setUserId(LoginHelper.getUserId());
|
||||||
|
|
||||||
|
|
||||||
@@ -91,10 +106,10 @@ public class SseServiceImpl implements ISseService {
|
|||||||
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||||
{
|
{
|
||||||
// 设置会话id
|
// 设置会话id
|
||||||
if (chatRequest.getUuid() == null){
|
if (chatRequest.getUuid() == null) {
|
||||||
//暂时随机生成会话id
|
//暂时随机生成会话id
|
||||||
chatRequest.setSessionId(System.currentTimeMillis());
|
chatRequest.setSessionId(System.currentTimeMillis());
|
||||||
}else{
|
} else {
|
||||||
//这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
//这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
||||||
chatRequest.setSessionId(chatRequest.getUuid());
|
chatRequest.setSessionId(chatRequest.getUuid());
|
||||||
}
|
}
|
||||||
@@ -104,7 +119,7 @@ public class SseServiceImpl implements ISseService {
|
|||||||
// 保存消息记录 并扣除费用
|
// 保存消息记录 并扣除费用
|
||||||
chatCostService.deductToken(chatRequest);
|
chatCostService.deductToken(chatRequest);
|
||||||
chatRequest.setUserId(chatCostService.getUserId());
|
chatRequest.setUserId(chatCostService.getUserId());
|
||||||
if(chatRequest.getSessionId()==null){
|
if (chatRequest.getSessionId() == null) {
|
||||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||||
chatSessionBo.setUserId(chatCostService.getUserId());
|
chatSessionBo.setUserId(chatCostService.getUserId());
|
||||||
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
||||||
@@ -113,16 +128,87 @@ public class SseServiceImpl implements ISseService {
|
|||||||
chatRequest.setSessionId(chatSessionBo.getId());
|
chatRequest.setSessionId(chatSessionBo.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 根据模型分类调用不同的处理逻辑
|
// 自动选择模型并获取对应的聊天服务
|
||||||
IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
|
IChatService chatService = autoSelectModelAndGetService(chatRequest);
|
||||||
chatService.chat(chatRequest, sseEmitter);
|
|
||||||
|
// 仅当 autoSelectModel = true 时,才启用重试与降级
|
||||||
|
if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
|
||||||
|
ChatModelVo currentModel = this.chatModelVo;
|
||||||
|
String currentCategory = currentModel.getCategory();
|
||||||
|
ChatRetryHelper.executeWithRetry(
|
||||||
|
currentModel,
|
||||||
|
currentCategory,
|
||||||
|
chatModelService,
|
||||||
|
sseEmitter,
|
||||||
|
(modelForTry, onFailure) -> {
|
||||||
|
// 替换请求中的模型名称
|
||||||
|
chatRequest.setModel(modelForTry.getModelName());
|
||||||
|
// 以 emitter 实例为唯一键注册失败回调
|
||||||
|
RetryNotifier.setFailureCallback(sseEmitter, onFailure);
|
||||||
|
try {
|
||||||
|
autoSelectServiceByCategoryAndInvoke(chatRequest, sseEmitter,
|
||||||
|
modelForTry.getCategory());
|
||||||
|
} finally {
|
||||||
|
// 不在此处清理,待下游结束/失败时清理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 不重试不降级,直接调用
|
||||||
|
chatService.chat(chatRequest, sseEmitter);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(),e);
|
log.error(e.getMessage(), e);
|
||||||
SSEUtil.sendErrorEvent(sseEmitter,e.getMessage());
|
SSEUtil.sendErrorEvent(sseEmitter, e.getMessage());
|
||||||
}
|
}
|
||||||
return sseEmitter;
|
return sseEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动选择模型并获取对应的聊天服务
|
||||||
|
*/
|
||||||
|
private IChatService autoSelectModelAndGetService(ChatRequest chatRequest) {
|
||||||
|
try {
|
||||||
|
if (Boolean.TRUE.equals(chatRequest.getHasAttachment())) {
|
||||||
|
chatModelVo = selectModelByCategory("image");
|
||||||
|
} else if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
|
||||||
|
chatModelVo = selectModelByCategory("chat");
|
||||||
|
} else {
|
||||||
|
chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatModelVo == null) {
|
||||||
|
throw new IllegalStateException("未找到模型名称:" + chatRequest.getModel());
|
||||||
|
}
|
||||||
|
// 自动设置请求参数中的模型名称
|
||||||
|
chatRequest.setModel(chatModelVo.getModelName());
|
||||||
|
// 直接返回对应的聊天服务
|
||||||
|
return chatServiceFactory.getChatService(chatModelVo.getCategory());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("模型选择和服务获取失败: {}", e.getMessage(), e);
|
||||||
|
throw new IllegalStateException("模型选择和服务获取失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据给定分类获取服务并发起调用(避免在降级时重复选择模型)
|
||||||
|
*/
|
||||||
|
private void autoSelectServiceByCategoryAndInvoke(ChatRequest chatRequest, SseEmitter sseEmitter, String category) {
|
||||||
|
IChatService service = chatServiceFactory.getChatService(category);
|
||||||
|
service.chat(chatRequest, sseEmitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类选择优先级最高的模型
|
||||||
|
*/
|
||||||
|
private ChatModelVo selectModelByCategory(String category) {
|
||||||
|
ChatModelVo model = chatModelService.selectModelByCategoryWithHighestPriority(category);
|
||||||
|
if (model == null) {
|
||||||
|
throw new IllegalStateException("未找到" + category + "分类的模型配置");
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取对话标题
|
* 获取对话标题
|
||||||
*
|
*
|
||||||
@@ -141,69 +227,23 @@ public class SseServiceImpl implements ISseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建消息列表
|
* 构建消息列表
|
||||||
*/
|
*/
|
||||||
private void buildChatMessageList(ChatRequest chatRequest){
|
private void buildChatMessageList(ChatRequest chatRequest) {
|
||||||
String sysPrompt;
|
|
||||||
// 矫正模型名称 如果是gpt-image 则查询image类型模型 获取模型名称
|
|
||||||
if(chatRequest.getModel().equals("gpt-image")) {
|
|
||||||
chatModelVo = chatModelService.selectModelByCategory("image");
|
|
||||||
if (chatModelVo == null) {
|
|
||||||
log.error("未找到image类型的模型配置");
|
|
||||||
throw new IllegalStateException("未找到image类型的模型配置");
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
|
||||||
}
|
|
||||||
// 获取对话消息列表
|
|
||||||
List<Message> messages = chatRequest.getMessages();
|
List<Message> messages = chatRequest.getMessages();
|
||||||
// 查询向量库相关信息加入到上下文
|
|
||||||
if(StringUtils.isNotEmpty(chatRequest.getKid())){
|
|
||||||
List<Message> knMessages = new ArrayList<>();
|
|
||||||
String content = messages.get(messages.size() - 1).getContent().toString();
|
|
||||||
// 通过kid查询知识库信息
|
|
||||||
KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoService.queryById(Long.valueOf(chatRequest.getKid()));
|
|
||||||
// 查询向量模型配置信息
|
|
||||||
ChatModelVo chatModel = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName());
|
|
||||||
|
|
||||||
QueryVectorBo queryVectorBo = new QueryVectorBo();
|
// 处理知识库相关逻辑
|
||||||
queryVectorBo.setQuery(content);
|
String sysPrompt = processKnowledgeBase(chatRequest, messages);
|
||||||
queryVectorBo.setKid(chatRequest.getKid());
|
|
||||||
queryVectorBo.setApiKey(chatModel.getApiKey());
|
// 设置系统提示词
|
||||||
queryVectorBo.setBaseUrl(chatModel.getApiHost());
|
Message sysMessage = Message.builder()
|
||||||
queryVectorBo.setVectorModelName(knowledgeInfoVo.getVectorModelName());
|
.content(sysPrompt)
|
||||||
queryVectorBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName());
|
.role(Message.Role.SYSTEM)
|
||||||
queryVectorBo.setMaxResults(knowledgeInfoVo.getRetrieveLimit());
|
.build();
|
||||||
List<String> nearestList = vectorStoreService.getQueryVector(queryVectorBo);
|
messages.add(0, sysMessage);
|
||||||
for (String prompt : nearestList) {
|
|
||||||
Message userMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
|
|
||||||
knMessages.add(userMessage);
|
|
||||||
}
|
|
||||||
messages.addAll(knMessages);
|
|
||||||
// 设置知识库系统提示词
|
|
||||||
sysPrompt = knowledgeInfoVo.getSystemPrompt();
|
|
||||||
if(StringUtils.isEmpty(sysPrompt)){
|
|
||||||
sysPrompt ="###角色设定\n" +
|
|
||||||
"你是一个智能知识助手,专注于利用上下文中的信息来提供准确和相关的回答。\n" +
|
|
||||||
"###指令\n" +
|
|
||||||
"当用户的问题与上下文知识匹配时,利用上下文信息进行回答。如果问题与上下文不匹配,运用自身的推理能力生成合适的回答。\n" +
|
|
||||||
"###限制\n" +
|
|
||||||
"确保回答清晰简洁,避免提供不必要的细节。始终保持语气友好" +
|
|
||||||
"当前时间:"+ DateUtils.getDate();
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
sysPrompt = chatModelVo.getSystemPrompt();
|
|
||||||
if(StringUtils.isEmpty(sysPrompt)){
|
|
||||||
sysPrompt ="你是一个由RuoYI-AI开发的人工智能助手,名字叫熊猫助手。你擅长中英文对话,能够理解并处理各种问题,提供安全、有帮助、准确的回答。" +
|
|
||||||
"当前时间:"+ DateUtils.getDate()+
|
|
||||||
"#注意:回复之前注意结合上下文和工具返回内容进行回复。";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 设置系统默认提示词
|
|
||||||
Message sysMessage = Message.builder().content(sysPrompt).role(Message.Role.SYSTEM).build();
|
|
||||||
messages.add(0,sysMessage);
|
|
||||||
|
|
||||||
chatRequest.setSysPrompt(sysPrompt);
|
chatRequest.setSysPrompt(sysPrompt);
|
||||||
|
|
||||||
// 用户对话内容
|
// 用户对话内容
|
||||||
String chatString = null;
|
String chatString = null;
|
||||||
// 获取用户对话信息
|
// 获取用户对话信息
|
||||||
@@ -212,13 +252,128 @@ public class SseServiceImpl implements ISseService {
|
|||||||
if (CollectionUtil.isNotEmpty(listContent)) {
|
if (CollectionUtil.isNotEmpty(listContent)) {
|
||||||
chatString = listContent.get(0).toString();
|
chatString = listContent.get(0).toString();
|
||||||
}
|
}
|
||||||
} else if (content instanceof String) {
|
} else {
|
||||||
chatString = (String) content;
|
chatString = content.toString();
|
||||||
}
|
}
|
||||||
// 设置对话信息
|
|
||||||
chatRequest.setPrompt(chatString);
|
chatRequest.setPrompt(chatString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理知识库相关逻辑
|
||||||
|
*/
|
||||||
|
private String processKnowledgeBase(ChatRequest chatRequest, List<Message> messages) {
|
||||||
|
if (StringUtils.isEmpty(chatRequest.getKid())) {
|
||||||
|
return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查询知识库信息
|
||||||
|
KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoService.queryById(Long.valueOf(chatRequest.getKid()));
|
||||||
|
if (knowledgeInfoVo == null) {
|
||||||
|
log.warn("知识库信息不存在,kid: {}", chatRequest.getKid());
|
||||||
|
return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询向量模型配置信息
|
||||||
|
ChatModelVo chatModel = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName());
|
||||||
|
if (chatModel == null) {
|
||||||
|
log.warn("向量模型配置不存在,模型名称: {}", knowledgeInfoVo.getEmbeddingModelName());
|
||||||
|
return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建向量查询参数
|
||||||
|
QueryVectorBo queryVectorBo = buildQueryVectorBo(chatRequest, knowledgeInfoVo, chatModel);
|
||||||
|
|
||||||
|
// 获取向量查询结果
|
||||||
|
List<String> nearestList = vectorStoreService.getQueryVector(queryVectorBo);
|
||||||
|
|
||||||
|
// 添加知识库消息到上下文
|
||||||
|
addKnowledgeMessages(messages, nearestList);
|
||||||
|
|
||||||
|
// 返回知识库系统提示词
|
||||||
|
return getKnowledgeSystemPrompt(knowledgeInfoVo);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理知识库信息失败: {}", e.getMessage(), e);
|
||||||
|
return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建向量查询参数
|
||||||
|
*/
|
||||||
|
private QueryVectorBo buildQueryVectorBo(ChatRequest chatRequest, KnowledgeInfoVo knowledgeInfoVo,
|
||||||
|
ChatModelVo chatModel) {
|
||||||
|
String content = chatRequest.getMessages().get(chatRequest.getMessages().size() - 1).getContent().toString();
|
||||||
|
|
||||||
|
QueryVectorBo queryVectorBo = new QueryVectorBo();
|
||||||
|
queryVectorBo.setQuery(content);
|
||||||
|
queryVectorBo.setKid(chatRequest.getKid());
|
||||||
|
queryVectorBo.setApiKey(chatModel.getApiKey());
|
||||||
|
queryVectorBo.setBaseUrl(chatModel.getApiHost());
|
||||||
|
queryVectorBo.setVectorModelName(knowledgeInfoVo.getVectorModelName());
|
||||||
|
queryVectorBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName());
|
||||||
|
queryVectorBo.setMaxResults(knowledgeInfoVo.getRetrieveLimit());
|
||||||
|
|
||||||
|
return queryVectorBo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加知识库消息到上下文
|
||||||
|
*/
|
||||||
|
private void addKnowledgeMessages(List<Message> messages, List<String> nearestList) {
|
||||||
|
for (String prompt : nearestList) {
|
||||||
|
Message userMessage = Message.builder()
|
||||||
|
.content(prompt)
|
||||||
|
.role(Message.Role.USER)
|
||||||
|
.build();
|
||||||
|
messages.add(userMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取知识库系统提示词
|
||||||
|
*/
|
||||||
|
private String getKnowledgeSystemPrompt(KnowledgeInfoVo knowledgeInfoVo) {
|
||||||
|
String sysPrompt = knowledgeInfoVo.getSystemPrompt();
|
||||||
|
if (StringUtils.isEmpty(sysPrompt)) {
|
||||||
|
sysPrompt = "###角色设定\n" +
|
||||||
|
"你是一个智能知识助手,专注于利用上下文中的信息来提供准确和相关的回答。\n" +
|
||||||
|
"###指令\n" +
|
||||||
|
"当用户的问题与上下文知识匹配时,利用上下文信息进行回答。如果问题与上下文不匹配,运用自身的推理能力生成合适的回答。\n" +
|
||||||
|
"###限制\n" +
|
||||||
|
"确保回答清晰简洁,避免提供不必要的细节。始终保持语气友好\n" +
|
||||||
|
"当前时间:" + DateUtils.getDate();
|
||||||
|
}
|
||||||
|
return sysPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取提示词模板提示词
|
||||||
|
*/
|
||||||
|
private String getPromptTemplatePrompt(String category) {
|
||||||
|
PromptTemplateVo promptTemplateVo = promptTemplateService.queryByCategory(category);
|
||||||
|
if (Objects.isNull(promptTemplateVo) || StringUtils.isEmpty(promptTemplateVo.getTemplateContent())) {
|
||||||
|
return getDefaultSystemPrompt();
|
||||||
|
}
|
||||||
|
return promptTemplateVo.getTemplateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认系统提示词
|
||||||
|
*/
|
||||||
|
private String getDefaultSystemPrompt() {
|
||||||
|
String sysPrompt = chatModelVo != null ? chatModelVo.getSystemPrompt() : null;
|
||||||
|
if (StringUtils.isEmpty(sysPrompt)) {
|
||||||
|
sysPrompt = "你是一个由RuoYI-AI开发的人工智能助手,名字叫RuoYI人工智能助手。"
|
||||||
|
+ "你擅长中英文对话,能够理解并处理各种问题,提供安全、有帮助、准确的回答。"
|
||||||
|
+ "当前时间:" + DateUtils.getDate()
|
||||||
|
+ "#注意:回复之前注意结合上下文和工具返回内容进行回复。";
|
||||||
|
}
|
||||||
|
return sysPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文字转语音
|
* 文字转语音
|
||||||
@@ -231,8 +386,8 @@ public class SseServiceImpl implements ISseService {
|
|||||||
InputStreamResource resource = new InputStreamResource(body.byteStream());
|
InputStreamResource resource = new InputStreamResource(body.byteStream());
|
||||||
// 创建并返回ResponseEntity
|
// 创建并返回ResponseEntity
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType("audio/mpeg"))
|
.contentType(MediaType.parseMediaType("audio/mpeg"))
|
||||||
.body(resource);
|
.body(resource);
|
||||||
} else {
|
} else {
|
||||||
// 如果ResponseBody为空,返回404状态码
|
// 如果ResponseBody为空,返回404状态码
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.ruoyi.service.IChatModelService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
import org.ruoyi.chat.support.ChatServiceHelper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -51,14 +52,14 @@ public class ZhipuAiChatServiceImpl implements IChatService {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable error) {
|
public void onError(Throwable error) {
|
||||||
// System.out.println(error.getMessage());
|
ChatServiceHelper.onStreamError(emitter, error.getMessage());
|
||||||
emitter.send(error.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCompleteResponse(ChatResponse response) {
|
public void onCompleteResponse(ChatResponse response) {
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
log.info("消息结束,完整消息ID: {}", response.aiMessage());
|
log.info("消息结束,完整消息ID: {}", response.aiMessage());
|
||||||
|
org.ruoyi.chat.support.RetryNotifier.clear(emitter);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ public class ZhipuAiChatServiceImpl implements IChatService {
|
|||||||
model.chat(chatRequest.getPrompt(), handler);
|
model.chat(chatRequest.getPrompt(), handler);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("智谱清言请求失败:{}", e.getMessage());
|
log.error("智谱清言请求失败:{}", e.getMessage());
|
||||||
|
ChatServiceHelper.onStreamError(emitter, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
* 根据知识库角色查询知识库列表
|
* 根据知识库角色查询知识库列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<KnowledgeInfoVo> queryPageListByRole(PageQuery pageQuery) {
|
public TableDataInfo<KnowledgeInfoVo> queryPageListByRole(KnowledgeInfoBo bo, PageQuery pageQuery) {
|
||||||
// 查询用户关联角色
|
// 查询用户关联角色
|
||||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||||
if (StringUtils.isEmpty(loginUser.getKroleGroupIds()) || StringUtils.isEmpty(loginUser.getKroleGroupType())) {
|
if (StringUtils.isEmpty(loginUser.getKroleGroupIds()) || StringUtils.isEmpty(loginUser.getKroleGroupType())) {
|
||||||
@@ -122,8 +122,15 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
return new TableDataInfo<>();
|
return new TableDataInfo<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaQueryWrapper<KnowledgeInfo> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo);
|
||||||
lqw.in(KnowledgeInfo::getId, knowledgeRoleRelations.stream().map(KnowledgeRoleRelation::getKnowledgeId).filter(Objects::nonNull).collect(Collectors.toList()));
|
// 在查询用户创建的知识库条件下,拼接角色分配知识库
|
||||||
|
lqw.or(q -> q.in(
|
||||||
|
KnowledgeInfo::getId,
|
||||||
|
knowledgeRoleRelations.stream()
|
||||||
|
.map(KnowledgeRoleRelation::getKnowledgeId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
));
|
||||||
Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package org.ruoyi.chat.support;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
|
import org.ruoyi.service.IChatModelService;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一的聊天重试与降级调度器。
|
||||||
|
*
|
||||||
|
* 策略:
|
||||||
|
* - 当前模型最多重试 3 次;仍失败则降级到同分类内、优先级小于当前的最高优先级模型。
|
||||||
|
* - 降级模型同样最多重试 3 次;仍失败则向前端返回失败信息并停止。
|
||||||
|
*
|
||||||
|
* 注意:实现依赖调用方在底层异步失败时执行 onFailure.run() 通知本调度器。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ChatRetryHelper {
|
||||||
|
|
||||||
|
public interface AttemptStarter {
|
||||||
|
void start(ChatModelVo model, Runnable onFailure) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void executeWithRetry(
|
||||||
|
ChatModelVo primaryModel,
|
||||||
|
String category,
|
||||||
|
IChatModelService chatModelService,
|
||||||
|
SseEmitter emitter,
|
||||||
|
AttemptStarter attemptStarter
|
||||||
|
) {
|
||||||
|
Objects.requireNonNull(primaryModel, "primaryModel must not be null");
|
||||||
|
Objects.requireNonNull(category, "category must not be null");
|
||||||
|
Objects.requireNonNull(chatModelService, "chatModelService must not be null");
|
||||||
|
Objects.requireNonNull(emitter, "emitter must not be null");
|
||||||
|
Objects.requireNonNull(attemptStarter, "attemptStarter must not be null");
|
||||||
|
|
||||||
|
AtomicInteger mainAttempts = new AtomicInteger(0);
|
||||||
|
AtomicInteger fallbackAttempts = new AtomicInteger(0);
|
||||||
|
AtomicBoolean inFallback = new AtomicBoolean(false);
|
||||||
|
AtomicBoolean scheduling = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
class Scheduler {
|
||||||
|
volatile ChatModelVo current = primaryModel;
|
||||||
|
volatile ChatModelVo fallback = null;
|
||||||
|
|
||||||
|
void startAttempt() {
|
||||||
|
try {
|
||||||
|
if (!inFallback.get()) {
|
||||||
|
if (mainAttempts.incrementAndGet() > 3) {
|
||||||
|
// 进入降级
|
||||||
|
inFallback.set(true);
|
||||||
|
if (fallback == null) {
|
||||||
|
Integer curPriority = primaryModel.getPriority();
|
||||||
|
if (curPriority == null) {
|
||||||
|
curPriority = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
fallback = chatModelService.selectFallbackModelByCategoryAndLessPriority(category, curPriority);
|
||||||
|
}
|
||||||
|
if (fallback == null) {
|
||||||
|
SSEUtil.sendErrorEvent(emitter, "当前模型重试3次均失败,且无可用降级模型");
|
||||||
|
emitter.complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current = fallback;
|
||||||
|
mainAttempts.set(3); // 锁定
|
||||||
|
fallbackAttempts.set(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fallbackAttempts.incrementAndGet() > 3) {
|
||||||
|
SSEUtil.sendErrorEvent(emitter, "降级模型重试3次仍失败");
|
||||||
|
emitter.complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Runnable onFailure = () -> {
|
||||||
|
// 去抖:避免同一次失败触发多次重试
|
||||||
|
if (scheduling.compareAndSet(false, true)) {
|
||||||
|
try {
|
||||||
|
SSEUtil.sendErrorEvent(emitter, (inFallback.get() ? "降级模型" : "当前模型") + "调用失败,准备重试...");
|
||||||
|
// 立即发起下一次尝试
|
||||||
|
startAttempt();
|
||||||
|
} finally {
|
||||||
|
scheduling.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
attemptStarter.start(current, onFailure);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("启动聊天尝试失败: {}", ex.getMessage(), ex);
|
||||||
|
SSEUtil.sendErrorEvent(emitter, "启动聊天尝试失败: " + ex.getMessage());
|
||||||
|
// 直接按失败处理,继续重试/降级
|
||||||
|
if (scheduling.compareAndSet(false, true)) {
|
||||||
|
try {
|
||||||
|
startAttempt();
|
||||||
|
} finally {
|
||||||
|
scheduling.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Scheduler().startAttempt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package org.ruoyi.chat.support;
|
||||||
|
|
||||||
|
import org.ruoyi.chat.listener.SSEEventSourceListener;
|
||||||
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽取各聊天实现类的通用逻辑:
|
||||||
|
* - 创建带开关的 SSE 监听器
|
||||||
|
* - 统一的流错误处理(根据是否在重试场景决定通知或直接结束)
|
||||||
|
* - 统一的完成处理(清理回调并 complete)
|
||||||
|
*/
|
||||||
|
public class ChatServiceHelper {
|
||||||
|
|
||||||
|
public static SSEEventSourceListener createOpenAiListener(SseEmitter emitter, ChatRequest chatRequest) {
|
||||||
|
boolean retryEnabled = Boolean.TRUE.equals(chatRequest.getAutoSelectModel());
|
||||||
|
return new SSEEventSourceListener(
|
||||||
|
emitter,
|
||||||
|
chatRequest.getUserId(),
|
||||||
|
chatRequest.getSessionId(),
|
||||||
|
chatRequest.getToken(),
|
||||||
|
retryEnabled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onStreamError(SseEmitter emitter, String errorMessage) {
|
||||||
|
SSEUtil.sendErrorEvent(emitter, errorMessage);
|
||||||
|
if (RetryNotifier.hasCallback(emitter)) {
|
||||||
|
RetryNotifier.notifyFailure(emitter);
|
||||||
|
} else {
|
||||||
|
emitter.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onStreamComplete(SseEmitter emitter) {
|
||||||
|
try {
|
||||||
|
emitter.complete();
|
||||||
|
} finally {
|
||||||
|
RetryNotifier.clear(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.ruoyi.chat.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败回调通知器:基于发射器实例(SseEmitter 等对象地址)绑定回调,
|
||||||
|
* 避免与业务标识绑定,且能跨线程正确关联。
|
||||||
|
*/
|
||||||
|
public class RetryNotifier {
|
||||||
|
|
||||||
|
private static final Map<Integer, Runnable> FAILURE_CALLBACKS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static int keyOf(Object obj) {
|
||||||
|
return System.identityHashCode(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFailureCallback(Object emitterLike, Runnable callback) {
|
||||||
|
if (emitterLike == null || callback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FAILURE_CALLBACKS.put(keyOf(emitterLike), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(Object emitterLike) {
|
||||||
|
if (emitterLike == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FAILURE_CALLBACKS.remove(keyOf(emitterLike));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyFailure(Object emitterLike) {
|
||||||
|
if (emitterLike == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Runnable cb = FAILURE_CALLBACKS.get(keyOf(emitterLike));
|
||||||
|
if (Objects.nonNull(cb)) {
|
||||||
|
cb.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasCallback(Object emitterLike) {
|
||||||
|
if (emitterLike == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return FAILURE_CALLBACKS.containsKey(keyOf(emitterLike));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,6 @@ public class SSEUtil {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("SSE发送失败: {}", e.getMessage());
|
log.error("SSE发送失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
sseEmitter.complete();
|
// 不立即关闭,由上层策略决定是否继续重试或降级
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.ruoyi.generator.controller;
|
package org.ruoyi.generator.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.net.URLDecoder;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.ruoyi.common.core.domain.R;
|
import org.ruoyi.common.core.domain.R;
|
||||||
@@ -8,10 +9,11 @@ import org.ruoyi.generator.service.IGenTableService;
|
|||||||
import org.ruoyi.generator.service.SchemaFieldService;
|
import org.ruoyi.generator.service.SchemaFieldService;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代码生成 操作处理
|
* 代码生成 操作处理
|
||||||
*
|
*
|
||||||
@@ -46,4 +48,18 @@ public class GenController extends BaseController {
|
|||||||
genTableService.generateCodeToClasspathByTableNames(tableNameStr);
|
genTableService.generateCodeToClasspathByTableNames(tableNameStr);
|
||||||
return R.ok("代码生成成功");
|
return R.ok("代码生成成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成前端代码
|
||||||
|
*
|
||||||
|
* @param workPath 执行命令路径
|
||||||
|
* @param previewCode 执行生成前端文件命令
|
||||||
|
*/
|
||||||
|
@GetMapping("/batchGenFrontendCode")
|
||||||
|
public R<String> batchGenFrontendCode(@NotNull(message = "路径不能为空") String workPath, @NotNull(message = "指令不能为空") String previewCode) {
|
||||||
|
String decodedWorkPath = URLDecoder.decode(workPath, StandardCharsets.UTF_8);
|
||||||
|
String decodedPreviewCode = URLDecoder.decode(previewCode, StandardCharsets.UTF_8);
|
||||||
|
genTableService.generateFrontendTemplateFiles(decodedWorkPath, decodedPreviewCode);
|
||||||
|
return R.ok("代码生成成功");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ public class SchemaGroupController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 获取数据模型分组选择列表
|
* 获取数据模型分组选择列表
|
||||||
*/
|
*/
|
||||||
|
@SaCheckPermission("dev:schemaGroup:select")
|
||||||
@GetMapping("/select")
|
@GetMapping("/select")
|
||||||
public R<List<SchemaGroupVo>> select() {
|
public R<List<SchemaGroupVo>> select() {
|
||||||
SchemaGroupBo bo = new SchemaGroupBo();
|
SchemaGroupBo bo = new SchemaGroupBo();
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.ruoyi.generator.domain;
|
package org.ruoyi.generator.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型对象 dev_schema
|
* 数据模型对象 dev_schema
|
||||||
@@ -16,12 +17,9 @@ import java.io.Serial;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("dev_schema")
|
@TableName("dev_schema")
|
||||||
public class Schema extends BaseEntity {
|
public class Schema implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -49,41 +47,6 @@ public class Schema extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
|
||||||
/**
|
|
||||||
* 表注释
|
|
||||||
*/
|
|
||||||
private String comment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储引擎
|
|
||||||
*/
|
|
||||||
private String engine;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 列表字段
|
|
||||||
*/
|
|
||||||
private String listKeys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 搜索表单字段
|
|
||||||
*/
|
|
||||||
private String searchFormKeys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 表单设计
|
|
||||||
*/
|
|
||||||
private String designer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序
|
|
||||||
*/
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
@@ -96,8 +59,33 @@ public class Schema extends BaseEntity {
|
|||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户编号
|
* 创建部门
|
||||||
*/
|
*/
|
||||||
private String tenantId;
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createDept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.ruoyi.generator.domain;
|
package org.ruoyi.generator.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型字段对象 dev_schema_field
|
* 数据模型字段对象 dev_schema_field
|
||||||
@@ -16,12 +17,8 @@ import java.io.Serial;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("dev_schema_field")
|
@TableName("dev_schema_field")
|
||||||
public class SchemaField extends BaseEntity {
|
public class SchemaField implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -129,15 +126,6 @@ public class SchemaField extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩展JSON
|
|
||||||
*/
|
|
||||||
private String extendJson;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
@@ -151,8 +139,33 @@ public class SchemaField extends BaseEntity {
|
|||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户编号
|
* 创建部门
|
||||||
*/
|
*/
|
||||||
private String tenantId;
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createDept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.ruoyi.generator.domain;
|
package org.ruoyi.generator.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型分组对象 dev_schema_group
|
* 数据模型分组对象 dev_schema_group
|
||||||
@@ -16,12 +17,8 @@ import java.io.Serial;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("dev_schema_group")
|
@TableName("dev_schema_group")
|
||||||
public class SchemaGroup extends BaseEntity {
|
public class SchemaGroup implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -44,16 +41,6 @@ public class SchemaGroup extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String icon;
|
private String icon;
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序
|
|
||||||
*/
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
@@ -66,8 +53,32 @@ public class SchemaGroup extends BaseEntity {
|
|||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户编号
|
* 创建部门
|
||||||
*/
|
*/
|
||||||
private String tenantId;
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createDept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,12 @@ import io.github.linpeilie.annotations.AutoMapper;
|
|||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
import org.ruoyi.common.core.validate.AddGroup;
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
import org.ruoyi.common.core.validate.EditGroup;
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
import org.ruoyi.generator.domain.Schema;
|
import org.ruoyi.generator.domain.Schema;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型业务对象 SchemaBo
|
* 数据模型业务对象 SchemaBo
|
||||||
*
|
*
|
||||||
@@ -17,9 +17,8 @@ import org.ruoyi.generator.domain.Schema;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@AutoMapper(target = Schema.class, reverseConvertGenerate = false)
|
@AutoMapper(target = Schema.class, reverseConvertGenerate = false)
|
||||||
public class SchemaBo extends BaseEntity {
|
public class SchemaBo implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -38,52 +37,12 @@ public class SchemaBo extends BaseEntity {
|
|||||||
@NotBlank(message = "模型名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
@NotBlank(message = "模型名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/**
|
|
||||||
* 模型编码
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "模型编码不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private String code;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表名
|
* 表名
|
||||||
*/
|
*/
|
||||||
|
@NotBlank(message = "表名不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
|
||||||
/**
|
|
||||||
* 表注释
|
|
||||||
*/
|
|
||||||
private String comment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储引擎
|
|
||||||
*/
|
|
||||||
private String engine;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 列表字段
|
|
||||||
*/
|
|
||||||
private String listKeys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 搜索表单字段
|
|
||||||
*/
|
|
||||||
private String searchFormKeys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 表单设计
|
|
||||||
*/
|
|
||||||
private String designer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序
|
|
||||||
*/
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import io.github.linpeilie.annotations.AutoMapper;
|
|||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
import org.ruoyi.common.core.validate.AddGroup;
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
import org.ruoyi.common.core.validate.EditGroup;
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
import org.ruoyi.generator.domain.SchemaField;
|
import org.ruoyi.generator.domain.SchemaField;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型字段业务对象 SchemaFieldBo
|
* 数据模型字段业务对象 SchemaFieldBo
|
||||||
*
|
*
|
||||||
@@ -17,9 +17,8 @@ import org.ruoyi.generator.domain.SchemaField;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@AutoMapper(target = SchemaField.class, reverseConvertGenerate = false)
|
@AutoMapper(target = SchemaField.class, reverseConvertGenerate = false)
|
||||||
public class SchemaFieldBo extends BaseEntity {
|
public class SchemaFieldBo implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -36,7 +35,7 @@ public class SchemaFieldBo extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 模型名称
|
* 模型名称
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "模型名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
// @NotNull(message = "模型名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||||
private String schemaName;
|
private String schemaName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,16 +130,6 @@ public class SchemaFieldBo extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩展JSON
|
|
||||||
*/
|
|
||||||
private String extendJson;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import io.github.linpeilie.annotations.AutoMapper;
|
|||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
import org.ruoyi.common.core.validate.AddGroup;
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
import org.ruoyi.common.core.validate.EditGroup;
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
import org.ruoyi.generator.domain.SchemaGroup;
|
import org.ruoyi.generator.domain.SchemaGroup;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型分组业务对象 SchemaGroupBo
|
* 数据模型分组业务对象 SchemaGroupBo
|
||||||
*
|
*
|
||||||
@@ -17,9 +17,8 @@ import org.ruoyi.generator.domain.SchemaGroup;
|
|||||||
* @date 2024-01-01
|
* @date 2024-01-01
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@AutoMapper(target = SchemaGroup.class, reverseConvertGenerate = false)
|
@AutoMapper(target = SchemaGroup.class, reverseConvertGenerate = false)
|
||||||
public class SchemaGroupBo extends BaseEntity {
|
public class SchemaGroupBo implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
@@ -44,16 +43,6 @@ public class SchemaGroupBo extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String icon;
|
private String icon;
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序
|
|
||||||
*/
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.ruoyi.generator.domain.SchemaField;
|
import org.ruoyi.generator.domain.SchemaField;
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型字段视图对象 SchemaFieldVo
|
* 数据模型字段视图对象 SchemaFieldVo
|
||||||
@@ -20,9 +18,6 @@ import java.util.Date;
|
|||||||
@AutoMapper(target = SchemaField.class)
|
@AutoMapper(target = SchemaField.class)
|
||||||
public class SchemaFieldVo implements Serializable {
|
public class SchemaFieldVo implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
*/
|
*/
|
||||||
@@ -136,25 +131,8 @@ public class SchemaFieldVo implements Serializable {
|
|||||||
@Schema(description = "字典类型")
|
@Schema(description = "字典类型")
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
@Schema(description = "状态")
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩展JSON
|
|
||||||
*/
|
|
||||||
private String extendJson;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建时间
|
|
||||||
*/
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import io.github.linpeilie.annotations.AutoMapper;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.ruoyi.generator.domain.SchemaGroup;
|
import org.ruoyi.generator.domain.SchemaGroup;
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@@ -18,9 +17,6 @@ import java.util.Date;
|
|||||||
@AutoMapper(target = SchemaGroup.class)
|
@AutoMapper(target = SchemaGroup.class)
|
||||||
public class SchemaGroupVo implements Serializable {
|
public class SchemaGroupVo implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
*/
|
*/
|
||||||
@@ -46,11 +42,6 @@ public class SchemaGroupVo implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private Integer sort;
|
private Integer sort;
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ import java.util.Date;
|
|||||||
@AutoMapper(target = Schema.class)
|
@AutoMapper(target = Schema.class)
|
||||||
public class SchemaVo implements Serializable {
|
public class SchemaVo implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 主键
|
||||||
*/
|
*/
|
||||||
@@ -46,6 +43,11 @@ public class SchemaVo implements Serializable {
|
|||||||
* 表名
|
* 表名
|
||||||
*/
|
*/
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
/**
|
||||||
|
* 字典
|
||||||
|
*/
|
||||||
|
private String dictType;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表注释
|
* 表注释
|
||||||
|
|||||||
@@ -11,26 +11,19 @@ import org.apache.velocity.app.Velocity;
|
|||||||
import org.ruoyi.common.core.constant.Constants;
|
import org.ruoyi.common.core.constant.Constants;
|
||||||
import org.ruoyi.generator.config.GenConfig;
|
import org.ruoyi.generator.config.GenConfig;
|
||||||
import org.ruoyi.generator.domain.vo.SchemaFieldVo;
|
import org.ruoyi.generator.domain.vo.SchemaFieldVo;
|
||||||
|
import org.ruoyi.generator.domain.vo.SchemaGroupVo;
|
||||||
import org.ruoyi.generator.domain.vo.SchemaVo;
|
import org.ruoyi.generator.domain.vo.SchemaVo;
|
||||||
import org.ruoyi.generator.service.IGenTableService;
|
import org.ruoyi.generator.service.IGenTableService;
|
||||||
import org.ruoyi.generator.service.SchemaFieldService;
|
import org.ruoyi.generator.service.SchemaFieldService;
|
||||||
|
import org.ruoyi.generator.service.SchemaGroupService;
|
||||||
import org.ruoyi.generator.service.SchemaService;
|
import org.ruoyi.generator.service.SchemaService;
|
||||||
import org.ruoyi.generator.util.VelocityInitializer;
|
import org.ruoyi.generator.util.VelocityInitializer;
|
||||||
import org.ruoyi.generator.util.VelocityUtils;
|
import org.ruoyi.generator.util.VelocityUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务 服务层实现
|
* 业务 服务层实现
|
||||||
@@ -44,6 +37,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
|
|
||||||
private final SchemaService schemaService;
|
private final SchemaService schemaService;
|
||||||
private final SchemaFieldService schemaFieldService;
|
private final SchemaFieldService schemaFieldService;
|
||||||
|
private final SchemaGroupService schemaGroupService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于表名称批量生成代码到classpath路径
|
* 基于表名称批量生成代码到classpath路径
|
||||||
@@ -59,6 +53,41 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateFrontendTemplateFiles(String workPath, String previewCode) {
|
||||||
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
|
||||||
|
ProcessBuilder builder;
|
||||||
|
if (os.contains("win")) {
|
||||||
|
// Windows下用 cmd /c 执行 previewCode
|
||||||
|
builder = new ProcessBuilder("cmd.exe", "/c", previewCode);
|
||||||
|
} else {
|
||||||
|
// macOS/Linux 用 bash -c 执行 previewCode
|
||||||
|
builder = new ProcessBuilder("bash", "-c", previewCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置工作目录
|
||||||
|
builder.directory(new File(workPath));
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(
|
||||||
|
builder.start().getInputStream(),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
)
|
||||||
|
)) {
|
||||||
|
String line;
|
||||||
|
log.info("执行结果:");
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
log.info(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成前端代码出错", e);
|
||||||
|
throw new RuntimeException("生成前端代码失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据表名称生成代码到classpath
|
* 根据表名称生成代码到classpath
|
||||||
*/
|
*/
|
||||||
@@ -69,6 +98,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
log.warn("Schema不存在,表名: {}", tableName);
|
log.warn("Schema不存在,表名: {}", tableName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询Schema字段信息
|
// 查询Schema字段信息
|
||||||
List<SchemaFieldVo> fields = schemaFieldService.queryListByTableName(tableName);
|
List<SchemaFieldVo> fields = schemaFieldService.queryListByTableName(tableName);
|
||||||
if (CollUtil.isEmpty(fields)) {
|
if (CollUtil.isEmpty(fields)) {
|
||||||
@@ -128,17 +158,19 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
*/
|
*/
|
||||||
private VelocityContext prepareSchemaContext(SchemaVo schema, List<SchemaFieldVo> fields) {
|
private VelocityContext prepareSchemaContext(SchemaVo schema, List<SchemaFieldVo> fields) {
|
||||||
VelocityContext context = new VelocityContext();
|
VelocityContext context = new VelocityContext();
|
||||||
|
|
||||||
// 从配置文件读取基本配置
|
// 从配置文件读取基本配置
|
||||||
String packageName = GenConfig.getPackageName();
|
String packageName = GenConfig.getPackageName();
|
||||||
String author = GenConfig.getAuthor();
|
String author = GenConfig.getAuthor();
|
||||||
String tablePrefix = GenConfig.getTablePrefix();
|
String tablePrefix = GenConfig.getTablePrefix();
|
||||||
boolean autoRemovePre = GenConfig.getAutoRemovePre();
|
boolean autoRemovePre = GenConfig.getAutoRemovePre();
|
||||||
|
|
||||||
// 处理表名和类名
|
// 处理表名和类名
|
||||||
|
Long schemaGroupId = schema.getSchemaGroupId();
|
||||||
|
SchemaGroupVo schemaGroupVo = schemaGroupService.queryById(schemaGroupId);
|
||||||
String tableName = schema.getTableName();
|
String tableName = schema.getTableName();
|
||||||
String baseClassName = schema.getTableName();
|
String baseClassName = schema.getTableName();
|
||||||
|
|
||||||
// 自动去除表前缀
|
// 自动去除表前缀
|
||||||
if (autoRemovePre && StrUtil.isNotBlank(tablePrefix)) {
|
if (autoRemovePre && StrUtil.isNotBlank(tablePrefix)) {
|
||||||
String[] prefixes = tablePrefix.split(",");
|
String[] prefixes = tablePrefix.split(",");
|
||||||
@@ -149,12 +181,12 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String className = toCamelCase(baseClassName, true); // 首字母大写的类名,如:SysRole
|
String className = toCamelCase(baseClassName, true); // 首字母大写的类名,如:SysRole
|
||||||
String classname = toCamelCase(baseClassName, false); // 首字母小写的类名,如:sysRole
|
String classname = toCamelCase(baseClassName, false); // 首字母小写的类名,如:sysRole
|
||||||
String businessName = toCamelCase(baseClassName, false);
|
String businessName = toCamelCase(baseClassName, false);
|
||||||
String moduleName = getModuleName(packageName);
|
String moduleName = schemaGroupVo.getCode();
|
||||||
|
|
||||||
// 基本信息
|
// 基本信息
|
||||||
context.put("tableName", tableName);
|
context.put("tableName", tableName);
|
||||||
context.put("tableComment", schema.getComment());
|
context.put("tableComment", schema.getComment());
|
||||||
@@ -168,18 +200,18 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
context.put("packageName", packageName);
|
context.put("packageName", packageName);
|
||||||
context.put("moduleName", moduleName);
|
context.put("moduleName", moduleName);
|
||||||
context.put("businessName", businessName);
|
context.put("businessName", businessName);
|
||||||
|
|
||||||
// 权限相关
|
// 权限相关
|
||||||
context.put("permissionPrefix", moduleName + ":" + businessName);
|
context.put("permissionPrefix", moduleName + ":" + businessName);
|
||||||
context.put("parentMenuId", "2000"); // 默认父菜单ID,可配置
|
context.put("parentMenuId", "2000"); // 默认父菜单ID,可配置
|
||||||
|
|
||||||
// 生成菜单ID
|
// 生成菜单ID
|
||||||
List<Long> menuIds = new ArrayList<>();
|
List<Long> menuIds = new ArrayList<>();
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
menuIds.add(IdUtil.getSnowflakeNextId());
|
menuIds.add(IdUtil.getSnowflakeNextId());
|
||||||
}
|
}
|
||||||
context.put("menuIds", menuIds);
|
context.put("menuIds", menuIds);
|
||||||
|
|
||||||
// 创建table对象,包含menuIds等信息和方法
|
// 创建table对象,包含menuIds等信息和方法
|
||||||
Map<String, Object> table = new HashMap<>();
|
Map<String, Object> table = new HashMap<>();
|
||||||
table.put("menuIds", menuIds);
|
table.put("menuIds", menuIds);
|
||||||
@@ -188,29 +220,19 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
table.put("className", className);
|
table.put("className", className);
|
||||||
table.put("classname", classname);
|
table.put("classname", classname);
|
||||||
table.put("functionName", schema.getName());
|
table.put("functionName", schema.getName());
|
||||||
|
|
||||||
// 添加表类型属性(默认为crud类型)
|
// 添加表类型属性(默认为crud类型)
|
||||||
table.put("crud", true);
|
table.put("crud", true);
|
||||||
table.put("sub", false);
|
table.put("sub", false);
|
||||||
table.put("tree", false);
|
table.put("tree", false);
|
||||||
|
|
||||||
// 添加isSuperColumn方法
|
|
||||||
table.put("isSuperColumn", new Object() {
|
|
||||||
public boolean isSuperColumn(String javaField) {
|
|
||||||
// 定义超类字段(BaseEntity中的字段)
|
|
||||||
return "createBy".equals(javaField) || "createTime".equals(javaField)
|
|
||||||
|| "updateBy".equals(javaField) || "updateTime".equals(javaField)
|
|
||||||
|| "remark".equals(javaField) || "tenantId".equals(javaField);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
context.put("table", table);
|
context.put("table", table);
|
||||||
|
|
||||||
// 处理字段信息
|
// 处理字段信息
|
||||||
List<Map<String, Object>> columns = new ArrayList<>();
|
List<Map<String, Object>> columns = new ArrayList<>();
|
||||||
Map<String, Object> pkColumn = null;
|
Map<String, Object> pkColumn = null;
|
||||||
Set<String> importList = new HashSet<>();
|
Set<String> importList = new HashSet<>();
|
||||||
|
|
||||||
// 添加基础导入
|
// 添加基础导入
|
||||||
importList.add("java.io.Serializable");
|
importList.add("java.io.Serializable");
|
||||||
|
|
||||||
@@ -218,7 +240,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
Map<String, Object> column = new HashMap<>();
|
Map<String, Object> column = new HashMap<>();
|
||||||
String javaType = getJavaType(field.getType());
|
String javaType = getJavaType(field.getType());
|
||||||
String javaField = StrUtil.toCamelCase(field.getCode());
|
String javaField = StrUtil.toCamelCase(field.getCode());
|
||||||
|
|
||||||
column.put("columnName", field.getCode());
|
column.put("columnName", field.getCode());
|
||||||
column.put("columnComment", field.getName());
|
column.put("columnComment", field.getName());
|
||||||
column.put("comment", field.getName()); // 添加comment别名
|
column.put("comment", field.getName()); // 添加comment别名
|
||||||
@@ -226,15 +248,15 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
column.put("javaType", javaType);
|
column.put("javaType", javaType);
|
||||||
column.put("javaField", javaField);
|
column.put("javaField", javaField);
|
||||||
column.put("capJavaField", toCamelCase(field.getCode(), true));
|
column.put("capJavaField", toCamelCase(field.getCode(), true));
|
||||||
|
|
||||||
// 布尔值属性(兼容两种格式)
|
// 布尔值dictType属性(兼容两种格式)
|
||||||
boolean isPk = "1".equals(field.getIsPk());
|
boolean isPk = "1".equals(field.getIsPk());
|
||||||
boolean isRequired = "1".equals(field.getIsRequired());
|
boolean isRequired = "1".equals(field.getIsRequired());
|
||||||
boolean isInsert = "1".equals(field.getIsInsert());
|
boolean isInsert = "1".equals(field.getIsInsert());
|
||||||
boolean isEdit = "1".equals(field.getIsEdit());
|
boolean isEdit = "1".equals(field.getIsEdit());
|
||||||
boolean isList = "1".equals(field.getIsList());
|
boolean isList = "1".equals(field.getIsList());
|
||||||
boolean isQuery = "1".equals(field.getIsQuery());
|
boolean isQuery = "1".equals(field.getIsQuery());
|
||||||
|
|
||||||
column.put("isPk", isPk ? 1 : 0);
|
column.put("isPk", isPk ? 1 : 0);
|
||||||
column.put("pk", isPk); // 添加pk别名
|
column.put("pk", isPk); // 添加pk别名
|
||||||
column.put("isRequired", isRequired);
|
column.put("isRequired", isRequired);
|
||||||
@@ -247,27 +269,27 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
column.put("list", isList); // 添加list别名
|
column.put("list", isList); // 添加list别名
|
||||||
column.put("isQuery", isQuery);
|
column.put("isQuery", isQuery);
|
||||||
column.put("query", isQuery); // 添加query别名
|
column.put("query", isQuery); // 添加query别名
|
||||||
|
|
||||||
column.put("queryType", field.getQueryType());
|
column.put("queryType", field.getQueryType());
|
||||||
column.put("htmlType", field.getHtmlType());
|
column.put("htmlType", field.getHtmlType());
|
||||||
column.put("dictType", field.getDictType());
|
column.put("dictType", field.getDictType());
|
||||||
column.put("sort", field.getSort());
|
column.put("sort", field.getSort());
|
||||||
|
|
||||||
// 添加readConverterExp方法
|
// 添加readConverterExp方法
|
||||||
column.put("readConverterExp", new Object() {
|
column.put("readConverterExp", new Object() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// 根据Java类型添加相应的导入
|
// 根据Java类型添加相应的导入
|
||||||
addImportForJavaType(javaType, importList);
|
addImportForJavaType(javaType, importList);
|
||||||
|
|
||||||
columns.add(column);
|
columns.add(column);
|
||||||
|
|
||||||
// 设置主键列
|
// 设置主键列
|
||||||
if (isPk) {
|
if (isPk) {
|
||||||
pkColumn = column;
|
pkColumn = column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有主键,使用第一个字段作为主键
|
// 如果没有主键,使用第一个字段作为主键
|
||||||
if (pkColumn == null && !columns.isEmpty()) {
|
if (pkColumn == null && !columns.isEmpty()) {
|
||||||
pkColumn = columns.get(0);
|
pkColumn = columns.get(0);
|
||||||
@@ -275,27 +297,28 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
pkColumn.put("isPk", 1);
|
pkColumn.put("isPk", 1);
|
||||||
pkColumn.put("pk", true);
|
pkColumn.put("pk", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.put("columns", columns);
|
context.put("columns", columns);
|
||||||
context.put("pkColumn", pkColumn);
|
context.put("pkColumn", pkColumn);
|
||||||
context.put("importList", new ArrayList<>(importList));
|
context.put("importList", new ArrayList<>(importList));
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据Java类型添加相应的导入
|
* 根据Java类型添加相应的导入
|
||||||
*/
|
*/
|
||||||
private void addImportForJavaType(String javaType, Set<String> importList) {
|
private void addImportForJavaType(String javaType, Set<String> importList) {
|
||||||
switch (javaType) {
|
switch (javaType) {
|
||||||
case "BigDecimal" -> importList.add("java.math.BigDecimal");
|
case "BigDecimal" -> importList.add("java.math.BigDecimal");
|
||||||
case "Date" -> importList.add("java.util.Date");
|
case "Date" -> importList.add("java.util.Date");
|
||||||
case "LocalDateTime" -> importList.add("java.time.LocalDateTime");
|
case "LocalDateTime" -> importList.add("java.time.LocalDateTime");
|
||||||
case "LocalDate" -> importList.add("java.time.LocalDate");
|
case "LocalDate" -> importList.add("java.time.LocalDate");
|
||||||
case "LocalTime" -> importList.add("java.time.LocalTime");
|
case "LocalTime" -> importList.add("java.time.LocalTime");
|
||||||
default -> {}
|
default -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从包名中提取模块名
|
* 从包名中提取模块名
|
||||||
@@ -319,10 +342,10 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
String packageName = GenConfig.getPackageName();
|
String packageName = GenConfig.getPackageName();
|
||||||
String tablePrefix = GenConfig.getTablePrefix();
|
String tablePrefix = GenConfig.getTablePrefix();
|
||||||
boolean autoRemovePre = GenConfig.getAutoRemovePre();
|
boolean autoRemovePre = GenConfig.getAutoRemovePre();
|
||||||
|
|
||||||
// 处理类名
|
// 处理类名
|
||||||
String baseClassName = schema.getTableName();
|
String baseClassName = schema.getTableName();
|
||||||
|
|
||||||
// 自动去除表前缀
|
// 自动去除表前缀
|
||||||
if (autoRemovePre && StrUtil.isNotBlank(tablePrefix)) {
|
if (autoRemovePre && StrUtil.isNotBlank(tablePrefix)) {
|
||||||
String[] prefixes = tablePrefix.split(",");
|
String[] prefixes = tablePrefix.split(",");
|
||||||
@@ -333,13 +356,13 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String className = toCamelCase(baseClassName, true); // 首字母大写,如:SysRole
|
String className = toCamelCase(baseClassName, true); // 首字母大写,如:SysRole
|
||||||
// 首字母小写,如:sysRole
|
// 首字母小写,如:sysRole
|
||||||
String moduleName = getModuleName(packageName);
|
String moduleName = getModuleName(packageName);
|
||||||
String javaPath = "src/main/java/";
|
String javaPath = "src/main/java/";
|
||||||
String mybatisPath = "src/main/resources/mapper/";
|
String mybatisPath = "src/main/resources/mapper/";
|
||||||
|
|
||||||
if (template.contains("domain.java.vm")) {
|
if (template.contains("domain.java.vm")) {
|
||||||
return javaPath + packageName.replace(".", "/") + "/domain/" + className + ".java";
|
return javaPath + packageName.replace(".", "/") + "/domain/" + className + ".java";
|
||||||
} else if (template.contains("mapper.java.vm")) {
|
} else if (template.contains("mapper.java.vm")) {
|
||||||
@@ -412,16 +435,17 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
return "String";
|
return "String";
|
||||||
}
|
}
|
||||||
String type = dbType.toLowerCase();
|
String type = dbType.toLowerCase();
|
||||||
if (type.contains("int") || type.contains("tinyint") || type.contains("smallint")) {
|
if (StrUtil.equalsAny(type, "int", "tinyint")) {
|
||||||
return "Integer";
|
return "Integer";
|
||||||
} else if (type.contains("bigint")) {
|
} else if (StrUtil.equalsAny(type, "bigint")) {
|
||||||
return "Long";
|
return "Long";
|
||||||
} else if (type.contains("decimal") || type.contains("numeric") || type.contains("float") || type.contains(
|
} else if (StrUtil.equalsAny(type, "decimal", "numeric", "float", "double")) {
|
||||||
"double")) {
|
|
||||||
return "BigDecimal";
|
return "BigDecimal";
|
||||||
} else if (type.contains("date") || type.contains("time")) {
|
} else if (StrUtil.equalsAny(type, "date")) {
|
||||||
return "Date";
|
return "LocalDate";
|
||||||
} else if (type.contains("bit") || type.contains("boolean")) {
|
} else if (StrUtil.equalsAny(type, "datetime", "timestamp")) {
|
||||||
|
return "LocalDateTime";
|
||||||
|
} else if (StrUtil.equalsAny(type, "bit", "boolean")) {
|
||||||
return "Boolean";
|
return "Boolean";
|
||||||
} else {
|
} else {
|
||||||
return "String";
|
return "String";
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
lqw.eq(StringUtils.isNotBlank(bo.getQueryType()), SchemaField::getQueryType, bo.getQueryType());
|
lqw.eq(StringUtils.isNotBlank(bo.getQueryType()), SchemaField::getQueryType, bo.getQueryType());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getHtmlType()), SchemaField::getHtmlType, bo.getHtmlType());
|
lqw.eq(StringUtils.isNotBlank(bo.getHtmlType()), SchemaField::getHtmlType, bo.getHtmlType());
|
||||||
lqw.like(StringUtils.isNotBlank(bo.getDictType()), SchemaField::getDictType, bo.getDictType());
|
lqw.like(StringUtils.isNotBlank(bo.getDictType()), SchemaField::getDictType, bo.getDictType());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SchemaField::getStatus, bo.getStatus());
|
|
||||||
lqw.orderByAsc(SchemaField::getSort);
|
lqw.orderByAsc(SchemaField::getSort);
|
||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
@@ -150,7 +149,6 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
public List<SchemaFieldVo> queryListBySchemaId(Long schemaId) {
|
public List<SchemaFieldVo> queryListBySchemaId(Long schemaId) {
|
||||||
LambdaQueryWrapper<SchemaField> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<SchemaField> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.eq(SchemaField::getSchemaId, schemaId);
|
lqw.eq(SchemaField::getSchemaId, schemaId);
|
||||||
lqw.eq(SchemaField::getStatus, "0"); // 只查询正常状态的字段
|
|
||||||
lqw.orderByAsc(SchemaField::getSort);
|
lqw.orderByAsc(SchemaField::getSort);
|
||||||
return baseMapper.selectVoList(lqw);
|
return baseMapper.selectVoList(lqw);
|
||||||
}
|
}
|
||||||
@@ -209,9 +207,9 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("schemaGroupCode", schemaGroupVo.getCode());
|
result.put("schemaGroupCode", schemaGroupVo.getCode());
|
||||||
result.put("tableName", schema.getTableName());
|
result.put("tableName", schema.getTableName());
|
||||||
|
result.put("dictType",schema.getDictType());
|
||||||
result.put("tableComment", schema.getComment());
|
result.put("tableComment", schema.getComment());
|
||||||
result.put("className", toCamelCase(schema.getTableName(), true));
|
result.put("className", toCamelCase(schema.getTableName(), true));
|
||||||
// result.put("className", StrUtil.toCamelCase(schema.getTableName()));
|
|
||||||
result.put("tableCamelName", StrUtil.toCamelCase(schema.getTableName()));
|
result.put("tableCamelName", StrUtil.toCamelCase(schema.getTableName()));
|
||||||
result.put("functionName", schema.getName());
|
result.put("functionName", schema.getName());
|
||||||
result.put("schemaName", schema.getName());
|
result.put("schemaName", schema.getName());
|
||||||
@@ -225,6 +223,8 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
if (pkField != null) {
|
if (pkField != null) {
|
||||||
Map<String, Object> pkColumn = new HashMap<>();
|
Map<String, Object> pkColumn = new HashMap<>();
|
||||||
pkColumn.put("columnName", pkField.getCode());
|
pkColumn.put("columnName", pkField.getCode());
|
||||||
|
pkColumn.put("dictType", pkField.getDictType());
|
||||||
|
|
||||||
pkColumn.put("columnComment", pkField.getName());
|
pkColumn.put("columnComment", pkField.getName());
|
||||||
pkColumn.put("javaField", StrUtil.toCamelCase(pkField.getCode()));
|
pkColumn.put("javaField", StrUtil.toCamelCase(pkField.getCode()));
|
||||||
pkColumn.put("javaType", getJavaType(pkField.getType()));
|
pkColumn.put("javaType", getJavaType(pkField.getType()));
|
||||||
@@ -236,6 +236,7 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
for (SchemaFieldVo field : fields) {
|
for (SchemaFieldVo field : fields) {
|
||||||
Map<String, Object> column = new HashMap<>();
|
Map<String, Object> column = new HashMap<>();
|
||||||
column.put("columnName", field.getCode());
|
column.put("columnName", field.getCode());
|
||||||
|
column.put("dictType", field.getDictType());
|
||||||
column.put("columnComment", field.getName());
|
column.put("columnComment", field.getName());
|
||||||
column.put("javaField", StrUtil.toCamelCase(field.getCode()));
|
column.put("javaField", StrUtil.toCamelCase(field.getCode()));
|
||||||
column.put("javaType", getJavaType(field.getType()));
|
column.put("javaType", getJavaType(field.getType()));
|
||||||
@@ -265,8 +266,7 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LambdaQueryWrapper<SchemaField> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<SchemaField> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.eq(SchemaField::getSchemaName, tableName);
|
lqw.eq(SchemaField::getSchemaId, schemaId);
|
||||||
lqw.eq(SchemaField::getStatus, "0");
|
|
||||||
// 检查是否已存在字段数据
|
// 检查是否已存在字段数据
|
||||||
List<SchemaFieldVo> existingFields = baseMapper.selectVoList(lqw);
|
List<SchemaFieldVo> existingFields = baseMapper.selectVoList(lqw);
|
||||||
if (CollUtil.isNotEmpty(existingFields)) {
|
if (CollUtil.isNotEmpty(existingFields)) {
|
||||||
@@ -280,20 +280,27 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
SchemaField field = new SchemaField();
|
SchemaField field = new SchemaField();
|
||||||
field.setSchemaId(schemaId);
|
field.setSchemaId(schemaId);
|
||||||
field.setSchemaName(tableName);
|
field.setSchemaName(tableName);
|
||||||
|
field.setDefaultValue((String) columnInfo.get("columnDefault"));
|
||||||
|
field.setComment((String) columnInfo.get("columnComment"));
|
||||||
field.setName((String) columnInfo.get("columnComment"));
|
field.setName((String) columnInfo.get("columnComment"));
|
||||||
|
field.setDictType(StrUtil.toCamelCase((String) columnInfo.get("dictType")));
|
||||||
field.setCode(StrUtil.toCamelCase((String) columnInfo.get("columnName")));
|
field.setCode(StrUtil.toCamelCase((String) columnInfo.get("columnName")));
|
||||||
field.setType((String) columnInfo.get("dataType"));
|
field.setType((String) columnInfo.get("dataType"));
|
||||||
field.setLength(Integer.valueOf(String.valueOf(columnInfo.get("columnSize"))));
|
field.setLength(Integer.valueOf(String.valueOf(columnInfo.get("columnSize"))));
|
||||||
field.setIsPk((Boolean) columnInfo.get("isPrimaryKey") ? "1" : "0");
|
field.setIsPk((Boolean) columnInfo.get("isPrimaryKey") ? "1" : "0");
|
||||||
field.setIsRequired(!(Boolean) columnInfo.get("isNullable") ? "1" : "0");
|
field.setIsRequired(!(Boolean) columnInfo.get("isNullable") ? "1" : "0");
|
||||||
field.setIsInsert("1");
|
if ("1".equals(field.getIsPk())) {
|
||||||
field.setIsEdit("1");
|
field.setIsInsert("0");
|
||||||
|
field.setIsEdit("0");
|
||||||
|
}else {
|
||||||
|
field.setIsInsert("1");
|
||||||
|
field.setIsEdit("1");
|
||||||
|
}
|
||||||
field.setIsList("1");
|
field.setIsList("1");
|
||||||
field.setIsQuery("1");
|
field.setIsQuery("1");
|
||||||
field.setQueryType("EQ");
|
field.setQueryType("EQ");
|
||||||
field.setHtmlType(getDefaultHtmlType((String) columnInfo.get("dataType")));
|
field.setHtmlType(getDefaultHtmlType((String) columnInfo.get("dataType")));
|
||||||
field.setSort(sort++);
|
field.setSort(sort++);
|
||||||
field.setStatus("0");
|
|
||||||
// 如果字段名为空,使用字段代码作为名称
|
// 如果字段名为空,使用字段代码作为名称
|
||||||
if (StringUtils.isBlank(field.getName())) {
|
if (StringUtils.isBlank(field.getName())) {
|
||||||
field.setName(field.getCode());
|
field.setName(field.getCode());
|
||||||
@@ -363,16 +370,15 @@ public class SchemaFieldServiceImpl implements SchemaFieldService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String type = dbType.toLowerCase();
|
String type = dbType.toLowerCase();
|
||||||
if (type.contains("int") || type.contains("tinyint") || type.contains("smallint")) {
|
if (StrUtil.equalsAny(type, "int", "tinyint", "smallint")) {
|
||||||
return "Integer";
|
return "Integer";
|
||||||
} else if (type.contains("bigint")) {
|
} else if (StrUtil.equalsAny(type, "bigint")) {
|
||||||
return "Long";
|
return "Long";
|
||||||
} else if (type.contains("decimal") || type.contains("numeric") || type.contains("float") || type.contains(
|
} else if (StrUtil.equalsAny(type, "decimal", "numeric", "float", "double")) {
|
||||||
"double")) {
|
|
||||||
return "BigDecimal";
|
return "BigDecimal";
|
||||||
} else if (type.contains("date") || type.contains("time")) {
|
} else if (StrUtil.equalsAny(type, "date", "datetime","timestamp")) {
|
||||||
return "Date";
|
return "Date";
|
||||||
} else if (type.contains("bit") || type.contains("boolean")) {
|
} else if (StrUtil.equalsAny(type, "bit", "boolean")) {
|
||||||
return "Boolean";
|
return "Boolean";
|
||||||
} else {
|
} else {
|
||||||
return "String";
|
return "String";
|
||||||
|
|||||||
@@ -60,9 +60,6 @@ public class SchemaGroupServiceImpl implements SchemaGroupService {
|
|||||||
LambdaQueryWrapper<SchemaGroup> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<SchemaGroup> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.like(StringUtils.isNotBlank(bo.getName()), SchemaGroup::getName, bo.getName());
|
lqw.like(StringUtils.isNotBlank(bo.getName()), SchemaGroup::getName, bo.getName());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getCode()), SchemaGroup::getCode, bo.getCode());
|
lqw.eq(StringUtils.isNotBlank(bo.getCode()), SchemaGroup::getCode, bo.getCode());
|
||||||
lqw.eq(bo.getSort() != null, SchemaGroup::getSort, bo.getSort());
|
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SchemaGroup::getStatus, bo.getStatus());
|
|
||||||
lqw.orderByAsc(SchemaGroup::getSort);
|
|
||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +99,6 @@ public class SchemaGroupServiceImpl implements SchemaGroupService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||||
if (isValid) {
|
return baseMapper.deleteByIds(ids) > 0;
|
||||||
//TODO 做一些业务上的校验,判断是否需要校验
|
|
||||||
}
|
|
||||||
return baseMapper.deleteBatchIds(ids) > 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,10 @@ import org.ruoyi.common.core.utils.MapstructUtils;
|
|||||||
import org.ruoyi.common.core.utils.StringUtils;
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
import org.ruoyi.core.page.PageQuery;
|
import org.ruoyi.core.page.PageQuery;
|
||||||
import org.ruoyi.core.page.TableDataInfo;
|
import org.ruoyi.core.page.TableDataInfo;
|
||||||
|
import org.ruoyi.generator.domain.SchemaGroup;
|
||||||
|
import org.ruoyi.generator.domain.vo.SchemaGroupVo;
|
||||||
import org.ruoyi.generator.event.SchemaDeletedEvent;
|
import org.ruoyi.generator.event.SchemaDeletedEvent;
|
||||||
|
import org.ruoyi.generator.service.SchemaGroupService;
|
||||||
import org.ruoyi.generator.service.SchemaService;
|
import org.ruoyi.generator.service.SchemaService;
|
||||||
import org.ruoyi.generator.domain.Schema;
|
import org.ruoyi.generator.domain.Schema;
|
||||||
import org.ruoyi.generator.domain.bo.SchemaBo;
|
import org.ruoyi.generator.domain.bo.SchemaBo;
|
||||||
@@ -20,6 +23,8 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据模型Service业务层处理
|
* 数据模型Service业务层处理
|
||||||
@@ -31,6 +36,7 @@ import java.util.List;
|
|||||||
public class SchemaServiceImpl implements SchemaService {
|
public class SchemaServiceImpl implements SchemaService {
|
||||||
|
|
||||||
private final SchemaMapper baseMapper;
|
private final SchemaMapper baseMapper;
|
||||||
|
private final SchemaGroupService schemaGroupService;
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,10 +70,7 @@ public class SchemaServiceImpl implements SchemaService {
|
|||||||
LambdaQueryWrapper<Schema> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<Schema> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.eq(bo.getSchemaGroupId() != null, Schema::getSchemaGroupId, bo.getSchemaGroupId());
|
lqw.eq(bo.getSchemaGroupId() != null, Schema::getSchemaGroupId, bo.getSchemaGroupId());
|
||||||
lqw.like(StringUtils.isNotBlank(bo.getName()), Schema::getName, bo.getName());
|
lqw.like(StringUtils.isNotBlank(bo.getName()), Schema::getName, bo.getName());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getCode()), Schema::getCode, bo.getCode());
|
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getTableName()), Schema::getTableName, bo.getTableName());
|
lqw.eq(StringUtils.isNotBlank(bo.getTableName()), Schema::getTableName, bo.getTableName());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), Schema::getStatus, bo.getStatus());
|
|
||||||
lqw.orderByAsc(Schema::getSort);
|
|
||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,15 +79,18 @@ public class SchemaServiceImpl implements SchemaService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Boolean insertByBo(SchemaBo bo) {
|
public Boolean insertByBo(SchemaBo bo) {
|
||||||
Schema add = MapstructUtils.convert(bo, Schema.class);
|
Schema schema = MapstructUtils.convert(bo, Schema.class);
|
||||||
validEntityBeforeSave(add);
|
Long schemaGroupId = bo.getSchemaGroupId();
|
||||||
boolean flag = baseMapper.insert(add) > 0;
|
SchemaGroupVo schemaGroupVo = schemaGroupService.queryById(schemaGroupId);
|
||||||
|
if (Objects.nonNull(schemaGroupVo)) {
|
||||||
|
schema.setCode(schemaGroupVo.getCode());
|
||||||
|
}
|
||||||
|
boolean flag = baseMapper.insert(schema) > 0;
|
||||||
if (flag) {
|
if (flag) {
|
||||||
bo.setId(add.getId());
|
bo.setId(schema.getId());
|
||||||
|
|
||||||
// 发布数据模型添加事件,由事件监听器处理字段插入
|
// 发布数据模型添加事件,由事件监听器处理字段插入
|
||||||
if (StringUtils.isNotBlank(bo.getTableName())) {
|
if (StringUtils.isNotBlank(bo.getTableName())) {
|
||||||
eventPublisher.publishEvent(new SchemaAddedEvent(this, add.getId(), bo.getTableName()));
|
eventPublisher.publishEvent(new SchemaAddedEvent(this, schema.getId(), bo.getTableName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return flag;
|
return flag;
|
||||||
@@ -96,17 +102,9 @@ public class SchemaServiceImpl implements SchemaService {
|
|||||||
@Override
|
@Override
|
||||||
public Boolean updateByBo(SchemaBo bo) {
|
public Boolean updateByBo(SchemaBo bo) {
|
||||||
Schema update = MapstructUtils.convert(bo, Schema.class);
|
Schema update = MapstructUtils.convert(bo, Schema.class);
|
||||||
validEntityBeforeSave(update);
|
|
||||||
return baseMapper.updateById(update) > 0;
|
return baseMapper.updateById(update) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存前的数据校验
|
|
||||||
*/
|
|
||||||
private void validEntityBeforeSave(Schema entity) {
|
|
||||||
//TODO 做一些数据校验,如唯一约束
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除数据模型
|
* 批量删除数据模型
|
||||||
*/
|
*/
|
||||||
@@ -127,8 +125,6 @@ public class SchemaServiceImpl implements SchemaService {
|
|||||||
public SchemaVo queryByTableName(String tableName) {
|
public SchemaVo queryByTableName(String tableName) {
|
||||||
LambdaQueryWrapper<Schema> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<Schema> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.eq(Schema::getTableName, tableName);
|
lqw.eq(Schema::getTableName, tableName);
|
||||||
// 只查询正常状态的模型
|
|
||||||
lqw.eq(Schema::getStatus, "0");
|
|
||||||
return baseMapper.selectVoOne(lqw);
|
return baseMapper.selectVoOne(lqw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,4 +13,12 @@ public interface IGenTableService {
|
|||||||
* @param tableName 表名称数组
|
* @param tableName 表名称数组
|
||||||
*/
|
*/
|
||||||
void generateCodeToClasspathByTableNames(String tableName);
|
void generateCodeToClasspathByTableNames(String tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成前端文件
|
||||||
|
*
|
||||||
|
* @param workPath 执行命令路径
|
||||||
|
* @param previewCode 执行生成前端文件命令
|
||||||
|
*/
|
||||||
|
void generateFrontendTemplateFiles(String workPath, String previewCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,26 +25,28 @@ import org.ruoyi.common.core.validate.EditGroup;
|
|||||||
public class ${ClassName}Bo implements Serializable {
|
public class ${ClassName}Bo implements Serializable {
|
||||||
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if(!$table.isSuperColumn($column.javaField) && ($column.isPk || $column.query || $column.insert || $column.edit))
|
#if($column.isPk)
|
||||||
/**
|
|
||||||
* $column.columnComment
|
|
||||||
*/
|
|
||||||
#if($column.insert && $column.edit)
|
|
||||||
#set($Group="AddGroup.class, EditGroup.class")
|
|
||||||
#elseif($column.insert)
|
|
||||||
#set($Group="AddGroup.class")
|
|
||||||
#elseif($column.edit)
|
|
||||||
#set($Group="EditGroup.class")
|
|
||||||
#end
|
|
||||||
#if($column.required)
|
|
||||||
#if($column.javaType == 'String')
|
|
||||||
@NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
|
|
||||||
#else
|
|
||||||
@NotNull(message = "$column.columnComment不能为空", groups = { $Group })
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
private $column.javaType $column.javaField;
|
private $column.javaType $column.javaField;
|
||||||
|
|
||||||
|
#elseif($column.insert || $column.edit)
|
||||||
|
/**
|
||||||
|
* $column.columnComment
|
||||||
|
*/
|
||||||
|
#if($column.insert && $column.edit)
|
||||||
|
#set($Group="AddGroup.class, EditGroup.class")
|
||||||
|
#elseif($column.insert)
|
||||||
|
#set($Group="AddGroup.class")
|
||||||
|
#elseif($column.edit)
|
||||||
|
#set($Group="EditGroup.class")
|
||||||
|
#end
|
||||||
|
#if($column.required)
|
||||||
|
#if($column.javaType == 'String')
|
||||||
|
@NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
|
||||||
|
#else
|
||||||
|
@NotNull(message = "$column.columnComment不能为空", groups = { $Group })
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
private $column.javaType $column.javaField;
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,10 @@
|
|||||||
package ${packageName}.domain;
|
package ${packageName}.domain;
|
||||||
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.javaField=='tenantId')
|
|
||||||
#set($IsTenant=1)
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#if($IsTenant==1)
|
|
||||||
import core.tenant.common.org.ruoyi.TenantEntity;
|
|
||||||
#else
|
|
||||||
#end
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
#foreach ($import in $importList)
|
||||||
#foreach ($import in $importList)
|
import ${import};
|
||||||
import ${import};
|
#end
|
||||||
#end
|
|
||||||
|
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ${functionName}对象 ${tableName}
|
* ${functionName}对象 ${tableName}
|
||||||
@@ -24,15 +12,9 @@ import org.ruoyi.core.domain.BaseEntity;
|
|||||||
* @author ${author}
|
* @author ${author}
|
||||||
* @date ${datetime}
|
* @date ${datetime}
|
||||||
*/
|
*/
|
||||||
#if($IsTenant==1)
|
|
||||||
#set($Entity="TenantEntity")
|
|
||||||
#else
|
|
||||||
#set($Entity="BaseEntity")
|
|
||||||
#end
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("${tableName}")
|
@TableName("${tableName}")
|
||||||
public class ${ClassName} extends ${Entity} {
|
public class ${ClassName} implements Serializable {
|
||||||
|
|
||||||
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
@@ -47,7 +29,7 @@ public class ${ClassName} extends ${Entity} {
|
|||||||
@Version
|
@Version
|
||||||
#end
|
#end
|
||||||
#if($column.isPk==1)
|
#if($column.isPk==1)
|
||||||
@TableId(value = "$column.columnName")
|
@TableId(value = "${column.columnName}", type = IdType.AUTO)
|
||||||
#end
|
#end
|
||||||
private $column.javaType $column.javaField;
|
private $column.javaType $column.javaField;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user