From e7fec1375e57d8b98e5478f998bdeb28aed48140 Mon Sep 17 00:00:00 2001 From: wsm <2746563060@qq.com> Date: Tue, 14 Jul 2026 16:12:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(openapi):=20=E6=96=B0=E5=A2=9E=E5=AF=B9?= =?UTF-8?q?=E5=A4=96=E6=8E=A5=E5=8F=A3=E6=A8=A1=E5=9D=97=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E9=97=A8=E6=88=B7=E5=BE=85=E5=8A=9E=E6=95=B0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=8E=A5=E5=8F=A3=20/openapi/countTaskforporter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 jeecg-system-openapi 模块,与 local-api/cloud-api 同级,作为对外能力聚合入口。 接口通过 shiro.excludeUrls 白名单放行,鉴权与业务实现完全解耦。 Co-Authored-By: Claude Opus 4.7 --- .../jeecg-system-openapi/pom.xml | 25 +++++++ .../openapi/controller/TaskApiController.java | 46 +++++++++++++ .../jeecg/openapi/mapper/TaskApiMapper.java | 30 +++++++++ .../org/jeecg/openapi/service/ITaskApi.java | 17 +++++ .../openapi/service/impl/TaskApiImpl.java | 25 +++++++ .../controller/TaskApiControllerTest.java | 65 +++++++++++++++++++ .../openapi/service/impl/TaskApiImplTest.java | 35 ++++++++++ jeecg-module-system/jeecg-system-api/pom.xml | 1 + .../jeecg-system-start/pom.xml | 7 +- .../src/main/resources/application-prod.yml | 2 +- .../src/main/resources/application-test.yml | 2 +- pom.xml | 6 ++ 12 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/pom.xml create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/controller/TaskApiController.java create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/mapper/TaskApiMapper.java create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/ITaskApi.java create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/impl/TaskApiImpl.java create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/controller/TaskApiControllerTest.java create mode 100644 jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/service/impl/TaskApiImplTest.java diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/pom.xml b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/pom.xml new file mode 100644 index 0000000..f9e5bc6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/pom.xml @@ -0,0 +1,25 @@ + + + + jeecg-system-api + org.jeecgframework.boot + ${jeecgProjectVersion} + + 4.0.0 + + jeecg-system-openapi + + + + + org.jeecgframework.boot + jeecg-system-local-api + + + + diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/controller/TaskApiController.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/controller/TaskApiController.java new file mode 100644 index 0000000..8b739f7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/controller/TaskApiController.java @@ -0,0 +1,46 @@ +package org.jeecg.openapi.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.openapi.service.ITaskApi; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +/** + * 对外接口 - 任务/待办 + *

+ * 统一前缀 /openapi/*,认证(签名)与放行由框架层按路径统一处理,本类不含任何鉴权代码。 + */ +@Tag(name = "对外接口-任务") +@RestController +@RequestMapping("/openapi") +@Slf4j +public class TaskApiController { + + @Resource + private ITaskApi taskApi; + + /** + * 门户查询待办数 + *

调用方(门户)通过 username 参数传入 sys_user.username(登录账号)。 + */ + @Operation(summary = "门户查询待办数", description = "门户查询待办数") + @GetMapping("/countTaskforporter") + public Result countTaskforporter(HttpServletRequest request) { + String username = request.getParameter("username"); + if (oConvertUtils.isEmpty(username)) { + log.warn("【对外接口-门户待办数】未传入用户账号,无法统计, 参数名: username"); + return Result.error("参数 username 不能为空"); + } + Long count = taskApi.countTodoByUsername(username); + log.info("【对外接口-门户待办数】统计完成, username: {}, 待办数: {}", username, count); + return Result.OK(count); + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/mapper/TaskApiMapper.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/mapper/TaskApiMapper.java new file mode 100644 index 0000000..aa9e37d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/mapper/TaskApiMapper.java @@ -0,0 +1,30 @@ +package org.jeecg.openapi.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 对外接口 - 任务/待办 通用查询(直查 Flowable 运行时表,不依赖任何业务模块)。 + */ +@Mapper +public interface TaskApiMapper { + + /** + * 用户待办任务数(直接分配 + 候选人/候选组),仅统计未挂起任务。 + * 与 ActivitiServiceImpl#findBaseTodoTasks 的待办口径一致。 + * + * @param username 登录账号,对应 ACT_RU_TASK.ASSIGNEE_ + */ + @Select("SELECT COUNT(*) FROM (" + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "INNER JOIN ACT_RU_IDENTITYLINK I ON I.TASK_ID_ = RES.ID_ " + + "WHERE RES.ASSIGNEE_ IS NULL AND I.TYPE_ = 'candidate' " + + "AND (I.USER_ID_ = #{username} OR I.GROUP_ID_ IN (SELECT g.GROUP_ID_ FROM ACT_ID_MEMBERSHIP g WHERE g.USER_ID_ = #{username})) " + + "AND RES.SUSPENSION_STATE_ = 1) " + + "UNION " + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "WHERE RES.ASSIGNEE_ = #{username} AND RES.SUSPENSION_STATE_ = 1)" + + ") T") + Long countTodo(@Param("username") String username); +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/ITaskApi.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/ITaskApi.java new file mode 100644 index 0000000..db9bceb --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/ITaskApi.java @@ -0,0 +1,17 @@ +package org.jeecg.openapi.service; + +/** + * 对外接口 - 任务/待办能力契约 + * + * @author openapi + */ +public interface ITaskApi { + + /** + * 统计某用户的待办任务数(Flowable:直接分配 + 候选人/候选组) + * + * @param username sys_user.username(登录账号,对应 Flowable ACT_RU_TASK.ASSIGNEE_) + * @return 待办数,无数据返回 0 + */ + Long countTodoByUsername(String username); +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/impl/TaskApiImpl.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/impl/TaskApiImpl.java new file mode 100644 index 0000000..ef13878 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/main/java/org/jeecg/openapi/service/impl/TaskApiImpl.java @@ -0,0 +1,25 @@ +package org.jeecg.openapi.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.openapi.mapper.TaskApiMapper; +import org.jeecg.openapi.service.ITaskApi; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * {@link ITaskApi} 的实现。通用能力,直查 Flowable 表,不依赖业务模块。 + */ +@Service +@Slf4j +public class TaskApiImpl implements ITaskApi { + + @Resource + private TaskApiMapper taskApiMapper; + + @Override + public Long countTodoByUsername(String username) { + Long count = taskApiMapper.countTodo(username); + return count == null ? 0L : count; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/controller/TaskApiControllerTest.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/controller/TaskApiControllerTest.java new file mode 100644 index 0000000..90c8419 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/controller/TaskApiControllerTest.java @@ -0,0 +1,65 @@ +package org.jeecg.openapi.controller; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.servlet.http.HttpServletRequest; + +import org.jeecg.common.api.vo.Result; +import org.jeecg.openapi.service.ITaskApi; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class TaskApiControllerTest { + + @Mock + private ITaskApi taskApi; + + @Mock + private HttpServletRequest request; + + @InjectMocks + private TaskApiController controller; + + @Test + void 传入用户账号时应返回待办数() { + when(request.getParameter("username")).thenReturn("admin"); + when(taskApi.countTodoByUsername("admin")).thenReturn(3L); + + Result result = controller.countTaskforporter(request); + + assertTrue(result.isSuccess()); + assertEquals(3L, result.getResult()); + verify(taskApi).countTodoByUsername("admin"); + } + + @Test + void 用户账号为空时应返回参数错误且不查询() { + when(request.getParameter("username")).thenReturn(null); + + Result result = controller.countTaskforporter(request); + + assertFalse(result.isSuccess()); + assertEquals("参数 username 不能为空", result.getMessage()); + verify(taskApi, never()).countTodoByUsername(any()); + } + + @Test + void 用户账号为空串时应返回参数错误且不查询() { + when(request.getParameter("username")).thenReturn(""); + + Result result = controller.countTaskforporter(request); + + assertFalse(result.isSuccess()); + verify(taskApi, never()).countTodoByUsername(any()); + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/service/impl/TaskApiImplTest.java b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/service/impl/TaskApiImplTest.java new file mode 100644 index 0000000..1586d92 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-openapi/src/test/java/org/jeecg/openapi/service/impl/TaskApiImplTest.java @@ -0,0 +1,35 @@ +package org.jeecg.openapi.service.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import org.jeecg.openapi.mapper.TaskApiMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class TaskApiImplTest { + + @Mock + private TaskApiMapper taskApiMapper; + + @InjectMocks + private TaskApiImpl taskApi; + + @Test + void 应返回Mapper查询到的待办数() { + when(taskApiMapper.countTodo("admin")).thenReturn(5L); + + assertEquals(5L, taskApi.countTodoByUsername("admin")); + } + + @Test + void Mapper返回null时应归零() { + when(taskApiMapper.countTodo("nobody")).thenReturn(null); + + assertEquals(0L, taskApi.countTodoByUsername("nobody")); + } +} diff --git a/jeecg-module-system/jeecg-system-api/pom.xml b/jeecg-module-system/jeecg-system-api/pom.xml index 0be72d5..c6c72e0 100644 --- a/jeecg-module-system/jeecg-system-api/pom.xml +++ b/jeecg-module-system/jeecg-system-api/pom.xml @@ -15,6 +15,7 @@ jeecg-system-local-api jeecg-system-cloud-api + jeecg-system-openapi diff --git a/jeecg-module-system/jeecg-system-start/pom.xml b/jeecg-module-system/jeecg-system-start/pom.xml index 58c1834..21d84d9 100644 --- a/jeecg-module-system/jeecg-system-start/pom.xml +++ b/jeecg-module-system/jeecg-system-start/pom.xml @@ -31,7 +31,12 @@ org.jeecgframework.boot jeecg-module-flow - + + + org.jeecgframework.boot + jeecg-system-openapi + + org.flywaydb diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml index 98e4d16..2c930ce 100644 --- a/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml @@ -251,7 +251,7 @@ jeecg: #webapp文件路径 webapp: /opt/jeecg-boot/webapp shiro: - excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/bigscreen/category/**,/bigscreen/visual/**,/bigscreen/map/**,/jmreport/bigscreen2/**,/api/getUserInfo + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/bigscreen/category/**,/bigscreen/visual/**,/bigscreen/map/**,/jmreport/bigscreen2/**,/api/getUserInfo,/openapi/* #阿里云oss存储和大鱼短信秘钥配置 oss: accessKey: ?? diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml index b700a3d..a40b0d7 100644 --- a/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml @@ -250,7 +250,7 @@ jeecg: #webapp文件路径 webapp: D://opt//webapp shiro: - excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/bigscreen/category/**,/bigscreen/visual/**,/bigscreen/map/**,/jmreport/bigscreen2/** + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/bigscreen/category/**,/bigscreen/visual/**,/bigscreen/map/**,/jmreport/bigscreen2/**,/openapi/* #阿里云oss存储和大鱼短信秘钥配置 oss: accessKey: ?? diff --git a/pom.xml b/pom.xml index 5357266..27311de 100644 --- a/pom.xml +++ b/pom.xml @@ -267,6 +267,12 @@ jeecg-system-cloud-api ${jeecgProjectVersion} + + + org.jeecgframework.boot + jeecg-system-openapi + ${jeecgProjectVersion} +