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>
|
||||
<module>jeecg-system-local-api</module>
|
||||
<module>jeecg-system-cloud-api</module>
|
||||
<module>jeecg-system-openapi</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -31,7 +31,12 @@
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-module-flow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 对外接口模块 -->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-system-openapi</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- flyway 数据库自动升级 -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
|
||||
@@ -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: ??
|
||||
|
||||
@@ -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: ??
|
||||
|
||||
@@ -267,6 +267,12 @@
|
||||
<artifactId>jeecg-system-cloud-api</artifactId>
|
||||
<version>${jeecgProjectVersion}</version>
|
||||
</dependency>
|
||||
<!-- system 对外接口 openapi -->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-system-openapi</artifactId>
|
||||
<version>${jeecgProjectVersion}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 流程引擎 api -->
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user