Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-dependencies/pom.xml
This commit is contained in:
commit
d0293ea2fa
File diff suppressed because it is too large
Load Diff
@ -77,6 +77,7 @@
|
||||
<jimureport.version>2.1.3</jimureport.version>
|
||||
<jimubi.version>2.3.0</jimubi.version>
|
||||
<weixin-java.version>4.7.9-20251224.161447</weixin-java.version>
|
||||
<alipay-sdk-java.version>4.40.607.ALL</alipay-sdk-java.version>
|
||||
<!-- 专属于 JDK8 安全漏洞升级 -->
|
||||
<logback.version>1.2.13</logback.version> <!-- 无法使用 1.3.X 版本,启动会报错 -->
|
||||
</properties>
|
||||
@ -596,6 +597,18 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay-sdk-java.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-pay</artifactId>
|
||||
|
||||
@ -59,6 +59,8 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -231,20 +233,24 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||
// 4.3 流式返回
|
||||
StringBuffer contentBuffer = new StringBuffer();
|
||||
StringBuffer reasoningContentBuffer = new StringBuffer();
|
||||
|
||||
// 防止执行多次知识库和联网搜索
|
||||
AtomicBoolean firstExecuteFlag = new AtomicBoolean(true);
|
||||
AtomicReference<List<AiChatMessageRespVO.KnowledgeSegment>> cacheSegments = new AtomicReference<>();
|
||||
AtomicReference<List<AiWebSearchResponse.WebPage>> cacheWebSearchPages = new AtomicReference<>();
|
||||
return streamResponse.map(chunk -> {
|
||||
// 仅首次:返回知识库、联网搜索
|
||||
List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
|
||||
List<AiWebSearchResponse.WebPage> webSearchPages = null;
|
||||
if (StrUtil.isEmpty(contentBuffer)) {
|
||||
Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() ->
|
||||
knowledgeDocumentService.getKnowledgeDocumentMap(
|
||||
convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));
|
||||
segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {
|
||||
AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
|
||||
segment.setDocumentName(document != null ? document.getName() : null);
|
||||
});
|
||||
if (webSearchResponse != null) {
|
||||
webSearchPages = webSearchResponse.getLists();
|
||||
if (firstExecuteFlag.compareAndSet(true, false)) { // CAS 操作,确保仅执行一次
|
||||
Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> knowledgeDocumentService.getKnowledgeDocumentMap(
|
||||
convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));
|
||||
cacheSegments.set(BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {
|
||||
AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
|
||||
segment.setDocumentName(document != null ? document.getName() : null);
|
||||
}));
|
||||
if (webSearchResponse != null) {
|
||||
cacheWebSearchPages.set(webSearchResponse.getLists());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 响应结果
|
||||
@ -261,7 +267,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
||||
.setContent(StrUtil.nullToDefault(newContent, "")) // 避免 null 的 情况
|
||||
.setReasoningContent(StrUtil.nullToDefault(newReasoningContent, "")) // 避免 null 的 情况
|
||||
.setSegments(segments).setWebSearchPages(webSearchPages))); // 知识库 + 联网搜索
|
||||
.setSegments(cacheSegments.get()).setWebSearchPages(cacheWebSearchPages.get()))); // 知识库 + 联网搜索
|
||||
}).doOnComplete(() -> {
|
||||
// 忽略租户,因为 Flux 异步无法透传租户
|
||||
TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
|
||||
|
||||
@ -4,12 +4,14 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
|
||||
@ -17,7 +19,9 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@ -45,6 +49,8 @@ public class CodegenServiceImpl implements CodegenService {
|
||||
|
||||
@Resource
|
||||
private DatabaseTableService databaseTableService;
|
||||
@Resource
|
||||
private DataSourceConfigService dataSourceConfigService;
|
||||
|
||||
@Resource
|
||||
private CodegenTableMapper codegenTableMapper;
|
||||
@ -284,8 +290,11 @@ public class CodegenServiceImpl implements CodegenService {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据源对应的数据库类型
|
||||
DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(table.getDataSourceConfigId());
|
||||
DbType dbType = JdbcUtils.getDbType(dataSourceConfig.getUrl());
|
||||
// 执行生成
|
||||
return codegenEngine.execute(table, columns, subTables, subColumnsList);
|
||||
return codegenEngine.execute(dbType, table, columns, subTables, subColumnsList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -33,6 +33,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -310,16 +311,17 @@ public class CodegenEngine {
|
||||
/**
|
||||
* 生成代码
|
||||
*
|
||||
* @param dbType 数据库类型
|
||||
* @param table 表定义
|
||||
* @param columns table 的字段定义数组
|
||||
* @param subTables 子表数组,当且仅当主子表时使用
|
||||
* @param subColumnsList subTables 的字段定义数组
|
||||
* @return 生成的代码,key 是路径,value 是对应代码
|
||||
*/
|
||||
public Map<String, String> execute(CodegenTableDO table, List<CodegenColumnDO> columns,
|
||||
public Map<String, String> execute(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
|
||||
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
|
||||
// 1.1 初始化 bindMap 上下文
|
||||
Map<String, Object> bindingMap = initBindingMap(table, columns, subTables, subColumnsList);
|
||||
Map<String, Object> bindingMap = initBindingMap(dbType, table, columns, subTables, subColumnsList);
|
||||
// 1.2 获得模版
|
||||
Map<String, String> templates = getTemplates(table.getFrontType());
|
||||
|
||||
@ -425,10 +427,11 @@ public class CodegenEngine {
|
||||
return content;
|
||||
}
|
||||
|
||||
private Map<String, Object> initBindingMap(CodegenTableDO table, List<CodegenColumnDO> columns,
|
||||
private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
|
||||
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
|
||||
// 创建 bindingMap
|
||||
Map<String, Object> bindingMap = new HashMap<>(globalBindingMap);
|
||||
bindingMap.put("dbType", dbType);
|
||||
bindingMap.put("table", table);
|
||||
bindingMap.put("columns", columns);
|
||||
bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段
|
||||
|
||||
@ -1,3 +1,24 @@
|
||||
## 通用变量定义
|
||||
#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
|
||||
#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
|
||||
##
|
||||
## 宏定义:生成按钮 SQL(通用部分)
|
||||
#macro(insertButtonSql $parentIdVar)
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, ${parentIdVar},
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
#end
|
||||
##
|
||||
## ======================= MySQL / OceanBase =======================
|
||||
#if ($dbType.name() == 'MYSQL' || $dbType.name() == 'OCEAN_BASE')
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
@ -9,12 +30,9 @@ VALUES (
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
|
||||
#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
@ -25,4 +43,139 @@ VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
#end
|
||||
##
|
||||
## ======================= Oracle / 达梦 DM =======================
|
||||
#elseif ($dbType.name() == 'ORACLE' || $dbType.name() == 'DM')
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
|
||||
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
-- 说明:Oracle/达梦 使用序列获取上一个插入的 ID
|
||||
DECLARE
|
||||
v_parent_id NUMBER;
|
||||
BEGIN
|
||||
SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
|
||||
-- 按钮 SQL
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
END;
|
||||
/
|
||||
##
|
||||
## ======================= PostgreSQL / 人大金仓 KingbaseES =======================
|
||||
#elseif ($dbType.name() == 'POSTGRE_SQL' || $dbType.name() == 'KINGBASE_ES')
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
|
||||
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
-- 说明:PostgreSQL/KingbaseES 使用 lastval() 获取上一个插入的序列值
|
||||
DO $$
|
||||
DECLARE
|
||||
v_parent_id BIGINT;
|
||||
BEGIN
|
||||
v_parent_id := lastval();
|
||||
-- 按钮 SQL
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
END $$;
|
||||
##
|
||||
## ======================= SQL Server =======================
|
||||
#elseif ($dbType.name() == 'SQL_SERVER' || $dbType.name() == 'SQL_SERVER2005')
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
|
||||
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
-- 说明:SQL Server 使用 SCOPE_IDENTITY() 获取上一个插入的自增 ID
|
||||
DECLARE @parentId BIGINT;
|
||||
SET @parentId = SCOPE_IDENTITY();
|
||||
|
||||
-- 按钮 SQL
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
##
|
||||
## ======================= 不支持的数据库类型 =======================
|
||||
#else
|
||||
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
-- 注意:当前数据库类型 ${dbType.name()} 暂不支持自动生成菜单 SQL
|
||||
-- 请参考以下 MySQL 语法,手动修改为您的数据库对应的语法
|
||||
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
|
||||
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
-- TODO: 请根据您的数据库类型,修改获取上一个插入 ID 的方式
|
||||
-- MySQL: SELECT @parentId := LAST_INSERT_ID();
|
||||
-- Oracle: SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
|
||||
-- PostgreSQL: SELECT lastval() INTO v_parent_id;
|
||||
-- SQL Server: SET @parentId = SCOPE_IDENTITY();
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
#foreach ($functionName in $functionNames)
|
||||
#set ($index = $foreach.count - 1)
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
#end
|
||||
#end
|
||||
|
||||
@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTa
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
@ -19,7 +20,9 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
|
||||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -62,6 +65,8 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@MockBean
|
||||
private DatabaseTableService databaseTableService;
|
||||
@MockitoBean
|
||||
private DataSourceConfigService dataSourceConfigService;
|
||||
|
||||
@MockBean
|
||||
private CodegenBuilder codegenBuilder;
|
||||
@ -453,9 +458,12 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
|
||||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())
|
||||
.setOrdinalPosition(2));
|
||||
codegenColumnMapper.insert(column02);
|
||||
// mock 数据(DataSourceConfigDO)
|
||||
when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))
|
||||
.thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl("jdbc:mysql://")));
|
||||
// mock 执行生成
|
||||
Map<String, String> codes = MapUtil.of(randomString(), randomString());
|
||||
when(codegenEngine.execute(eq(table), argThat(columns -> {
|
||||
when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {
|
||||
assertEquals(2, columns.size());
|
||||
assertEquals(column01, columns.get(0));
|
||||
assertEquals(column02, columns.get(1));
|
||||
@ -494,9 +502,12 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
|
||||
// mock 数据(sub CodegenColumnDO)
|
||||
CodegenColumnDO subColumn01 = randomPojo(CodegenColumnDO.class, o -> o.setId(1024L).setTableId(subTable.getId()));
|
||||
codegenColumnMapper.insert(subColumn01);
|
||||
// mock 数据(DataSourceConfigDO)
|
||||
when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))
|
||||
.thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl("jdbc:mysql://")));
|
||||
// mock 执行生成
|
||||
Map<String, String> codes = MapUtil.of(randomString(), randomString());
|
||||
when(codegenEngine.execute(eq(table), argThat(columns -> {
|
||||
when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {
|
||||
assertEquals(2, columns.size());
|
||||
assertEquals(column01, columns.get(0));
|
||||
assertEquals(column02, columns.get(1));
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -28,7 +29,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> columns = getColumnList("student");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + "/vue2_one");
|
||||
// 断言
|
||||
@ -44,7 +45,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> columns = getColumnList("category");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + "/vue2_tree");
|
||||
// 断言
|
||||
@ -88,7 +89,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns,
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
|
||||
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + path);
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -28,7 +29,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> columns = getColumnList("student");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + "/vue3_one");
|
||||
// 断言
|
||||
@ -44,7 +45,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> columns = getColumnList("category");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + "/vue3_tree");
|
||||
// 断言
|
||||
@ -88,7 +89,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
|
||||
List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(table, columns,
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
|
||||
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
|
||||
// 生成测试文件
|
||||
//writeResult(result, resourcesPath + path);
|
||||
|
||||
@ -76,7 +76,7 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
|
||||
.in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
|
||||
.and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()) // 3.1 未过期
|
||||
.or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后
|
||||
.apply(" (take_count < total_count OR total_count = -1)"); // 4. 剩余数量大于 0,或者无限领取
|
||||
.apply(" (take_count < total_count OR total_count = " + CouponTemplateDO.TOTAL_COUNT_MAX + ")"); // 4. 剩余数量大于 0,或者无限领取
|
||||
}
|
||||
return canTakeConsumer;
|
||||
}
|
||||
|
||||
@ -285,8 +285,8 @@ public class CouponServiceImpl implements CouponService {
|
||||
// 校验剩余发放数量是否充足(仅在 CouponTakeTypeEnum.USER 用户领取时)
|
||||
// 关联案例:https://t.zsxq.com/mElGQ、https://t.zsxq.com/6pLzr
|
||||
if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())
|
||||
&& ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TAKE_LIMIT_COUNT_MAX) // 校验不限制领取数
|
||||
&& couponTemplate.getTakeCount() > couponTemplate.getTotalCount()) { // 已领取数量 >= 总发放数量
|
||||
&& !couponTemplateService.isTotalCountUnlimited(couponTemplate.getTotalCount()) // 校验不限制总发放数量
|
||||
&& couponTemplate.getTakeCount() > couponTemplate.getTotalCount()) { // 已领取数量 > 总发放数量
|
||||
throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
|
||||
}
|
||||
// 校验"固定日期"的有效期类型是否过期
|
||||
@ -304,7 +304,7 @@ public class CouponServiceImpl implements CouponService {
|
||||
* @param couponTemplate 优惠劵模版
|
||||
*/
|
||||
private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
|
||||
if (couponTemplate.getTakeLimitCount() <= 0) {
|
||||
if (couponTemplateService.isTakeLimitCountUnlimited(couponTemplate.getTakeLimitCount())) {
|
||||
return;
|
||||
}
|
||||
// 查询已领过券的用户
|
||||
@ -360,7 +360,8 @@ public class CouponServiceImpl implements CouponService {
|
||||
}
|
||||
|
||||
// 2.1 过滤领取数量无限制的
|
||||
Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId, template -> template.getTakeLimitCount() != -1);
|
||||
Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId,
|
||||
template -> !couponTemplateService.isTakeLimitCountUnlimited(template.getTakeLimitCount()));
|
||||
// 2.2 检查用户领取的数量是否超过限制
|
||||
if (CollUtil.isNotEmpty(templateIds)) {
|
||||
Map<Long, Integer> couponTakeCountMap = this.getTakeCountMapByTemplateIds(templateIds, userId);
|
||||
|
||||
@ -18,6 +18,23 @@ import java.util.List;
|
||||
*/
|
||||
public interface CouponTemplateService {
|
||||
|
||||
/**
|
||||
* 判断是否不限制每人领取数量
|
||||
*
|
||||
* @param takeLimitCount 每人限领个数
|
||||
* @return 是否不限制
|
||||
*/
|
||||
boolean isTakeLimitCountUnlimited(Integer takeLimitCount);
|
||||
|
||||
/**
|
||||
* 判断是否不限制总发放数量
|
||||
*
|
||||
* @param totalCount 发放数量
|
||||
* @return 是否不限制
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isTotalCountUnlimited(Integer totalCount);
|
||||
|
||||
/**
|
||||
* 创建优惠劵模板
|
||||
*
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.coupon;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.product.api.category.ProductCategoryApi;
|
||||
@ -41,6 +40,16 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
|
||||
@Override
|
||||
public boolean isTakeLimitCountUnlimited(Integer takeLimitCount) {
|
||||
return CouponTemplateDO.TAKE_LIMIT_COUNT_MAX.equals(takeLimitCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTotalCountUnlimited(Integer totalCount) {
|
||||
return CouponTemplateDO.TOTAL_COUNT_MAX.equals(totalCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
|
||||
// 校验商品范围
|
||||
@ -59,7 +68,7 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
|
||||
CouponTemplateDO couponTemplate = validateCouponTemplateExists(updateReqVO.getId());
|
||||
// 校验发放数量不能过小(仅在 CouponTakeTypeEnum.USER 用户领取时)
|
||||
if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeType())
|
||||
&& ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TAKE_LIMIT_COUNT_MAX) // 非不限制
|
||||
&& !isTotalCountUnlimited(updateReqVO.getTotalCount()) // 非不限制总发放数量
|
||||
&& updateReqVO.getTotalCount() < couponTemplate.getTakeCount()) {
|
||||
throw exception(COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL, couponTemplate.getTakeCount());
|
||||
}
|
||||
|
||||
@ -71,13 +71,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>4.35.79.ALL</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>4.40.607.ALL</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
|
||||
@ -130,4 +130,18 @@ public class PayTransferCreateReqDTO {
|
||||
return channelExtras;
|
||||
}
|
||||
|
||||
// ========== 支付宝场景 ==========
|
||||
|
||||
/**
|
||||
* 【支付宝】构建转账渠道额外参数
|
||||
*
|
||||
* @param sceneName 转账场景名称,用于描述转账用途
|
||||
* @return channelExtras
|
||||
*/
|
||||
public static Map<String, String> buildAlipayChannelExtra(String sceneName) {
|
||||
Map<String, String> channelExtras = new HashMap<>();
|
||||
channelExtras.put("sceneName", sceneName);
|
||||
return channelExtras;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -234,6 +234,10 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
if (reqDTO.getChannelExtras() != null) {
|
||||
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
||||
}
|
||||
String sceneName = MapUtil.getStr(reqDTO.getChannelExtras(), "sceneName");
|
||||
if (StrUtil.isNotBlank(sceneName)) {
|
||||
model.setTransferSceneName(sceneName);
|
||||
}
|
||||
// ② 个性化的参数
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); // 暂时只考虑转账到支付宝,银行没有权限 https://opendocs.alipay.com/open/02byvc?scene=66dd06f5a923403393b85de68d3c0055
|
||||
|
||||
@ -61,6 +61,7 @@ public class OperateLogController {
|
||||
@Operation(summary = "导出操作日志")
|
||||
@GetMapping("/export-excel")
|
||||
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
|
||||
@TransMethodResult
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
|
||||
@ -5,8 +5,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsSendReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
@ -26,8 +28,6 @@ public interface AuthConvert {
|
||||
|
||||
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
|
||||
|
||||
AuthLoginRespVO convert(OAuth2AccessTokenDO bean);
|
||||
|
||||
default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
|
||||
return AuthPermissionInfoRespVO.builder()
|
||||
.user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class))
|
||||
|
||||
@ -91,10 +91,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
|
||||
@Override
|
||||
public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
|
||||
// 1. 执行请求
|
||||
// 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplate
|
||||
// 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/GetSmsTemplate
|
||||
TreeMap<String, Object> queryParam = new TreeMap<>();
|
||||
queryParam.put("TemplateCode", apiTemplateId);
|
||||
JSONObject response = request("QuerySmsTemplate", queryParam);
|
||||
JSONObject response = request("GetSmsTemplate", queryParam);
|
||||
|
||||
// 2.1 请求失败
|
||||
String code = response.getStr("Code");
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
@ -215,13 +216,13 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
||||
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
|
||||
// 构建返回结果
|
||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||
return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthLoginRespVO refreshToken(String refreshToken) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||
return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -180,7 +180,13 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
.setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
|
||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()));
|
||||
accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
|
||||
// 优先从 refreshToken 获取租户编号,避免 ThreadLocal 被污染时导致 tenantId 为 null
|
||||
// 可能关联的 issue:https://t.zsxq.com/JIi5G
|
||||
Long tenantId = refreshTokenDO.getTenantId();
|
||||
if (tenantId == null) {
|
||||
tenantId = TenantContextHolder.getTenantId();
|
||||
}
|
||||
accessTokenDO.setTenantId(tenantId);
|
||||
oauth2AccessTokenMapper.insert(accessTokenDO);
|
||||
// 记录到 Redis 中
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user