From 91ebd3428c274ae6bd29f6ede907c5ee5610578a Mon Sep 17 00:00:00 2001 From: lu-yg <128358973+lu-yg@users.noreply.github.com> Date: Thu, 18 Dec 2025 22:44:55 -0800 Subject: [PATCH 1/6] feat: Add Multi-Tenant Support with Context Isolation, Improve Token Handling, and Update Tests (#278) --- .../it/common/context/LoginUserContext.java | 10 ++++++-- .../it/login/config/SSOInterceptor.java | 12 +++++----- .../context/DefaultLoginUserContext.java | 17 +++++++++++++ .../com/tinyengine/it/mapper/AppMapper.java | 24 +++++++++---------- .../it/service/app/impl/AppServiceImpl.java | 22 ++++++++++------- .../app/impl/AppTemplateServiceImpl.java | 13 ++++++---- .../it/service/app/impl/PageServiceImpl.java | 4 ++-- .../service/app/impl/v1/AppV1ServiceImpl.java | 6 ++++- .../material/impl/BlockServiceImpl.java | 2 +- base/src/main/resources/mappers/AppMapper.xml | 17 +++++++++---- .../tinyengine/it/mapper/AppMapperTest.java | 6 ++++- .../service/app/impl/AppServiceImplTest.java | 15 ++++++++---- 12 files changed, 101 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/common/context/LoginUserContext.java b/base/src/main/java/com/tinyengine/it/common/context/LoginUserContext.java index 7358dffc..5a2ae916 100644 --- a/base/src/main/java/com/tinyengine/it/common/context/LoginUserContext.java +++ b/base/src/main/java/com/tinyengine/it/common/context/LoginUserContext.java @@ -21,8 +21,14 @@ */ public interface LoginUserContext { /** - * 返回当前用户所诉的业务租户信息 - * @return 租户ID + * 返回当前用户所在的业务租户id + * @return 租户Id + */ + String getTenantId(); + + /** + * 返回当前用户所在的业务租户信息 + * @return 租户 */ List getTenants(); diff --git a/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java b/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java index 28456880..b138bb8f 100644 --- a/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java +++ b/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java @@ -41,17 +41,17 @@ public class SSOInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String token = request.getHeader("Authorization"); - String requestURI = request.getRequestURI(); - - log.info("Intercepting: {}, Token: {}", requestURI, token != null ? "present" : "null"); - + String authorization = request.getHeader("Authorization"); // 如果没有token,重定向到登录页 - if (token == null || token.isEmpty()) { + if (authorization == null || authorization.isEmpty()) { log.info("No token, redirecting to: {}", SSO_SERVER); response.sendRedirect(SSO_SERVER); return false; } + String token = jwtUtil.getTokenFromRequest(authorization); + String requestURI = request.getRequestURI(); + + log.info("Intercepting: {}, Token: {}", requestURI, token != null ? "present" : "null"); try { // 验证token diff --git a/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java b/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java index ade54dfa..25467e16 100644 --- a/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java +++ b/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java @@ -16,6 +16,23 @@ public class DefaultLoginUserContext implements LoginUserContext { private static final ThreadLocal CURRENT_USER = new ThreadLocal<>(); private static final int DEFAULT_PLATFORM = 1; + private static final String DEFAULT_TENANT = "1"; + + /** + * 返回当前用户所在的业务租户id + * + * @return 租户Id + */ + @Override + public String getTenantId() { + UserInfo userInfo = CURRENT_USER.get(); + List tenantList = userInfo != null ? userInfo.getTenants() : null; + if (tenantList == null || tenantList.isEmpty()) { + return DEFAULT_TENANT; + } + + return tenantList.get(0).getId(); + } @Override public List getTenants() { diff --git a/base/src/main/java/com/tinyengine/it/mapper/AppMapper.java b/base/src/main/java/com/tinyengine/it/mapper/AppMapper.java index 20960c61..c308bcf6 100644 --- a/base/src/main/java/com/tinyengine/it/mapper/AppMapper.java +++ b/base/src/main/java/com/tinyengine/it/mapper/AppMapper.java @@ -31,7 +31,7 @@ public interface AppMapper extends BaseMapper { * * @return the list */ - List queryAllApp(); + List queryAllApp(String tenantId); /** * 分页查询应用所有信息 @@ -45,24 +45,24 @@ public interface AppMapper extends BaseMapper { * @param createdBy the createdBy * @return the list */ - List queryAllAppByPage(Integer pageSize, Integer offset, String name, - Integer industryId, Integer sceneId, String framework, String orderBy, String createdBy); + List queryAllAppByPage(Integer pageSize, Integer offset, String name, Integer industryId, + Integer sceneId, String framework, String orderBy, String createdBy, String tenantId); /** * 查询表t_app 应用总数 * * @return the int */ - @Select("SELECT COUNT(id) FROM t_app WHERE is_template IS NOT TRUE") - int queryAppTotal(); + @Select("SELECT COUNT(id) FROM t_app WHERE tenant_id = #{tenantId} AND is_template IS NOT TRUE") + int queryAppTotal(String tenantId); /** * 查询表t_app 模版总数 * * @return the int */ - @Select("SELECT COUNT(id) FROM t_app WHERE is_template = TRUE") - int queryAppTemplateTotal(); + @Select("SELECT COUNT(id) FROM t_app WHERE tenant_id = #{tenantId} AND is_template = TRUE") + int queryAppTemplateTotal(String tenantId); /** * 分页查询应用模版所有信息 @@ -76,8 +76,8 @@ List queryAllAppByPage(Integer pageSize, Integer offset, String name, * @param createdBy the createdBy * @return the list */ - List queryAllAppTemplate(Integer pageSize, Integer offset, String name, - Integer industryId, Integer sceneId, String framework, String orderBy, String createdBy); + List queryAllAppTemplate(Integer pageSize, Integer offset, String name, Integer industryId, + Integer sceneId, String framework, String orderBy, String createdBy, String tenantId); /** * 根据主键id查询应用模版数据 @@ -85,7 +85,7 @@ List queryAllAppTemplate(Integer pageSize, Integer offset, String name, * @param id the id * @return the app */ - App queryAppTemplateById(Integer id); + App queryAppTemplateById(Integer id, String tenantId); /** * 根据主键id查询表t_app数据 @@ -93,7 +93,7 @@ List queryAllAppTemplate(Integer pageSize, Integer offset, String name, * @param id the id * @return the app */ - App queryAppById(Integer id); + App queryAppById(Integer id, String tenantId); /** * 根据条件查询表t_app数据 @@ -109,7 +109,7 @@ List queryAllAppTemplate(Integer pageSize, Integer offset, String name, * @param id the id * @return the integer */ - Integer deleteAppById(@Param("id") Integer id); + Integer deleteAppById(@Param("id") Integer id, String tenantId); /** * 根据主键id更新表t_app数据 diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/AppServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/AppServiceImpl.java index 829dd911..f9263f1a 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/AppServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/AppServiceImpl.java @@ -14,6 +14,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.log.SystemServiceLog; @@ -28,6 +29,7 @@ import com.tinyengine.it.model.entity.App; import com.tinyengine.it.model.entity.I18nEntry; import com.tinyengine.it.model.entity.Platform; +import com.tinyengine.it.model.entity.Tenant; import com.tinyengine.it.service.app.AppService; import com.tinyengine.it.service.app.I18nEntryService; import com.tinyengine.it.service.app.impl.v1.AppV1ServiceImpl; @@ -74,6 +76,9 @@ public class AppServiceImpl extends ServiceImpl implements AppSe @Autowired private AppV1ServiceImpl appV1ServiceImpl; + @Autowired + private LoginUserContext loginUserContext; + /** * 查询表t_app所有数据 * @@ -81,7 +86,7 @@ public class AppServiceImpl extends ServiceImpl implements AppSe */ @Override public List queryAllApp() { - return baseMapper.queryAllApp(); + return baseMapper.queryAllApp(loginUserContext.getTenantId()); } /** @@ -105,8 +110,9 @@ public AppDto queryAllAppByPage(Integer currentPage, Integer pageSize, String or } int offset = (currentPage - 1) * pageSize; List apps = this.baseMapper.queryAllAppByPage(pageSize, offset, app.getName(), - app.getIndustryId(), app.getSceneId(), app.getFramework(), orderBy, app.getCreatedBy()); - Integer total = this.baseMapper.queryAppTotal(); + app.getIndustryId(), app.getSceneId(), app.getFramework(), orderBy, app.getCreatedBy(), + loginUserContext.getTenantId()); + Integer total = this.baseMapper.queryAppTotal(loginUserContext.getTenantId()); AppDto appDto = new AppDto(); appDto.setApps(apps); appDto.setTotal(total); @@ -122,7 +128,7 @@ public AppDto queryAllAppByPage(Integer currentPage, Integer pageSize, String or @Override @SystemServiceLog(description = "通过id查询应用实现方法") public Result queryAppById(Integer id) { - App app = baseMapper.queryAppById(id); + App app = baseMapper.queryAppById(id, loginUserContext.getTenantId()); if (app == null) { return Result.failed(ExceptionEnum.CM009); } @@ -149,8 +155,8 @@ public List queryAppByCondition(App app) { @Override @SystemServiceLog(description = "应用删除实现方法") public Result deleteAppById(Integer id) { - App app = baseMapper.queryAppById(id); - int result = baseMapper.deleteAppById(id); + App app = baseMapper.queryAppById(id, loginUserContext.getTenantId()); + int result = baseMapper.deleteAppById(id, loginUserContext.getTenantId()); if (result < 1) { return Result.failed(ExceptionEnum.CM009); } @@ -168,7 +174,7 @@ public Result deleteAppById(Integer id) { public Result updateAppById(App app) { // 如果更新extend_config字段,从platform获取数据,继承非route部分 if (app.getExtendConfig() != null && !app.getExtendConfig().isEmpty()) { - App appResult = baseMapper.queryAppById(app.getId()); + App appResult = baseMapper.queryAppById(app.getId(), loginUserContext.getTenantId()); Platform platform = platformService.queryPlatformById(appResult.getPlatformId()); Map appExtendConfig = platform.getAppExtendConfig(); appExtendConfig.remove("route"); @@ -178,7 +184,7 @@ public Result updateAppById(App app) { if (result < 1) { return Result.failed(ExceptionEnum.CM001); } - App selectedApp = baseMapper.queryAppById(app.getId()); + App selectedApp = baseMapper.queryAppById(app.getId(), loginUserContext.getTenantId()); return Result.success(selectedApp); } diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/AppTemplateServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/AppTemplateServiceImpl.java index 702e5213..6e7bd266 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/AppTemplateServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/AppTemplateServiceImpl.java @@ -15,6 +15,7 @@ import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.mapper.AppExtensionMapper; @@ -91,6 +92,9 @@ public class AppTemplateServiceImpl extends ServiceImpl implemen @Autowired private ModelMapper modelMapper; + @Autowired + private LoginUserContext loginUserContext; + /** * 分页查询应用模版所有信息 * @param currentPage the currentPage @@ -113,8 +117,9 @@ public AppDto queryAllAppTemplate(Integer currentPage, Integer pageSize, String int offset = (currentPage - 1) * pageSize; List apps = this.baseMapper.queryAllAppTemplate(pageSize, offset, app.getName(), - app.getIndustryId(), app.getSceneId(), app.getFramework(), orderBy, app.getCreatedBy()); - Integer total = this.baseMapper.queryAppTemplateTotal(); + app.getIndustryId(), app.getSceneId(), app.getFramework(), orderBy, app.getCreatedBy(), + loginUserContext.getTenantId()); + Integer total = this.baseMapper.queryAppTemplateTotal(loginUserContext.getTenantId()); AppDto appDto = new AppDto(); appDto.setApps(apps); appDto.setTotal(total); @@ -129,7 +134,7 @@ public AppDto queryAllAppTemplate(Integer currentPage, Integer pageSize, String */ @Override public Result queryAppTemplateById(Integer id) { - App app = baseMapper.queryAppTemplateById(id); + App app = baseMapper.queryAppTemplateById(id, loginUserContext.getTenantId()); if (app == null) { return Result.failed(ExceptionEnum.CM009); } @@ -157,7 +162,7 @@ public App createAppByTemplate(App app) { throw new ServiceException(ExceptionEnum.CM001.getResultCode(), ExceptionEnum.CM001.getResultMsg()); } copyData(templateId, app.getId()); - return appMapper.queryAppById(app.getId()); + return appMapper.queryAppById(app.getId(), loginUserContext.getTenantId()); } private void copyData(int templateId, int appId) { diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/PageServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/PageServiceImpl.java index 9659b671..f115314f 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/PageServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/PageServiceImpl.java @@ -177,7 +177,7 @@ public List queryAllPage(Integer aid) { public Page queryPageById(Integer id) { Page pageInfo = baseMapper.queryPageById(id); // 获取schemaMeta进行获取materialHistory中的framework进行判断 - String framework = appMapper.queryAppById(pageInfo.getApp()).getFramework(); + String framework = appMapper.queryAppById(pageInfo.getApp(), loginUserContext.getTenantId()).getFramework(); if (framework.isEmpty()) { throw new ServiceException(ExceptionEnum.CM312.getResultCode(), ExceptionEnum.CM312.getResultMsg()); } @@ -495,7 +495,7 @@ public Page addIsHome(Page pageInfo) { * @return the app home page id */ public int getAppHomePageId(int appId) { - App appInfo = appMapper.queryAppById(appId); + App appInfo = appMapper.queryAppById(appId, loginUserContext.getTenantId()); // appHomePageId 存在为null的情况,即app没有设置首页 Integer homePage = appInfo.getHomePage(); diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java index d2846ed0..c2bf340d 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java @@ -16,6 +16,7 @@ import static com.tinyengine.it.common.utils.Utils.findMaxVersion; import com.fasterxml.jackson.core.type.TypeReference; +import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.common.log.SystemServiceLog; import com.tinyengine.it.common.utils.Schema; @@ -142,6 +143,9 @@ public class AppV1ServiceImpl implements AppV1Service { @Autowired private ComponentLibraryMapper componentLibraryMapper; + @Autowired + private LoginUserContext loginUserContext; + /** * 获取应用schema * @@ -275,7 +279,7 @@ private List getPackages() { * @return the meta */ public MetaDto getMetaDto(Integer id) { - App app = appMapper.queryAppById(id); + App app = appMapper.queryAppById(id, loginUserContext.getTenantId()); Platform platform = platformService.queryPlatformById(app.getPlatformId()); diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java index e22361ee..60cd055e 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java @@ -639,7 +639,7 @@ public Result> listNew(String appId, String groupId) { if (appId != null && !appId.isEmpty()) { appIdTemp = Integer.parseInt(appId); } - App apps = appMapper.queryAppById(appIdTemp); + App apps = appMapper.queryAppById(appIdTemp, loginUserContext.getTenantId()); if (groupIdTemp != 0) { if (!apps.getId().equals(appIdTemp)) { return Result.failed(ExceptionEnum.CM206); diff --git a/base/src/main/resources/mappers/AppMapper.xml b/base/src/main/resources/mappers/AppMapper.xml index 03478304..05bd57cd 100644 --- a/base/src/main/resources/mappers/AppMapper.xml +++ b/base/src/main/resources/mappers/AppMapper.xml @@ -430,7 +430,7 @@ SELECT - WHERE A.is_template IS NOT TRUE + WHERE A.is_template IS NOT TRUE AND A.tenant_id = #{tenantId} ORDER BY A.created_time DESC @@ -455,6 +455,9 @@ AND A.created_by = #{createdBy} + + AND A.tenant_id = #{tenantId} + ORDER BY @@ -488,6 +491,9 @@ AND A.created_by = #{createdBy} + + AND A.tenant_id = #{tenantId} + ORDER BY @@ -505,7 +511,7 @@ - A.id = #{id} + A.id = #{id} AND A.tenant_id = #{tenantId} @@ -515,7 +521,8 @@ - A.id = #{id} AND A.is_template = true + A.tenant_id = #{tenantId} AND + A.id = #{id} AND A.is_template = true @@ -532,7 +539,7 @@ DELETE FROM t_app - WHERE id = #{id} + WHERE id = #{id} AND tenant_id = #{tenantId} @@ -542,7 +549,7 @@ WHERE - id=#{id} + id = #{id} AND tenant_id = #{tenantId} diff --git a/base/src/test/java/com/tinyengine/it/mapper/AppMapperTest.java b/base/src/test/java/com/tinyengine/it/mapper/AppMapperTest.java index 3c9d057c..05595afa 100644 --- a/base/src/test/java/com/tinyengine/it/mapper/AppMapperTest.java +++ b/base/src/test/java/com/tinyengine/it/mapper/AppMapperTest.java @@ -15,6 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.model.entity.App; import org.junit.jupiter.api.Disabled; @@ -35,9 +36,12 @@ class AppMapperTest { @Autowired private AppMapper appMapper; + @Autowired + LoginUserContext loginUserContext; @Test void queryAllApp() { - List result = appMapper.queryAllApp(); + String tenantId = loginUserContext.getTenantId(); + List result = appMapper.queryAllApp(tenantId); assertNotNull(result); assertEquals(1, result.size()); App app = result.get(0); diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java index e78f8c66..3087a176 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java @@ -19,6 +19,7 @@ import cn.hutool.core.util.ReflectUtil; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.mapper.AppMapper; import com.tinyengine.it.mapper.I18nEntryMapper; import com.tinyengine.it.model.dto.I18nEntryDto; @@ -64,6 +65,10 @@ class AppServiceImplTest { @InjectMocks private AppServiceImpl appServiceImpl; + @Mock + LoginUserContext loginUserContext; + + private String TENANTID = loginUserContext.getTenantId(); @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); @@ -73,7 +78,7 @@ void setUp() { @Test void testQueryAllApp() { List mockData = Arrays.asList(new App()); - when(appMapper.queryAllApp()).thenReturn(mockData); + when(appMapper.queryAllApp(TENANTID)).thenReturn(mockData); List result = appServiceImpl.queryAllApp(); Assertions.assertEquals(mockData, result); @@ -82,7 +87,7 @@ void testQueryAllApp() { @Test void testQueryAppById() { App app = new App(); - when(appMapper.queryAppById(1)).thenReturn(app); + when(appMapper.queryAppById(1, TENANTID)).thenReturn(app); Result result = appServiceImpl.queryAppById(1); Assertions.assertEquals(app, result.getData()); @@ -100,8 +105,8 @@ void testQueryAppByCondition() { @Test void testDeleteAppById() { App app = new App(); - when(appMapper.queryAppById(1)).thenReturn(app); - when(appMapper.deleteAppById(1)).thenReturn(2); + when(appMapper.queryAppById(1, TENANTID)).thenReturn(app); + when(appMapper.deleteAppById(1, TENANTID)).thenReturn(2); Result result = appServiceImpl.deleteAppById(1); Assertions.assertEquals(app, result.getData()); @@ -115,7 +120,7 @@ void testUpdateAppById() { resultApp.setExtendConfig(mockConfig); int appId = 1; - when(appMapper.queryAppById(appId)).thenReturn(resultApp); + when(appMapper.queryAppById(appId, TENANTID)).thenReturn(resultApp); App param = new App(); param.setId(appId); From 887f1b652275edc2616b5beaabd5bc257a315347 Mon Sep 17 00:00:00 2001 From: lu-yg <128358973+lu-yg@users.noreply.github.com> Date: Tue, 23 Dec 2025 19:13:26 -0800 Subject: [PATCH 2/6] fix: modify tenant API (#280) * fix: modify resource upload * fix: modify resource upload * fix: modify resource upload * fix: modify resource upload * fix: modify app API * fix: modify app API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API * fix: modify tenant API --- .../it/common/exception/ExceptionEnum.java | 8 +++++++- .../it/login/config/SSOInterceptor.java | 18 +++++++----------- .../context/DefaultLoginUserContext.java | 7 ++++++- .../it/login/controller/LoginController.java | 19 +++++++++++++++---- .../it/mapper/AuthUsersUnitsRolesMapper.java | 3 ++- .../tinyengine/it/model/entity/Tenant.java | 6 +++++- .../platform/impl/TenantServiceImpl.java | 9 +++++++++ .../it/common/handler/MockUserContext.java | 10 ++++++++++ .../handler/MyMetaObjectHandlerTest.java | 2 +- .../it/controller/UserControllerTest.java | 12 +++++++++++- .../service/app/impl/AppServiceImplTest.java | 18 +++++++++--------- .../service/app/impl/PageServiceImplTest.java | 12 +++++++----- .../service/app/impl/UserServiceImplTest.java | 12 +++--------- .../app/impl/v1/AppV1ServiceImplTest.java | 16 +++++++++++----- .../material/impl/BlockServiceImplTest.java | 5 +++-- .../platform/impl/TenantServiceImplTest.java | 9 --------- example-docker.yml | 2 -- 17 files changed, 106 insertions(+), 62 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/common/exception/ExceptionEnum.java b/base/src/main/java/com/tinyengine/it/common/exception/ExceptionEnum.java index 691324a8..606f179a 100644 --- a/base/src/main/java/com/tinyengine/it/common/exception/ExceptionEnum.java +++ b/base/src/main/java/com/tinyengine/it/common/exception/ExceptionEnum.java @@ -312,10 +312,16 @@ public enum ExceptionEnum implements IBaseError { * Cm 337 exception enum. */ CM337("CM337", "组织不存在"), + /** * Cm 338 exception enum. */ - CM338("CM338", "用户不存在,请重新注册"); + CM338("CM338", "用户不存在,请重新注册"), + + /** + * Cm 339 exception enum. + */ + CM339("CM339", "token检验失败,请重新登录"); /** * 错误码 diff --git a/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java b/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java index b138bb8f..4d27abf5 100644 --- a/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java +++ b/base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java @@ -12,6 +12,8 @@ package com.tinyengine.it.login.config; +import com.tinyengine.it.common.exception.ExceptionEnum; +import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.login.utils.JwtUtil; import com.tinyengine.it.login.config.context.DefaultLoginUserContext; import com.tinyengine.it.login.model.UserInfo; @@ -35,8 +37,6 @@ public class SSOInterceptor implements HandlerInterceptor { @Autowired private JwtUtil jwtUtil; - private static final String SSO_SERVER = System.getenv("SSO_SERVER"); - @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -44,9 +44,8 @@ public boolean preHandle(HttpServletRequest request, String authorization = request.getHeader("Authorization"); // 如果没有token,重定向到登录页 if (authorization == null || authorization.isEmpty()) { - log.info("No token, redirecting to: {}", SSO_SERVER); - response.sendRedirect(SSO_SERVER); - return false; + log.info("No token"); + throw new ServiceException(ExceptionEnum.CM336.getResultCode(), ExceptionEnum.CM336.getResultMsg()); } String token = jwtUtil.getTokenFromRequest(authorization); String requestURI = request.getRequestURI(); @@ -57,8 +56,7 @@ public boolean preHandle(HttpServletRequest request, // 验证token if (!jwtUtil.validateToken(token)) { log.warn("Token validation failed"); - response.sendRedirect(SSO_SERVER); - return false; + throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg()); } // 从token中获取用户信息 @@ -72,8 +70,7 @@ public boolean preHandle(HttpServletRequest request, // 检查必需的用户信息 if (username == null || username.isEmpty() || userId == null) { log.warn("User information is incomplete - username: {}, userId: {}", username, userId); - response.sendRedirect(SSO_SERVER); - return false; + throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg()); } // 存储用户信息到LoginUserContext @@ -90,9 +87,8 @@ public boolean preHandle(HttpServletRequest request, } catch (Exception e) { log.error("Token validation exception: {}", e.getMessage(), e); - response.sendRedirect(SSO_SERVER); DefaultLoginUserContext.clear(); - return false; + throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg()); } } diff --git a/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java b/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java index 25467e16..8ec5534a 100644 --- a/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java +++ b/base/src/main/java/com/tinyengine/it/login/config/context/DefaultLoginUserContext.java @@ -30,8 +30,13 @@ public String getTenantId() { if (tenantList == null || tenantList.isEmpty()) { return DEFAULT_TENANT; } + for (Tenant tenant : tenantList) { + if (tenant.getIsInUse()) { + return tenant.getId(); + } + } - return tenantList.get(0).getId(); + return DEFAULT_TENANT; } @Override diff --git a/base/src/main/java/com/tinyengine/it/login/controller/LoginController.java b/base/src/main/java/com/tinyengine/it/login/controller/LoginController.java index 7672caf1..495ac16f 100644 --- a/base/src/main/java/com/tinyengine/it/login/controller/LoginController.java +++ b/base/src/main/java/com/tinyengine/it/login/controller/LoginController.java @@ -239,18 +239,29 @@ public Result validateToken(@RequestParam String token) { @GetMapping("/user/tenant") public Result setTenant(@RequestParam Integer tenantId) { List tenants = loginUserContext.getTenants(); + if (tenantId == null) { + return Result.failed(ExceptionEnum.CM320); + } if (tenants == null || tenants.isEmpty()) { return Result.failed(ExceptionEnum.CM337); } - List currentTenant = new ArrayList<>(); + List tenantList = new ArrayList<>(); + boolean found = false; for (Tenant tenant : tenants) { if (tenant.getId().equals(tenantId.toString())) { - currentTenant.add(tenant); + tenant.setIsInUse(true); + found = true; + } else { + tenant.setIsInUse(false); } + + tenantList.add(tenant); } - if (currentTenant.isEmpty()) { + + if (!found) { return Result.failed(ExceptionEnum.CM337); } + // 通过 RequestContextHolder 获取请求 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) .getRequest(); @@ -259,7 +270,7 @@ public Result setTenant(@RequestParam Integer tenantId) { if (headerToken == null || headerToken.isEmpty()) { return Result.failed(ExceptionEnum.CM336); } - String token = jwtUtil.generateTokenWithSelectedTenant(headerToken, currentTenant); + String token = jwtUtil.generateTokenWithSelectedTenant(headerToken, tenantList); // 将原 token 加入黑名单 Claims claims = Jwts.parser() .verifyWith(JwtUtil.getSecretKey()) diff --git a/base/src/main/java/com/tinyengine/it/mapper/AuthUsersUnitsRolesMapper.java b/base/src/main/java/com/tinyengine/it/mapper/AuthUsersUnitsRolesMapper.java index 572e6558..e5a2b30f 100644 --- a/base/src/main/java/com/tinyengine/it/mapper/AuthUsersUnitsRolesMapper.java +++ b/base/src/main/java/com/tinyengine/it/mapper/AuthUsersUnitsRolesMapper.java @@ -1,5 +1,6 @@ package com.tinyengine.it.mapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.tinyengine.it.model.entity.AuthUsersUnitsRoles; import com.tinyengine.it.model.entity.Tenant; import org.apache.ibatis.annotations.Param; @@ -7,7 +8,7 @@ import java.util.List; -public interface AuthUsersUnitsRolesMapper { +public interface AuthUsersUnitsRolesMapper extends BaseMapper { /** * 查询表r_auth_users_units_roles所有信息 * diff --git a/base/src/main/java/com/tinyengine/it/model/entity/Tenant.java b/base/src/main/java/com/tinyengine/it/model/entity/Tenant.java index 59b13606..dfaae6c4 100644 --- a/base/src/main/java/com/tinyengine/it/model/entity/Tenant.java +++ b/base/src/main/java/com/tinyengine/it/model/entity/Tenant.java @@ -42,7 +42,7 @@ public class Tenant { @TableId(value = "id", type = IdType.AUTO) private String id; - @Schema(name = "nameCn", description = "组织唯一代码") + @Schema(name = "orgCode", description = "组织唯一代码") private String orgCode; @Schema(name = "nameCn", description = "组织中文名") @@ -54,6 +54,10 @@ public class Tenant { @Schema(name = "description", description = "组织描述") private String description; + @TableField(exist = false) + @Schema(name = "isInUse", description = "是否当前所在组织") + private Boolean isInUse; + @TableField(fill = FieldFill.INSERT) @Schema(name = "createdTime", description = "创建时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/base/src/main/java/com/tinyengine/it/service/platform/impl/TenantServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/platform/impl/TenantServiceImpl.java index 03b0ad6e..62ea9109 100644 --- a/base/src/main/java/com/tinyengine/it/service/platform/impl/TenantServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/platform/impl/TenantServiceImpl.java @@ -12,8 +12,11 @@ package com.tinyengine.it.service.platform.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.tinyengine.it.common.context.LoginUserContext; +import com.tinyengine.it.common.exception.ExceptionEnum; +import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.mapper.TenantMapper; import com.tinyengine.it.mapper.AuthUsersUnitsRolesMapper; import com.tinyengine.it.model.entity.AuthUsersUnitsRoles; @@ -110,6 +113,12 @@ public Integer updateTenantById(Tenant tenant) { */ @Override public Integer createTenant(Tenant tenant) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("name_cn", tenant.getNameCn()); + Tenant tenantResult = this.baseMapper.selectOne(queryWrapper); + if (tenantResult != null) { + throw new ServiceException(ExceptionEnum.CM003.getResultCode(), ExceptionEnum.CM003.getResultMsg()); + } int result = baseMapper.createTenant(tenant); if (result == 1) { AuthUsersUnitsRoles authUsersUnitsRoles = new AuthUsersUnitsRoles(); diff --git a/base/src/test/java/com/tinyengine/it/common/handler/MockUserContext.java b/base/src/test/java/com/tinyengine/it/common/handler/MockUserContext.java index 064b9591..1522012a 100644 --- a/base/src/test/java/com/tinyengine/it/common/handler/MockUserContext.java +++ b/base/src/test/java/com/tinyengine/it/common/handler/MockUserContext.java @@ -23,6 +23,16 @@ * @since 2025-04-14 */ public class MockUserContext implements LoginUserContext { + /** + * 返回当前用户所在的业务租户id + * + * @return 租户Id + */ + @Override + public String getTenantId() { + return "1"; + } + /** * 返回当前用户所诉的业务租户信息 * diff --git a/base/src/test/java/com/tinyengine/it/common/handler/MyMetaObjectHandlerTest.java b/base/src/test/java/com/tinyengine/it/common/handler/MyMetaObjectHandlerTest.java index a713172c..d398dcd3 100644 --- a/base/src/test/java/com/tinyengine/it/common/handler/MyMetaObjectHandlerTest.java +++ b/base/src/test/java/com/tinyengine/it/common/handler/MyMetaObjectHandlerTest.java @@ -36,7 +36,7 @@ void testInsertFill() throws NoSuchFieldException, IllegalAccessException { when(param.hasSetter("tenantId")).thenReturn(true); TestUtil.setPrivateValue(myMetaObjectHandler, "loginUserContext", new MockUserContext()); myMetaObjectHandler.insertFill(param); - verify(param, times(6)).hasSetter(anyString()); + verify(param, times(5)).hasSetter(anyString()); } @Test diff --git a/base/src/test/java/com/tinyengine/it/controller/UserControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/UserControllerTest.java index c35f5be2..0997a549 100644 --- a/base/src/test/java/com/tinyengine/it/controller/UserControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/UserControllerTest.java @@ -12,6 +12,7 @@ package com.tinyengine.it.controller; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @@ -19,6 +20,8 @@ import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.handler.MockUserContext; import com.tinyengine.it.common.utils.TestUtil; +import com.tinyengine.it.mapper.AuthUsersUnitsRolesMapper; +import com.tinyengine.it.model.entity.Tenant; import com.tinyengine.it.model.entity.User; import com.tinyengine.it.service.app.UserService; @@ -29,6 +32,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + /** * test case * @@ -42,7 +48,7 @@ class UserControllerTest { private UserController userController; @Mock - private LoginUserContext loginUserContext = new MockUserContext(); + private AuthUsersUnitsRolesMapper authUsersUnitsRolesMapper; @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException { @@ -53,7 +59,11 @@ void setUp() throws NoSuchFieldException, IllegalAccessException { void testMe() throws NoSuchFieldException, IllegalAccessException { TestUtil.setPrivateValue(userController, "loginUserContext", new MockUserContext()); User mockData = new User(); + Tenant tenant = new Tenant(); + List tenants = new ArrayList<>(); + tenants.add(tenant); when(userService.queryUserById(anyString())).thenReturn(mockData); + when(authUsersUnitsRolesMapper.queryAllTenantByUserId(anyInt())).thenReturn(tenants); Result result = userController.me(); Assertions.assertEquals(mockData, result.getData()); } diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java index 3087a176..0073e31c 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/AppServiceImplTest.java @@ -20,6 +20,8 @@ import cn.hutool.core.util.ReflectUtil; import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.context.LoginUserContext; +import com.tinyengine.it.common.handler.MockUserContext; +import com.tinyengine.it.common.utils.TestUtil; import com.tinyengine.it.mapper.AppMapper; import com.tinyengine.it.mapper.I18nEntryMapper; import com.tinyengine.it.model.dto.I18nEntryDto; @@ -65,20 +67,17 @@ class AppServiceImplTest { @InjectMocks private AppServiceImpl appServiceImpl; - @Mock - LoginUserContext loginUserContext; - - private String TENANTID = loginUserContext.getTenantId(); @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); ReflectUtil.setFieldValue(appServiceImpl, "baseMapper", appMapper); + ReflectUtil.setFieldValue(appServiceImpl, "loginUserContext", new MockUserContext()); } @Test void testQueryAllApp() { List mockData = Arrays.asList(new App()); - when(appMapper.queryAllApp(TENANTID)).thenReturn(mockData); + when(appMapper.queryAllApp("1")).thenReturn(mockData); List result = appServiceImpl.queryAllApp(); Assertions.assertEquals(mockData, result); @@ -87,7 +86,7 @@ void testQueryAllApp() { @Test void testQueryAppById() { App app = new App(); - when(appMapper.queryAppById(1, TENANTID)).thenReturn(app); + when(appMapper.queryAppById(1, "1")).thenReturn(app); Result result = appServiceImpl.queryAppById(1); Assertions.assertEquals(app, result.getData()); @@ -105,8 +104,8 @@ void testQueryAppByCondition() { @Test void testDeleteAppById() { App app = new App(); - when(appMapper.queryAppById(1, TENANTID)).thenReturn(app); - when(appMapper.deleteAppById(1, TENANTID)).thenReturn(2); + when(appMapper.queryAppById(1, "1")).thenReturn(app); + when(appMapper.deleteAppById(1, "1")).thenReturn(2); Result result = appServiceImpl.deleteAppById(1); Assertions.assertEquals(app, result.getData()); @@ -120,7 +119,7 @@ void testUpdateAppById() { resultApp.setExtendConfig(mockConfig); int appId = 1; - when(appMapper.queryAppById(appId, TENANTID)).thenReturn(resultApp); + when(appMapper.queryAppById(appId, "1")).thenReturn(resultApp); App param = new App(); param.setId(appId); @@ -139,6 +138,7 @@ void testUpdateAppById() { void testCreateApp() { List appResult = new ArrayList<>(); App param = new App(); + param.setTenantId("1"); when(appMapper.queryAppByCondition(param)).thenReturn(appResult); when(appMapper.createApp(param)).thenReturn(1); diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/PageServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/PageServiceImplTest.java index 73769274..b827db31 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/PageServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/PageServiceImplTest.java @@ -24,6 +24,7 @@ import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.enums.Enums; +import com.tinyengine.it.common.handler.MockUserContext; import com.tinyengine.it.mapper.AppExtensionMapper; import com.tinyengine.it.mapper.AppMapper; import com.tinyengine.it.mapper.BlockMapper; @@ -95,6 +96,7 @@ class PageServiceImplTest { void setUp() { MockitoAnnotations.openMocks(this); ReflectUtil.setFieldValue(pageServiceImpl, "baseMapper", pageMapper); + ReflectUtil.setFieldValue(pageServiceImpl, "loginUserContext", new MockUserContext()); } @Test @@ -111,7 +113,7 @@ void testQueryPageById() { app.setFramework("Vue"); app.setHomePage(1); // not home page - when(appMapper.queryAppById(333)).thenReturn(app); + when(appMapper.queryAppById(333, "1")).thenReturn(app); Page result = pageServiceImpl.queryPageById(1); assertEquals(returnPage, result); @@ -151,7 +153,7 @@ void testCreatePage() { app.setFramework("Vue"); app.setHomePage(1); // not home page - when(appMapper.queryAppById(333)).thenReturn(app); + when(appMapper.queryAppById(333, "1")).thenReturn(app); when(pageHistoryService.createPageHistory(any(PageHistory.class))).thenReturn(1); when(loginUserContext.getLoginUserId()).thenReturn("1"); @@ -186,7 +188,7 @@ void testCreateFolder() { app.setFramework("Vue"); app.setHomePage(1); // not home page - when(appMapper.queryAppById(333)).thenReturn(app); + when(appMapper.queryAppById(333, "1")).thenReturn(app); HashMap> blockAsset = new HashMap>() { { put("blockAsset", Arrays.asList("getBlockAssetsResponse")); @@ -222,7 +224,7 @@ void testUpdatePage() { app.setFramework("Vue"); app.setHomePage(1); // not home page - when(appMapper.queryAppById(222)).thenReturn(app); + when(appMapper.queryAppById(222, "1")).thenReturn(app); User occupier = new User(); occupier.setId("111"); when(userService.queryUserById("555")).thenReturn(occupier); @@ -245,7 +247,7 @@ void testUpdate() { App app = new App(); app.setFramework("Vue"); app.setHomePage(1); - when(appMapper.queryAppById(222)).thenReturn(app); + when(appMapper.queryAppById(222, "1")).thenReturn(app); Page parentInfo = new Page(); parentInfo.setDepth(4); parentInfo.setIsPage(false); diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/UserServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/UserServiceImplTest.java index 0d8f14c7..f156075c 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/UserServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/UserServiceImplTest.java @@ -16,6 +16,7 @@ import static org.mockito.Mockito.when; import cn.hutool.core.util.ReflectUtil; +import com.tinyengine.it.common.handler.MockUserContext; import com.tinyengine.it.login.service.impl.LoginServiceImpl; import com.tinyengine.it.mapper.UserMapper; import com.tinyengine.it.model.entity.User; @@ -27,6 +28,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,6 +51,7 @@ class UserServiceImplTest { void setUp() { MockitoAnnotations.openMocks(this); ReflectUtil.setFieldValue(userServiceImpl, "baseMapper", userMapper); + ReflectUtil.setFieldValue(loginServiceImpl, "baseMapper", userMapper); } @Test @@ -95,13 +98,4 @@ void testUpdateUserById() { Integer result = userServiceImpl.updateUserById(param); Assertions.assertEquals(1, result); } - - @Test - void testCreateUser() throws Exception { - User param = new User(); - when(userMapper.createUser(param)).thenReturn(1); - when(userMapper.queryUserById(any())).thenReturn(param); - User result = loginServiceImpl.createUser(param); - Assertions.assertEquals(param, result); - } } diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java index 02a1d11b..dc873278 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java @@ -17,7 +17,9 @@ import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.when; +import cn.hutool.core.util.ReflectUtil; import com.fasterxml.jackson.core.JsonProcessingException; +import com.tinyengine.it.common.handler.MockUserContext; import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.mapper.AppExtensionMapper; import com.tinyengine.it.mapper.AppMapper; @@ -73,9 +75,6 @@ class AppV1ServiceImplTest { @Mock private I18nEntryMapper i18nEntryMapper; - @Mock - private I18nEntryService i18nEntryService; - @Mock private AppExtensionMapper appExtensionMapper; @@ -98,12 +97,17 @@ class AppV1ServiceImplTest { private PlatformService platformService; @Mock private ComponentLibraryMapper componentLibraryMapper; + + @Mock + private I18nEntryService i18nEntryService; @InjectMocks private AppV1ServiceImpl appV1ServiceImpl; @BeforeEach void setUp() { + MockitoAnnotations.openMocks(this); + ReflectUtil.setFieldValue(appV1ServiceImpl, "loginUserContext", new MockUserContext()); } @Test @@ -119,7 +123,7 @@ void testAppSchema() throws JsonProcessingException { Map dataSourceGlobal = JsonUtils.MAPPER.readValue(json, Map.class); app.setDataSourceGlobal(dataSourceGlobal); - when(appMapper.queryAppById(anyInt())).thenReturn(app); + when(appMapper.queryAppById(anyInt(), anyString())).thenReturn(app); Page page = new Page(); page.setIsPage(true); page.setPageContent(new HashMap<>()); @@ -140,7 +144,9 @@ void testAppSchema() throws JsonProcessingException { when(platformService.queryPlatformById(any())).thenReturn(platform); List componentLibraryList = new ArrayList<>(); + SchemaI18n schemaI18n = new SchemaI18n(); when(componentLibraryMapper.queryAllComponentLibrary()).thenReturn(componentLibraryList); + when(i18nEntryService.formatEntriesList(any())).thenReturn(schemaI18n); SchemaDto result = appV1ServiceImpl.appSchema(appId); Assertions.assertEquals("2", result.getMeta().getAppId()); } @@ -155,7 +161,7 @@ void testMergeEntries() { void testGetMetaDto() { App app = new App(); app.setPlatformId(1); - when(appMapper.queryAppById(anyInt())).thenReturn(app); + when(appMapper.queryAppById(anyInt(), anyString())).thenReturn(app); when(i18nEntryMapper.findI18nEntriesByHostandHostType(anyInt(), anyString())).thenReturn( Arrays.asList(new I18nEntryDto())); when(appExtensionMapper.queryAppExtensionByCondition(any(AppExtension.class))).thenReturn( diff --git a/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java index a633c0dd..69bda56c 100644 --- a/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java @@ -80,6 +80,7 @@ class BlockServiceImplTest { void setUp() { MockitoAnnotations.openMocks(this); when(loginUserContext.getLoginUserId()).thenReturn("1"); + when(loginUserContext.getTenantId()).thenReturn("1"); ReflectUtil.setFieldValue(blockServiceImpl, "baseMapper", blockMapper); } @@ -290,7 +291,7 @@ void testListNewWhenGroupIdNotEmpty() { App app = new App(); app.setId(1); - when(appMapper.queryAppById(anyInt())).thenReturn(app); + when(appMapper.queryAppById(anyInt(), anyString())).thenReturn(app); Result> result = blockServiceImpl.listNew("1", "1"); Assertions.assertEquals(blocksList, result.getData()); @@ -307,7 +308,7 @@ void testListNewWhenGroupIdIsEmpty() { App app = new App(); app.setId(1); - when(appMapper.queryAppById(anyInt())).thenReturn(app); + when(appMapper.queryAppById(anyInt(), anyString())).thenReturn(app); HashMap param = new HashMap<>(); diff --git a/base/src/test/java/com/tinyengine/it/service/platform/impl/TenantServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/platform/impl/TenantServiceImplTest.java index bc170124..4600234d 100644 --- a/base/src/test/java/com/tinyengine/it/service/platform/impl/TenantServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/platform/impl/TenantServiceImplTest.java @@ -90,13 +90,4 @@ void testUpdateTenantById() { Integer result = tenantServiceImpl.updateTenantById(param); Assertions.assertEquals(2, result); } - - @Test - void testCreateTenant() { - Tenant param = new Tenant(); - when(tenantMapper.createTenant(param)).thenReturn(2); - - Integer result = tenantServiceImpl.createTenant(param); - Assertions.assertEquals(2, result); - } } \ No newline at end of file diff --git a/example-docker.yml b/example-docker.yml index 32df45fd..e9108321 100644 --- a/example-docker.yml +++ b/example-docker.yml @@ -19,8 +19,6 @@ COPY --from=build /app/all-MiniLM-L6-v2 /app/all-MiniLM-L6-v2 # 设置环境变量 ENV FOLDER_PATH="/app/documents" -# 登录页面路径 -ENV SSO_SERVER="" #资源下载接口路径 ENV TINY_ENGINE_URL="" ENTRYPOINT ["java", "-jar", "tiny-engine-app.jar", "--spring.profiles.active=alpha"] From bbc21deae26bff1798f5a583917c42056d30e980 Mon Sep 17 00:00:00 2001 From: msslulu <1484036491@qq.com> Date: Wed, 24 Dec 2025 22:17:44 -0800 Subject: [PATCH 3/6] feat:add AIChat endpoint --- .../it/controller/AiChatController.java | 82 +++++++++++++------ 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index e0086c78..538f1264 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -1,13 +1,12 @@ /** * Copyright (c) 2023 - present TinyEngine Authors. * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. - * + *

* Use of this source code is governed by an MIT-style license. - * + *

* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. - * */ package com.tinyengine.it.controller; @@ -18,6 +17,9 @@ import com.tinyengine.it.model.dto.AiToken; import com.tinyengine.it.model.dto.ChatRequest; +import com.tinyengine.it.rag.entity.EmbeddingMatchDto; +import com.tinyengine.it.rag.entity.SearchRequest; +import com.tinyengine.it.rag.service.StorageService; import com.tinyengine.it.service.app.v1.AiChatV1Service; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -37,6 +39,8 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import java.util.List; + /** * The type Ai chat controller. * @@ -52,6 +56,11 @@ public class AiChatController { */ @Autowired private AiChatV1Service aiChatV1Service; + /** + * vector storage service + */ + @Autowired + private StorageService vectorStorageService; /** * AI api @@ -60,17 +69,17 @@ public class AiChatController { * @return ai回答信息 result */ @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", - parameters = { - @Parameter(name = "ChatRequest", description = "入参对象") - }, responses = { + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI chat") @PostMapping("/ai/chat") public ResponseEntity aiChat(@RequestBody ChatRequest request, - @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { + @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { if (authorization != null && authorization.startsWith("Bearer ")) { String token = authorization.replace("Bearer ", ""); @@ -81,16 +90,36 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, if (request.isStream()) { return ResponseEntity.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .header("Cache-Control", "no-cache") - .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 - .body((StreamingResponseBody) response); + .contentType(MediaType.TEXT_EVENT_STREAM) + .header("Cache-Control", "no-cache") + .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 + .body((StreamingResponseBody) response); } else { return ResponseEntity.ok(response); } } + /** + * search in collection + * + * @param searchDto the searchDto + * @return result + */ + @Operation(summary = "在指定集合中搜索", description = "在指定集合中搜索", + parameters = { + @Parameter(name = "searchDto", description = "搜索请求参数体"), + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "AI search in collection") + @PostMapping("/ai/search") + public Result> searchInCollection(@RequestBody SearchRequest searchDto) { + List results = vectorStorageService.search(searchDto); + return Result.success(results); + } /** * AI api v1 @@ -99,17 +128,17 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, * @return ai回答信息 result */ @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", - parameters = { - @Parameter(name = "ChatRequest", description = "入参对象") - }, responses = { + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI completions") @PostMapping("/chat/completions") public ResponseEntity completions(@RequestBody ChatRequest request, - @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { + @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { if (authorization != null && authorization.startsWith("Bearer ")) { String token = authorization.replace("Bearer ", ""); request.setApiKey(token); @@ -119,14 +148,15 @@ public ResponseEntity completions(@RequestBody ChatRequest request, if (request.isStream()) { return ResponseEntity.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .header("Cache-Control", "no-cache") - .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 - .body((StreamingResponseBody) response); + .contentType(MediaType.TEXT_EVENT_STREAM) + .header("Cache-Control", "no-cache") + .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 + .body((StreamingResponseBody) response); } else { return ResponseEntity.ok(response); } } + /** * get token * @@ -134,18 +164,18 @@ public ResponseEntity completions(@RequestBody ChatRequest request, * @return ai回答信息 result */ @Operation(summary = "获取加密key信息", description = "获取加密key信息", - parameters = { - @Parameter(name = "request", description = "入参对象") - }, responses = { + parameters = { + @Parameter(name = "request", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "get token") @PostMapping("/encrypt-key") public Result getToken(@RequestBody ChatRequest request) throws Exception { String apiKey = request.getApiKey(); - if(apiKey == null || apiKey.isEmpty()) { + if (apiKey == null || apiKey.isEmpty()) { return Result.failed(ExceptionEnum.CM320); } String token = aiChatV1Service.getToken(apiKey); From bd750f8c945ff33f5376391b5a8b38fc7884fdc3 Mon Sep 17 00:00:00 2001 From: msslulu <1484036491@qq.com> Date: Thu, 25 Dec 2025 19:59:40 -0800 Subject: [PATCH 4/6] feat:add AIChat endpoint --- .../it/controller/AiChatController.java | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index 538f1264..a9ed590e 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -1,12 +1,13 @@ /** * Copyright (c) 2023 - present TinyEngine Authors. * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. - *

+ * * Use of this source code is governed by an MIT-style license. - *

+ * * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * */ package com.tinyengine.it.controller; @@ -56,9 +57,7 @@ public class AiChatController { */ @Autowired private AiChatV1Service aiChatV1Service; - /** - * vector storage service - */ + @Autowired private StorageService vectorStorageService; @@ -100,26 +99,6 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, } - /** - * search in collection - * - * @param searchDto the searchDto - * @return result - */ - @Operation(summary = "在指定集合中搜索", description = "在指定集合中搜索", - parameters = { - @Parameter(name = "searchDto", description = "搜索请求参数体"), - }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") - }) - @SystemControllerLog(description = "AI search in collection") - @PostMapping("/ai/search") - public Result> searchInCollection(@RequestBody SearchRequest searchDto) { - List results = vectorStorageService.search(searchDto); - return Result.success(results); - } /** * AI api v1 @@ -156,7 +135,6 @@ public ResponseEntity completions(@RequestBody ChatRequest request, return ResponseEntity.ok(response); } } - /** * get token * @@ -175,10 +153,31 @@ public ResponseEntity completions(@RequestBody ChatRequest request, @PostMapping("/encrypt-key") public Result getToken(@RequestBody ChatRequest request) throws Exception { String apiKey = request.getApiKey(); - if (apiKey == null || apiKey.isEmpty()) { + if(apiKey == null || apiKey.isEmpty()) { return Result.failed(ExceptionEnum.CM320); } String token = aiChatV1Service.getToken(apiKey); return Result.success(new AiToken(token)); } + + /** + * search in collection + * + * @param searchDto the searchDto + * @return result + */ + @Operation(summary = "在指定集合中搜索", description = "在指定集合中搜索", + parameters = { + @Parameter(name = "searchDto", description = "搜索请求参数体"), + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "AI search in collection") + @PostMapping("/ai/search") + public Result> searchInCollection(@RequestBody SearchRequest searchDto) { + List results = vectorStorageService.search(searchDto); + return Result.success(results); + } } From bd4167bf9e9e7110e001b05c0e6dcfc079c0040e Mon Sep 17 00:00:00 2001 From: msslulu <1484036491@qq.com> Date: Thu, 25 Dec 2025 22:36:35 -0800 Subject: [PATCH 5/6] feat:AiChatController optimize code formatting --- .../it/controller/AiChatController.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index a9ed590e..0c4296dd 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -68,17 +68,17 @@ public class AiChatController { * @return ai回答信息 result */ @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", - parameters = { - @Parameter(name = "ChatRequest", description = "入参对象") - }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI chat") @PostMapping("/ai/chat") public ResponseEntity aiChat(@RequestBody ChatRequest request, - @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { + @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { if (authorization != null && authorization.startsWith("Bearer ")) { String token = authorization.replace("Bearer ", ""); @@ -89,10 +89,10 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, if (request.isStream()) { return ResponseEntity.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .header("Cache-Control", "no-cache") - .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 - .body((StreamingResponseBody) response); + .contentType(MediaType.TEXT_EVENT_STREAM) + .header("Cache-Control", "no-cache") + .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 + .body((StreamingResponseBody) response); } else { return ResponseEntity.ok(response); } @@ -107,17 +107,17 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, * @return ai回答信息 result */ @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", - parameters = { - @Parameter(name = "ChatRequest", description = "入参对象") - }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI completions") @PostMapping("/chat/completions") public ResponseEntity completions(@RequestBody ChatRequest request, - @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { + @RequestHeader(value = "Authorization", required = false) String authorization) throws Exception { if (authorization != null && authorization.startsWith("Bearer ")) { String token = authorization.replace("Bearer ", ""); request.setApiKey(token); @@ -127,10 +127,10 @@ public ResponseEntity completions(@RequestBody ChatRequest request, if (request.isStream()) { return ResponseEntity.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .header("Cache-Control", "no-cache") - .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 - .body((StreamingResponseBody) response); + .contentType(MediaType.TEXT_EVENT_STREAM) + .header("Cache-Control", "no-cache") + .header("X-Accel-Buffering", "no") // 禁用Nginx缓冲 + .body((StreamingResponseBody) response); } else { return ResponseEntity.ok(response); } @@ -142,12 +142,12 @@ public ResponseEntity completions(@RequestBody ChatRequest request, * @return ai回答信息 result */ @Operation(summary = "获取加密key信息", description = "获取加密key信息", - parameters = { - @Parameter(name = "request", description = "入参对象") - }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + parameters = { + @Parameter(name = "request", description = "入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "get token") @PostMapping("/encrypt-key") @@ -167,12 +167,12 @@ public Result getToken(@RequestBody ChatRequest request) throws Excepti * @return result */ @Operation(summary = "在指定集合中搜索", description = "在指定集合中搜索", - parameters = { - @Parameter(name = "searchDto", description = "搜索请求参数体"), - }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + parameters = { + @Parameter(name = "searchDto", description = "搜索请求参数体"), + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI search in collection") @PostMapping("/ai/search") From b6a37dbee7d0903dd8e733f38ef5e518390503d0 Mon Sep 17 00:00:00 2001 From: msslulu <1484036491@qq.com> Date: Thu, 25 Dec 2025 22:42:19 -0800 Subject: [PATCH 6/6] feat:AiChatController optimize code formatting --- .../it/controller/AiChatController.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index 0c4296dd..ff129fa0 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -71,9 +71,9 @@ public class AiChatController { parameters = { @Parameter(name = "ChatRequest", description = "入参对象") }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI chat") @PostMapping("/ai/chat") @@ -110,9 +110,9 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request, parameters = { @Parameter(name = "ChatRequest", description = "入参对象") }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI completions") @PostMapping("/chat/completions") @@ -145,9 +145,9 @@ public ResponseEntity completions(@RequestBody ChatRequest request, parameters = { @Parameter(name = "request", description = "入参对象") }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "get token") @PostMapping("/encrypt-key") @@ -170,9 +170,9 @@ public Result getToken(@RequestBody ChatRequest request) throws Excepti parameters = { @Parameter(name = "searchDto", description = "搜索请求参数体"), }, responses = { - @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败") + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI search in collection") @PostMapping("/ai/search")