feat(openapi): 新增对外接口模块,提供门户待办数查询接口 /openapi/countTaskforporter
新增 jeecg-system-openapi 模块,与 local-api/cloud-api 同级,作为对外能力聚合入口。 接口通过 shiro.excludeUrls 白名单放行,鉴权与业务实现完全解耦。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>jeecg-system-api</artifactId>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<version>${jeecgProjectVersion}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>jeecg-system-openapi</artifactId>
|
||||||
|
|
||||||
|
<!-- 对外接口模块:HTTP 入口 + 能力实现,统一 /openapi/* 前缀。
|
||||||
|
base-core 由父模块 jeecg-system-api 继承;额外依赖 local-api,
|
||||||
|
便于后续注入 ISysBaseAPI 等接口做跨模块调用。
|
||||||
|
认证/放行由 base-core 的签名拦截器 + Shiro 按路径统一处理,本模块零鉴权代码。 -->
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<artifactId>jeecg-system-local-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
+46
@@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对外接口 - 任务/待办
|
||||||
|
* <p>
|
||||||
|
* 统一前缀 /openapi/*,认证(签名)与放行由框架层按路径统一处理,本类不含任何鉴权代码。
|
||||||
|
*/
|
||||||
|
@Tag(name = "对外接口-任务")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/openapi")
|
||||||
|
@Slf4j
|
||||||
|
public class TaskApiController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ITaskApi taskApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门户查询待办数
|
||||||
|
* <p>调用方(门户)通过 username 参数传入 sys_user.username(登录账号)。
|
||||||
|
*/
|
||||||
|
@Operation(summary = "门户查询待办数", description = "门户查询待办数")
|
||||||
|
@GetMapping("/countTaskforporter")
|
||||||
|
public Result<Long> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -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);
|
||||||
|
}
|
||||||
+17
@@ -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);
|
||||||
|
}
|
||||||
+25
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -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<Long> result = controller.countTaskforporter(request);
|
||||||
|
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals(3L, result.getResult());
|
||||||
|
verify(taskApi).countTodoByUsername("admin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void 用户账号为空时应返回参数错误且不查询() {
|
||||||
|
when(request.getParameter("username")).thenReturn(null);
|
||||||
|
|
||||||
|
Result<Long> result = controller.countTaskforporter(request);
|
||||||
|
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals("参数 username 不能为空", result.getMessage());
|
||||||
|
verify(taskApi, never()).countTodoByUsername(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void 用户账号为空串时应返回参数错误且不查询() {
|
||||||
|
when(request.getParameter("username")).thenReturn("");
|
||||||
|
|
||||||
|
Result<Long> result = controller.countTaskforporter(request);
|
||||||
|
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
verify(taskApi, never()).countTodoByUsername(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>jeecg-system-local-api</module>
|
<module>jeecg-system-local-api</module>
|
||||||
<module>jeecg-system-cloud-api</module>
|
<module>jeecg-system-cloud-api</module>
|
||||||
|
<module>jeecg-system-openapi</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -31,7 +31,12 @@
|
|||||||
<groupId>org.jeecgframework.boot</groupId>
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
<artifactId>jeecg-module-flow</artifactId>
|
<artifactId>jeecg-module-flow</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 对外接口模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<artifactId>jeecg-system-openapi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- flyway 数据库自动升级 -->
|
<!-- flyway 数据库自动升级 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.flywaydb</groupId>
|
<groupId>org.flywaydb</groupId>
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ jeecg:
|
|||||||
#webapp文件路径
|
#webapp文件路径
|
||||||
webapp: /opt/jeecg-boot/webapp
|
webapp: /opt/jeecg-boot/webapp
|
||||||
shiro:
|
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存储和大鱼短信秘钥配置
|
||||||
oss:
|
oss:
|
||||||
accessKey: ??
|
accessKey: ??
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ jeecg:
|
|||||||
#webapp文件路径
|
#webapp文件路径
|
||||||
webapp: D://opt//webapp
|
webapp: D://opt//webapp
|
||||||
shiro:
|
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存储和大鱼短信秘钥配置
|
||||||
oss:
|
oss:
|
||||||
accessKey: ??
|
accessKey: ??
|
||||||
|
|||||||
@@ -267,6 +267,12 @@
|
|||||||
<artifactId>jeecg-system-cloud-api</artifactId>
|
<artifactId>jeecg-system-cloud-api</artifactId>
|
||||||
<version>${jeecgProjectVersion}</version>
|
<version>${jeecgProjectVersion}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- system 对外接口 openapi -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<artifactId>jeecg-system-openapi</artifactId>
|
||||||
|
<version>${jeecgProjectVersion}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 流程引擎 api -->
|
<!-- 流程引擎 api -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
Reference in New Issue
Block a user