diff --git a/pom.xml b/pom.xml
index 9082f0b0eb..9d772bd543 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
yudao-module-system
yudao-module-infra
- yudao-module-bpm
+
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
index d236d1817f..47a53a5957 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.string;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
+import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
import java.util.Collection;
@@ -77,4 +78,30 @@ public class StrUtils {
.collect(Collectors.joining("\n"));
}
+ /**
+ * 拼接方法的参数
+ *
+ * 特殊:排除一些无法序列化的参数,如 ServletRequest、ServletResponse、MultipartFile
+ *
+ * @param joinPoint 连接点
+ * @return 拼接后的参数
+ */
+ public static String joinMethodArgs(JoinPoint joinPoint) {
+ Object[] args = joinPoint.getArgs();
+ if (ArrayUtil.isEmpty(args)) {
+ return "";
+ }
+ return ArrayUtil.join(args, ",", item -> {
+ if (item == null) {
+ return "";
+ }
+ // 讨论可见:https://t.zsxq.com/XUJVk、https://t.zsxq.com/MnKcL
+ String clazzName = item.getClass().getName();
+ if (StrUtil.startWithAny(clazzName, "javax.servlet", "jakarta.servlet", "org.springframework.web")) {
+ return "";
+ }
+ return item;
+ });
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
index a804cce871..c142e3dbb4 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.config;
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
@@ -19,11 +20,13 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import jakarta.annotation.Resource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.BatchStrategies;
@@ -32,14 +35,24 @@ import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.util.pattern.PathPattern;
+import java.util.Map;
import java.util.Objects;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
@AutoConfiguration
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
@EnableConfigurationProperties(TenantProperties.class)
public class YudaoTenantAutoConfiguration {
+ @Resource
+ private ApplicationContext applicationContext;
+
@Bean
public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
return new TenantFrameworkServiceImpl(tenantApi);
@@ -67,13 +80,41 @@ public class YudaoTenantAutoConfiguration {
// ========== WEB ==========
@Bean
- public FilterRegistrationBean tenantContextWebFilter() {
+ public FilterRegistrationBean tenantContextWebFilter(TenantProperties tenantProperties) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new TenantContextWebFilter());
registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);
+ addIgnoreUrls(tenantProperties);
return registrationBean;
}
+ /**
+ * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中
+ *
+ * @param tenantProperties 租户配置
+ */
+ private void addIgnoreUrls(TenantProperties tenantProperties) {
+ // 获得接口对应的 HandlerMethod 集合
+ RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
+ applicationContext.getBean("requestMappingHandlerMapping");
+ Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
+ // 获得有 @TenantIgnore 注解的接口
+ for (Map.Entry entry : handlerMethodMap.entrySet()) {
+ HandlerMethod handlerMethod = entry.getValue();
+ if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) {
+ continue;
+ }
+ // 添加到忽略的 URL 中
+ if (entry.getKey().getPatternsCondition() != null) {
+ tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns());
+ }
+ if (entry.getKey().getPathPatternsCondition() != null) {
+ tenantProperties.getIgnoreUrls().addAll(
+ convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
+ }
+ }
+ }
+
// ========== Security ==========
@Bean
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java
index 7b5e145e4c..402184895b 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -18,7 +18,7 @@ public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
return SecureUtil.md5(methodName + argsStr);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java
index 2fa91ff976..dd89617058 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -19,7 +19,7 @@ public class UserIdempotentKeyResolver implements IdempotentKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
Long userId = WebFrameworkUtils.getLoginUserId();
Integer userType = WebFrameworkUtils.getLoginUserType();
return SecureUtil.md5(methodName + argsStr + userId + userType);
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java
index 8d6253caa7..4ed68ddb36 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java
@@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -19,7 +19,7 @@ public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
String clientIp = ServletUtils.getClientIP();
return SecureUtil.md5(methodName + argsStr + clientIp);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java
index 236ea45cb6..9ae2ff3f1d 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -18,7 +18,7 @@ public class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
return SecureUtil.md5(methodName + argsStr);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java
index 300a4d2f1e..653790dff7 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java
@@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.system.SystemUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -19,7 +19,7 @@ public class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
String serverNode = String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());
return SecureUtil.md5(methodName + argsStr + serverNode);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java
index a8d1c3a98c..eccabf19bc 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -19,7 +19,7 @@ public class UserRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
- String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ String argsStr = StrUtils.joinMethodArgs(joinPoint);
Long userId = WebFrameworkUtils.getLoginUserId();
Integer userType = WebFrameworkUtils.getLoginUserType();
return SecureUtil.md5(methodName + argsStr + userId + userType);
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
index 3f72767c0b..75a6d92a71 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.URLUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.service.file.FileService;
@@ -77,6 +78,7 @@ public class FileController {
@GetMapping("/{configId}/get/**")
@PermitAll
+ @TenantIgnore
@Operation(summary = "下载文件")
@Parameter(name = "configId", description = "配置编号", required = true)
public void getFileContent(HttpServletRequest request,
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
index cb645f42b5..6e4b0f62bc 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -164,8 +164,6 @@ public class CodegenEngine {
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
// VUE3_VBEN5_ANTD
- .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"),
- vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm
index 18dd72bd13..d3342f1fc8 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm
@@ -1,4 +1,5 @@
import type { PageParam, PageResult } from '@vben/request';
+import type { Dayjs } from 'dayjs';
import { requestClient } from '#/api/request';
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
@@ -16,7 +17,7 @@ export namespace ${simpleClassName}Api {
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
- ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
+ ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
@@ -32,7 +33,7 @@ export namespace ${simpleClassName}Api {
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
- ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
+ ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm
deleted file mode 100644
index 9f356f6848..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm
+++ /dev/null
@@ -1,349 +0,0 @@
-import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
-import type { VbenFormSchema } from '#/adapter/form';
-import type { OnActionClickFn } from '#/adapter/vxe-table';
-import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
-
-import { z } from '#/adapter/form';
-#if(${table.templateType} == 2)## 树表需要导入这些
-import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
-import { handleTree } from '#/utils/tree';
-#end
-import { DICT_TYPE, getDictOptions } from '#/utils/dict';
-import { getRangePickerDefaultProps } from '#/utils/date';
-import { useAccess } from '@vben/access';
-
-const { hasAccessByCodes } = useAccess();
-
-/** 列表的搜索表单 */
-export function useGridFormSchema(): VbenFormSchema[] {
- return [
-#foreach($column in $columns)
-#if ($column.listOperation)
- #set ($dictType = $column.dictType)
- #set ($javaType = $column.javaType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
- #set ($dictMethod = "number")
- #elseif ($javaType == "String")
- #set ($dictMethod = "string")
- #elseif ($javaType == "Boolean")
- #set ($dictMethod = "boolean")
- #end
- {
- fieldName: '${javaField}',
- label: '${comment}',
- #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor")
- component: 'Input',
- componentProps: {
- allowClear: true,
- placeholder: '请输入${comment}',
- },
- #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
- component: 'Select',
- componentProps: {
- allowClear: true,
- #if ("" != $dictType)## 设置了 dictType 数据字典的情况
- options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
- #else## 未设置 dictType 数据字典的情况
- options: [],
- #end
- placeholder: '请选择${comment}',
- },
- #elseif($column.htmlType == "datetime")
- component: 'RangePicker',
- componentProps: {
- ...getRangePickerDefaultProps(),
- allowClear: true,
- },
- #end
- },
-#end
-#end
- ];
-}
-
-/** 列表的字段 */
-export function useGridColumns(
- onActionClick?: OnActionClickFn<${simpleClassName}Api.${simpleClassName}>,
-): VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>['columns'] {
- return [
-#if ($table.templateType == 12) ## 内嵌情况
- { type: 'expand', width: 80, slots: { content: 'expand_content' } },
-#end
-#foreach($column in $columns)
-#if ($column.listOperationResult)
- #set ($dictType = $column.dictType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- {
- field: '${javaField}',
- title: '${comment}',
- minWidth: 120,
- #if ($column.javaType == "LocalDateTime")## 时间类型
- formatter: 'formatDateTime',
- #elseif("" != $dictType)## 数据字典
- cellRender: {
- name: 'CellDict',
- props: { type: DICT_TYPE.$dictType.toUpperCase() },
- },
- #end
- #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列
- treeNode: true,
- #end
- },
-#end
-#end
- {
- field: 'operation',
- title: '操作',
- minWidth: 200,
- align: 'center',
- fixed: 'right',
- headerAlign: 'center',
- showOverflow: false,
- cellRender: {
- attrs: {
- nameField: '${columns[0].javaField}',
- nameTitle: '${table.classComment}',
- onClick: onActionClick,
- },
- name: 'CellOperation',
- options: [
-#if (${table.templateType} == 2)## 树表特有操作
- {
- code: 'append',
- text: '新增下级',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:create']),
- },
-#end
- {
- code: 'edit',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']),
- },
- {
- code: 'delete',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
-#if (${table.templateType} == 2)## 树表禁止删除带有子节点的数据
- disabled: (row: ${simpleClassName}Api.${simpleClassName}) => {
- return !!(row.children && row.children.length > 0);
- },
-#end
- },
- ],
- },
- },
- ];
-}
-
-## 标准模式和内嵌模式时,主子关系一对一则生成表单schema,一对多则生成列表schema(内嵌模式时表单schema也要生成)。erp 模式时都生成
-## 特殊:主子表专属逻辑
-#foreach ($subTable in $subTables)
- #set ($index = $foreach.count - 1)
- #set ($subColumns = $subColumnsList.get($index))##当前字段数组
- #set ($subSimpleClassName = $subSimpleClassNames.get($index))
- #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
- #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
-// ==================== 子表($subTable.classComment) ====================
-
-#if ($table.templateType == 11) ## erp 情况
-/** 列表的搜索表单 */
-export function use${subSimpleClassName}GridFormSchema(): VbenFormSchema[] {
- return [
- #foreach($column in $subColumns)
- #if ($column.listOperation)
- #set ($dictType = $column.dictType)
- #set ($javaType = $column.javaType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
- #set ($dictMethod = "number")
- #elseif ($javaType == "String")
- #set ($dictMethod = "string")
- #elseif ($javaType == "Boolean")
- #set ($dictMethod = "boolean")
- #end
- {
- fieldName: '${javaField}',
- label: '${comment}',
- #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor")
- component: 'Input',
- componentProps: {
- allowClear: true,
- placeholder: '请输入${comment}',
- },
- #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
- component: 'Select',
- componentProps: {
- allowClear: true,
- #if ("" != $dictType)## 设置了 dictType 数据字典的情况
- options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
- #else## 未设置 dictType 数据字典的情况
- options: [],
- #end
- placeholder: '请选择${comment}',
- },
- #elseif($column.htmlType == "datetime")
- component: 'RangePicker',
- componentProps: {
- ...getRangePickerDefaultProps(),
- allowClear: true,
- },
- #end
- },
- #end
- #end
- ];
-}
-
-/** 列表的字段 */
-export function use${subSimpleClassName}GridColumns(
- onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>,
-): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
- return [
- #foreach($column in $subColumns)
- #if ($column.listOperationResult)
- #set ($dictType = $column.dictType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- {
- field: '${javaField}',
- title: '${comment}',
- minWidth: 120,
- #if ($column.javaType == "LocalDateTime")## 时间类型
- formatter: 'formatDateTime',
- #elseif("" != $dictType)## 数据字典
- cellRender: {
- name: 'CellDict',
- props: { type: DICT_TYPE.$dictType.toUpperCase() },
- },
- #end
- },
- #end
- #end
- {
- field: 'operation',
- title: '操作',
- minWidth: 200,
- align: 'center',
- fixed: 'right',
- headerAlign: 'center',
- showOverflow: false,
- cellRender: {
- attrs: {
- nameField: '${columns[0].javaField}',
- nameTitle: '${subTable.classComment}',
- onClick: onActionClick,
- },
- name: 'CellOperation',
- options: [
- {
- code: 'edit',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']),
- },
- {
- code: 'delete',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
- },
- ],
- },
- },
- ];
-}
-
-#else
- #if ($subTable.subJoinMany) ## 一对多
- /** 新增/修改列表的字段 */
- export function use${subSimpleClassName}GridEditColumns(
- onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>,
- ): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
- return [
- #foreach($column in $subColumns)
- #if ($column.createOperation || $column.updateOperation)
- #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
- #set ($dictType = $column.dictType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
- #set ($dictMethod = "number")
- #elseif ($javaType == "String")
- #set ($dictMethod = "string")
- #elseif ($javaType == "Boolean")
- #set ($dictMethod = "boolean")
- #end
- {
- field: '${javaField}',
- title: '${comment}',
- minWidth: 120,
- slots: { default: '${javaField}' },
- #if ($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio")
- #if ("" != $dictType)## 有数据字典
- params: {
- options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
- },
- #else
- params: {
- options: [],
- },
- #end
- #end
- },
- #end
- #end
- #end
- {
- field: 'operation',
- title: '操作',
- minWidth: 60,
- align: 'center',
- fixed: 'right',
- headerAlign: 'center',
- showOverflow: false,
- cellRender: {
- attrs: {
- nameField: '${columns[0].javaField}',
- nameTitle: '${table.classComment}',
- onClick: onActionClick,
- },
- name: 'CellOperation',
- options: [
- {
- code: 'delete',
- show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
- },
- ],
- },
- },
- ];
- }
- #end
- #if ($table.templateType == 12) ## 内嵌情况
- /** 列表的字段 */
- export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
- return [
- #foreach($column in $subColumns)
- #if ($column.listOperationResult)
- #set ($dictType = $column.dictType)
- #set ($javaField = $column.javaField)
- #set ($comment = $column.columnComment)
- {
- field: '${javaField}',
- title: '${comment}',
- minWidth: 120,
- #if ($column.javaType == "LocalDateTime")## 时间类型
- formatter: 'formatDateTime',
- #elseif("" != $dictType)## 数据字典
- cellRender: {
- name: 'CellDict',
- props: { type: DICT_TYPE.$dictType.toUpperCase() },
- },
- #end
- },
- #end
- #end
- ];
- }
-
- #end
-#end
-#end
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm
index 281df24799..187f66c144 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm
@@ -5,11 +5,11 @@ import type { Rule } from 'ant-design-vue/es/form';
import { useVbenModal } from '@vben/common-ui';
import { Tinymce as RichTextarea } from '#/components/tinymce';
import { ImageUpload, FileUpload } from "#/components/upload";
-import { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker } from 'ant-design-vue';
+import { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker, TreeSelect } from 'ant-design-vue';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
#if($table.templateType == 2)## 树表需要导入这些
import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
-import { handleTree } from '#/utils/tree';
+import { handleTree } from '@vben/utils'
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
@@ -25,9 +25,8 @@ import { $t } from '#/locales';
import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
const emit = defineEmits(['success']);
+
const formRef = ref();
-const labelCol = { span: 5 };
-const wrapperCol = { span: 13 };
const formData = ref>({
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
@@ -91,10 +90,12 @@ const resetForm = () => {
/** 获得${table.classComment}树 */
const get${simpleClassName}Tree = async () => {
${classNameVar}Tree.value = []
- const data = await get${simpleClassName}List()
- const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }
- root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
- ${classNameVar}Tree.value.push(root)
+ const data = await get${simpleClassName}List({});
+ data.unshift({
+ id: 0,
+ name: '顶级${table.classComment}',
+ });
+ ${classNameVar}Tree.value = handleTree(data);
}
#end
@@ -111,10 +112,11 @@ const [Modal, modalApi] = useVbenModal({
#if ($subTable.subJoinMany) ## 一对多
## TODO 列表值校验?
#else
- const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
- if (!${subClassNameVar}Valid) {
- subTabsName.value = '${subClassNameVar}';
- return;
+ try {
+ await ${subClassNameVar}FormRef.value?.validate()
+ } catch (e) {
+ subTabsName.value = '${subClassNameVar}'
+ return
}
#end
#end
@@ -133,7 +135,7 @@ const [Modal, modalApi] = useVbenModal({
#if ($subTable.subJoinMany)
data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();
#else
- data.${subClassNameVar} = await ${subClassNameVar}FormRef.value?.getValues();
+ data.${subClassNameVar} = ${subClassNameVar}FormRef.value?.getValues();
#end
#end
#end
@@ -156,7 +158,6 @@ const [Modal, modalApi] = useVbenModal({
resetForm()
return;
}
-
// 加载数据
let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
if (!data) {
@@ -185,8 +186,8 @@ const [Modal, modalApi] = useVbenModal({
ref="formRef"
:model="formData"
:rules="rules"
- :label-col="labelCol"
- :wrapper-col="wrapperCol"
+ :label-col="{ span: 5 }"
+ :wrapper-col="{ span: 18 }"
>
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)
@@ -207,9 +208,17 @@ const [Modal, modalApi] = useVbenModal({
v-model:value="formData.${javaField}"
:treeData="${classNameVar}Tree"
#if ($treeNameColumn.javaField == "name")
- :fieldNames="defaultProps"
+ :fieldNames="{
+ label: 'name',
+ value: 'id',
+ children: 'children',
+ }"
#else
- :fieldNames="{...defaultProps, label: '$treeNameColumn.javaField'}"
+ :fieldNames="{
+ label: '$treeNameColumn.javaField',
+ value: 'id',
+ children: 'children',
+ }"
#end
checkable
treeDefaultExpandAll
@@ -239,9 +248,10 @@ const [Modal, modalApi] = useVbenModal({
+ >
+ {{ dict.label }}
+
#else##没数据字典
#end
@@ -254,9 +264,10 @@ const [Modal, modalApi] = useVbenModal({
+ >
+ {{ dict.label }}
+
#else##没数据字典
#end
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
index aaaad6b1d8..c7f4822686 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
@@ -1,8 +1,9 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+ #if (${table.templateType} == 2)
+
+ #end
-
-
-
-
-
-
-
+
+
+
## 特殊:主子表专属逻辑
#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
@@ -298,6 +376,8 @@ onMounted(() => {
+ #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
+
#else
#end
@@ -305,6 +385,16 @@ onMounted(() => {
#end
+#if ( $table.templateType == 2 )
+
+#end
+#if ( $table.templateType != 2 )
+#end
#if ($table.templateType == 11) ## erp情况
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm
index e114b7daea..77b9d11dd2 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm
@@ -4,49 +4,54 @@
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
-
+
+
+
+ #elseif($column.htmlType == "imageUpload")## 图片上传
+
+
+
+ #elseif($column.htmlType == "fileUpload")## 文件上传
+
+
+
+ #elseif($column.htmlType == "editor")## 文本编辑器
+
+
+
+ #elseif($column.htmlType == "select")## 下拉框
+
+
+
+ #elseif($column.htmlType == "checkbox")## 多选框
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+
+ #end
+
+
+ #elseif($column.htmlType == "radio")## 单选框
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+ 请选择字典生成
+ #end
+
+
+ #elseif($column.htmlType == "datetime")## 时间框
+
+
+
+ #elseif($column.htmlType == "textarea")## 文本框
+
+
+
+ #end
+ #end
+ #end
+
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm
index 2c596951cc..7f0ec3a1e9 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm
@@ -1,2 +1,2 @@
## 主表的 normal 和 inner 使用相同的 form 表单
-#parse("codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm")
\ No newline at end of file
+#parse("codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm")
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm
index 072fd8c3e7..c838f95320 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm
@@ -7,19 +7,19 @@
#if ($table.templateType == 11) ## erp
-
-
-
-
- {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
-
-
-
+
+
+
+
+
+
+
+ #elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
+
+
+
+ #elseif($column.htmlType == "datetime")
+ #if ($column.listOperationCondition != "BETWEEN")## 非范围
+
+
+
+ #else## 范围
+
+
+
+ #end
+ #end
+ #end
+ #end
+
+ 重置
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
+ {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
+
+
+
+
+ #foreach($column in $subColumns)
+ #if ($column.listOperationResult)
+ #set ($dictType=$column.dictType)
+ #set ($javaField = $column.javaField)
+ #set ($comment=$column.columnComment)
+ #if ($column.javaType == "LocalDateTime")## 时间类型
+
+
+ {{formatDateTime(row.${javaField})}}
+
+
+ #elseif($column.dictType && "" != $column.dictType)## 数据字典
+
+
+
+
+
+ #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
+
+ #else
+
+ #end
+ #end
+ #end
+
+
+
+ {{ $t('ui.actionTitle.edit') }}
+
+
+ {{ $t('ui.actionTitle.delete') }}
+
+
+
+
+
+
+
+
#else
-
+
+
+ #foreach($column in $subColumns)
+ #if ($column.listOperationResult)
+ #set ($dictType=$column.dictType)
+ #set ($javaField = $column.javaField)
+ #set ($comment=$column.columnComment)
+ #if ($column.javaType == "LocalDateTime")## 时间类型
+
+
+ {{formatDateTime(row.${javaField})}}
+
+
+ #elseif($column.dictType && "" != $column.dictType)## 数据字典
+
+
+
+
+
+ #else
+
+ #end
+ #end
+ #end
+
+
#end
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm
index 2d8aa202d7..d46037b0aa 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm
@@ -1,4 +1,4 @@
## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
## 1)inner 使用 list 不分页,erp 使用 page 分页
## 2)erp 支持单个子表的新增、修改、删除,inner 不支持
-#parse("codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm")
\ No newline at end of file
+#parse("codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm")
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm
index 18dd72bd13..d3342f1fc8 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm
@@ -1,4 +1,5 @@
import type { PageParam, PageResult } from '@vben/request';
+import type { Dayjs } from 'dayjs';
import { requestClient } from '#/api/request';
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
@@ -16,7 +17,7 @@ export namespace ${simpleClassName}Api {
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
- ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
+ ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
@@ -32,7 +33,7 @@ export namespace ${simpleClassName}Api {
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
- ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
+ ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm
index 2be97e4829..227b8c2982 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm
@@ -21,24 +21,11 @@ import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<${simpleClassName}Api.${simpleClassName}>();
-#if (${table.templateType} == 2)## 树表特有:父ID处理
-const parentId = ref(); // 新增下级时的父级 ID
-
-const getTitle = computed(() => {
- if (formData.value?.id) {
- return $t('ui.actionTitle.edit', ['${table.classComment}']);
- }
- return parentId.value
- ? $t('ui.actionTitle.create', ['下级${table.classComment}'])
- : $t('ui.actionTitle.create', ['${table.classComment}']);
-});
-#else## 标准表标题
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['${table.classComment}'])
: $t('ui.actionTitle.create', ['${table.classComment}']);
});
-#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
@@ -128,7 +115,6 @@ const [Modal, modalApi] = useVbenModal({
formData.value = undefined;
return;
}
-
// 加载数据
let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
if (!data) {
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm
index 072fd8c3e7..2909a5badf 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm
@@ -13,6 +13,7 @@
#if ($subTable.subJoinMany) ## 一对多
import { Plus } from "@vben/icons";
import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
+import { ImageUpload, FileUpload } from "#/components/upload";
import type { OnActionClickParams } from '#/adapter/vxe-table';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { use${subSimpleClassName}GridEditColumns } from '../data';
@@ -145,15 +146,11 @@ watch(
#elseif($column.htmlType == "imageUpload")## 图片上传
-
+
#elseif($column.htmlType == "fileUpload")## 文件上传
-
-
- #elseif($column.htmlType == "editor")## 文本编辑器
-
-
+
#elseif($column.htmlType == "select")## 下拉框
@@ -180,7 +177,7 @@ watch(
valueFormat='x'
/>
- #elseif($column.htmlType == "textarea")## 文本框
+ #elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
index a664501a54..c59c452163 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
@@ -300,7 +300,7 @@ public class PointActivityServiceImpl implements PointActivityService {
throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED);
}
// 2.2 校验库存是否充足
- if (count >= product.getStock()) {
+ if (count > product.getStock()) {
throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
}
return BeanUtils.toBean(product, PointValidateJoinRespDTO.class);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
index f1b6dab644..2ebd9894b8 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
@@ -317,7 +317,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
throw exception(SECKILL_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
}
// 2.2 校验库存是否充足
- if (count >= product.getStock()) {
+ if (count > product.getStock()) {
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
}
return SeckillActivityConvert.INSTANCE.convert02(activity, product);
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java
index 81b96184c9..c852e03ead 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java
@@ -12,9 +12,9 @@ import lombok.Getter;
@AllArgsConstructor
public enum ExpressClientEnum {
- NOT_PROVIDE("not-provide","未提供"),
- KD_NIAO("kd-niao", "快递鸟"),
- KD_100("kd-100", "快递100");
+ NOT_PROVIDE("not_provide","未提供"),
+ KD_NIAO("kd_niao", "快递鸟"),
+ KD_100("kd_100", "快递100");
/**
* 快递服务商唯一编码
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java
index 47e23923d2..2e9b72590e 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mp.controller.admin.open;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO;
import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenHandleMessageReqVO;
@@ -35,26 +36,6 @@ public class MpOpenController {
@Resource
private MpAccountService mpAccountService;
- /**
- * 接收微信公众号的校验签名
- *
- * 对应 文档
- */
- @Operation(summary = "校验签名") // 参见
- @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
- public String checkSignature(@PathVariable("appId") String appId,
- MpOpenCheckSignatureReqVO reqVO) {
- log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
- // 校验请求签名
- WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
- // 校验通过
- if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
- return reqVO.getEchostr();
- }
- // 校验不通过
- return "非法请求";
- }
-
/**
* 接收微信公众号的消息推送
*
@@ -62,6 +43,7 @@ public class MpOpenController {
*/
@Operation(summary = "处理消息")
@PostMapping(value = "/{appId}", produces = "application/xml; charset=UTF-8")
+ @TenantIgnore
public String handleMessage(@PathVariable("appId") String appId,
@RequestBody String content,
MpOpenHandleMessageReqVO reqVO) {
@@ -79,6 +61,27 @@ public class MpOpenController {
}
}
+ /**
+ * 接收微信公众号的校验签名
+ *
+ * 对应 文档
+ */
+ @Operation(summary = "校验签名") // 参见
+ @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
+ @TenantIgnore
+ public String checkSignature(@PathVariable("appId") String appId,
+ MpOpenCheckSignatureReqVO reqVO) {
+ log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
+ // 校验请求签名
+ WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
+ // 校验通过
+ if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
+ return reqVO.getEchostr();
+ }
+ // 校验不通过
+ return "非法请求";
+ }
+
private String handleMessage0(String appId, String content, MpOpenHandleMessageReqVO reqVO) {
// 校验请求签名
WxMpService mppService = mpServiceFactory.getRequiredMpService(appId);
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java
index bd13f925af..472b3575fe 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java
@@ -21,7 +21,7 @@ public interface MpUserMapper extends BaseMapperX {
}
default MpUserDO selectByAppIdAndOpenid(String appId, String openid) {
- return selectOne(MpUserDO::getAppId, appId,
+ return selectFirstOne(MpUserDO::getAppId, appId,
MpUserDO::getOpenid, openid);
}
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java
index 0397125144..daeadb9864 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mp.service.handler.menu;
import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
import cn.iocoder.yudao.module.mp.service.menu.MpMenuService;
import me.chanjar.weixin.common.session.WxSessionManager;
-import me.chanjar.weixin.mp.api.WxMpMenuService;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
@@ -13,8 +12,6 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
-import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
-
/**
* 自定义菜单的事件处理器
*
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java
index 4e4a4ebf4e..aaae88daa8 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java
@@ -29,7 +29,7 @@ public class MessageReceiveHandler implements WxMpMessageHandler {
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context,
WxMpService wxMpService, WxSessionManager sessionManager) {
log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
- mpMessageService.receiveMessage(MpContextHolder.getAppId(), wxMessage);
+ mpMessageService.receiveMessage(wxMpService, MpContextHolder.getAppId(), wxMessage);
return null;
}
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
index 01c691a342..9231bf0534 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageP
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageSendReqVO;
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
+import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
@@ -31,7 +32,7 @@ public interface MpMessageService {
* @param appId 微信公众号 appId
* @param wxMessage 消息
*/
- void receiveMessage(String appId, WxMpXmlMessage wxMessage);
+ void receiveMessage(WxMpService weixinService, String appId, WxMpXmlMessage wxMessage);
/**
* 使用公众号,给粉丝回复消息
diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
index 07553c7648..4a511c8b76 100644
--- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
+++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.mp.service.message;
import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO;
@@ -18,6 +18,9 @@ import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
import cn.iocoder.yudao.module.mp.service.material.MpMaterialService;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
import cn.iocoder.yudao.module.mp.service.user.MpUserService;
+import jakarta.annotation.Resource;
+import jakarta.validation.Validator;
+import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
@@ -25,12 +28,12 @@ import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import javax.annotation.Resource;
-import javax.validation.Validator;
+import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MESSAGE_SEND_FAIL;
@@ -69,18 +72,31 @@ public class MpMessageServiceImpl implements MpMessageService {
}
@Override
- public void receiveMessage(String appId, WxMpXmlMessage wxMessage) {
+ @SneakyThrows
+ public void receiveMessage(WxMpService weixinService, String appId, WxMpXmlMessage wxMessage) {
// 获得关联信息
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
Assert.notNull(account, "公众号账号({}) 不存在", appId);
- // 订阅事件不记录,因为此时公众号粉丝表中还没有此粉丝的数据
- // TODO @芋艿:这个修复,后续看看还有啥问题
- if (ObjUtil.equal(wxMessage.getEvent(), WxConsts.EventType.SUBSCRIBE)) {
- return;
- }
-
+ // 获取用户
MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser());
+ if (user == null) {
+ // 特殊情况:因为 receiveMessage 是异步记录,可能 SubscribeHandler 还没存储好 User,此时 sleep 轮询
+ for (int i = 0; i < 3; i++) {
+ ThreadUtil.sleep(5, TimeUnit.SECONDS);
+ user = mpUserService.getUser(appId, wxMessage.getFromUser());
+ if (user != null) {
+ break;
+ }
+ log.warn("[receiveMessage][粉丝({}/{}) 不存在,第 {} 次重试失败]", appId, wxMessage.getFromUser(), i + 1);
+ }
+ }
+ // 特殊情况:可能 SubscribeHandler 没处理正确(例如说发生异常),则主动创建
+ if (user == null) {
+ log.warn("[receiveMessage][粉丝({}/{}) 不存在,主动创建]", appId, wxMessage.getFromUser());
+ WxMpUser wxMpUser = weixinService.getUserService().userInfo(wxMessage.getFromUser());
+ user = mpUserService.saveUser(appId, wxMpUser);
+ }
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
// 记录消息
diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
index 7da7fd1d12..2d646b8021 100644
--- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
+++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
@@ -62,6 +63,7 @@ public class PayNotifyController {
@PostMapping(value = "/order/{channelId}")
@Operation(summary = "支付渠道的统一【支付】回调")
@PermitAll
+ @TenantIgnore
public String notifyOrder(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map params,
@RequestBody(required = false) String body) {
@@ -82,6 +84,7 @@ public class PayNotifyController {
@PostMapping(value = "/refund/{channelId}")
@Operation(summary = "支付渠道的统一【退款】回调")
@PermitAll
+ @TenantIgnore
public String notifyRefund(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map params,
@RequestBody(required = false) String body) {
@@ -102,6 +105,7 @@ public class PayNotifyController {
@PostMapping(value = "/transfer/{channelId}")
@Operation(summary = "支付渠道的统一【转账】回调")
@PermitAll
+ @TenantIgnore
public String notifyTransfer(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map params,
@RequestBody(required = false) String body) {
diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
index a8593a1930..09a9d326d6 100644
--- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
+++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.pay.service.demo;
import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -24,7 +25,6 @@ import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
import static cn.hutool.core.util.ObjectUtil.notEqual;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -231,7 +231,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
throw exception(DEMO_ORDER_NOT_FOUND);
}
// 1.2 校验退款订单匹配
- if (Objects.equals(order.getPayRefundId(), payRefundId)) {
+ if (ObjUtil.notEqual(order.getPayRefundId(), payRefundId)) {
log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!order 数据是:{}]",
id, payRefundId, toJsonString(order));
throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java
index cb5fb2e4b9..271dfe0696 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.controller.admin.captcha;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
@@ -27,6 +28,7 @@ public class CaptchaController {
@PostMapping({"/get"})
@Operation(summary = "获得验证码")
@PermitAll
+ @TenantIgnore
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
assert request.getRemoteHost() != null;
data.setBrowserInfo(getRemoteId(request));
@@ -36,6 +38,7 @@ public class CaptchaController {
@PostMapping("/check")
@Operation(summary = "校验验证码")
@PermitAll
+ @TenantIgnore
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
data.setBrowserInfo(getRemoteId(request));
return captchaService.check(data);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
index b682ea9d6a..e681dee9cb 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.controller.admin.sms;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import io.swagger.v3.oas.annotations.Operation;
@@ -24,6 +25,7 @@ public class SmsCallbackController {
@PostMapping("/aliyun")
@PermitAll
+ @TenantIgnore
@Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
public CommonResult receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {
String text = ServletUtils.getBody(request);
@@ -33,6 +35,7 @@ public class SmsCallbackController {
@PostMapping("/tencent")
@PermitAll
+ @TenantIgnore
@Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档")
public CommonResult receiveTencentSmsStatus(HttpServletRequest request) throws Throwable {
String text = ServletUtils.getBody(request);
@@ -43,6 +46,7 @@ public class SmsCallbackController {
@PostMapping("/huawei")
@PermitAll
+ @TenantIgnore
@Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档")
public CommonResult receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable {
smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody);
@@ -51,6 +55,7 @@ public class SmsCallbackController {
@PostMapping("/qiniu")
@PermitAll
+ @TenantIgnore
@Operation(summary = "七牛云短信的回调", description = "参见 https://developer.qiniu.com/sms/5910/message-push 文档")
public CommonResult receiveQiniuSmsStatus(@RequestBody String requestBody) throws Throwable {
smsSendService.receiveSmsStatus(SmsChannelEnum.QINIU.getCode(), requestBody);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
index b9dc0af524..76c3b5225b 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
@@ -39,6 +40,7 @@ public class TenantController {
@GetMapping("/get-id-by-name")
@PermitAll
+ @TenantIgnore
@Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号")
@Parameter(name = "name", description = "租户名", required = true, example = "1024")
public CommonResult getTenantIdByName(@RequestParam("name") String name) {
@@ -48,6 +50,7 @@ public class TenantController {
@GetMapping({ "simple-list" })
@PermitAll
+ @TenantIgnore
@Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项")
public CommonResult> getTenantSimpleList() {
List list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -57,6 +60,7 @@ public class TenantController {
@GetMapping("/get-by-website")
@PermitAll
+ @TenantIgnore
@Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
public CommonResult getTenantByWebsite(@RequestParam("website") String website) {
diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml
index 147391abbb..808737f15c 100644
--- a/yudao-server/src/main/resources/application-local.yaml
+++ b/yudao-server/src/main/resources/application-local.yaml
@@ -4,6 +4,7 @@ server:
--- #################### 数据库相关配置 ####################
spring:
autoconfigure:
+ # noinspection SpringBootApplicationYaml
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index 57b3b87d0a..59307604b3 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -273,16 +273,7 @@ yudao:
tenant: # 多租户相关配置项
enable: true
ignore-urls:
- - /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
- - /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号
- - /admin-api/system/tenant/simple-list # 获取租户列表,不许带租户编号
- - /admin-api/system/captcha/get # 获取图片验证码,和租户无关
- - /admin-api/system/captcha/check # 校验图片验证码,和租户无关
- - /admin-api/infra/file/*/get/** # 获取图片,和租户无关
- - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
- - /admin-api/pay/notify/** # 支付回调通知,不携带租户编号
- /jmreport/* # 积木报表,无法携带租户编号
- - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,无法携带租户编号
ignore-tables:
- system_tenant
- system_tenant_package
@@ -349,7 +340,7 @@ yudao:
receive-expire-time: 14d # 收货的过期时间
comment-expire-time: 7d # 评论的过期时间
express:
- client: KD_NIAO
+ client: kd_100
kd-niao:
api-key: cb022f1e-48f1-4c4a-a723-9001ac9676b8
business-id: 1809751