feat(flow): 用户面四查询(todo/done/cc/my)支持 appKey 按应用筛选
build / build-push (push) Successful in 48s

工作台四页签加「按应用筛选」:controller 收可选 appKey,经 FlowQueryService
透传到分页查询;口径=办件定义归属 flw_def.app_key(join flw_instance→flw_def,
均多对一,不影响已办「一个办件一行」的去重与分页)。appKey 空串按不过滤处理
(emptyToNull),避免清筛选后误成空列表。FlwCcRepository 抄送派生查询相应改
@Query 以支持 join 过滤;FlowEngineIT 两处调用补 null 实参。

门禁:mvn compile/test-compile ✓ + 非 DB 单测 44 例 ✓;本机无 MySQL(3306
未监听),新 JPQL 未在真实库执行,FlowEngineIT/OapiFlowContractIT 待有库环境补跑
This commit is contained in:
chenxing
2026-10-12 11:26:11 +08:00
parent b4856a480d
commit c896fbb90b
6 changed files with 51 additions and 25 deletions
@@ -45,33 +45,37 @@ public class FlowUserController {
@GetMapping("/my")
public BasePageResult<FlowModels.InstanceItem> my(BoaUser user,
@RequestParam(value = "status", required = false) Integer status,
@RequestParam(value = "appKey", required = false) String appKey,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size) {
return queryService.pageMy(requireSchId(user), user.getId(), status, page, size);
return queryService.pageMy(requireSchId(user), user.getId(), status, appKey, page, size);
}
/** 我的待办(主路径,走 assignee 索引) */
@GetMapping("/todo")
public BasePageResult<FlowModels.TaskItem> todo(BoaUser user,
@RequestParam(value = "appKey", required = false) String appKey,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size) {
return queryService.pageTodo(requireSchId(user), user.getId(), page, size);
return queryService.pageTodo(requireSchId(user), user.getId(), appKey, page, size);
}
/** 我的已办 */
@GetMapping("/done")
public BasePageResult<FlowModels.TaskItem> done(BoaUser user,
@RequestParam(value = "appKey", required = false) String appKey,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size) {
return queryService.pageDone(requireSchId(user), user.getId(), page, size);
return queryService.pageDone(requireSchId(user), user.getId(), appKey, page, size);
}
/** 我的抄送(NOTIFY 节点落库;知会不占待办,独立页签) */
@GetMapping("/cc/my")
public BasePageResult<FlowModels.CcItem> ccMy(BoaUser user,
@RequestParam(value = "appKey", required = false) String appKey,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size) {
return queryService.pageCc(requireSchId(user), user.getId(), page, size);
return queryService.pageCc(requireSchId(user), user.getId(), appKey, page, size);
}
/** 办件详情(提交人/办理人/本校管理员可见) */
@@ -4,13 +4,19 @@ import com.yunhai.flowcenter.entity.FlwCc;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface FlwCcRepository extends JpaRepository<FlwCc, Long> {
/** 抄送我的(用户面页签,走 user+sent_time 索引) */
Page<FlwCc> findBySchIdAndUserIdOrderBySentTimeDesc(Long schId, Integer userId, Pageable pageable);
/** 抄送我的(用户面页签,走 user+sent_time 索引;appKey 可空=不按应用过滤,口径=定义归属 flw_def.app_key) */
@Query("select c from FlwCc c join FlwInstance i on i.id = c.instanceId join FlwDef d on d.id = i.defId "
+ "where c.schId = :schId and c.userId = :userId "
+ "and (:appKey is null or d.appKey = :appKey) order by c.sentTime desc")
Page<FlwCc> pageCc(@Param("schId") Long schId, @Param("userId") Integer userId,
@Param("appKey") String appKey, Pageable pageable);
/** 办件轨迹/进度留痕 */
List<FlwCc> findByInstanceIdOrderByIdAsc(Long instanceId);
@@ -17,11 +17,14 @@ public interface FlwInstanceRepository extends JpaRepository<FlwInstance, Long>
List<FlwInstance> findBySchIdAndSubmitterIdAndStatusOrderByIdDesc(Long schId, Integer submitterId, Integer status);
/** 我提交的办件(分页,状态可空) */
@Query("select i from FlwInstance i where i.schId = :schId and i.submitterId = :submitterId "
+ "and (:status is null or i.status = :status) order by i.id desc")
/** 我提交的办件(分页,状态/应用可空;应用口径=定义归属 flw_def.app_key) */
@Query("select i from FlwInstance i join FlwDef d on d.id = i.defId "
+ "where i.schId = :schId and i.submitterId = :submitterId "
+ "and (:status is null or i.status = :status) "
+ "and (:appKey is null or d.appKey = :appKey) order by i.id desc")
Page<FlwInstance> pageMy(@Param("schId") Long schId, @Param("submitterId") Integer submitterId,
@Param("status") Integer status, Pageable pageable);
@Param("status") Integer status, @Param("appKey") String appKey,
Pageable pageable);
/** 管理端办件分页(状态/关键字可空) */
@Query("select i from FlwInstance i where i.schId = :schId "
@@ -52,10 +52,13 @@ public interface FlwTaskRepository extends JpaRepository<FlwTask, Long> {
@Query("update FlwTask t set t.status = 3 where t.instanceId = :instanceId and t.status = 0 and t.id <> :exceptTaskId")
int invalidatePendingExcept(@Param("instanceId") Long instanceId, @Param("exceptTaskId") Long exceptTaskId);
/** 我的待办(主路径,走 assignee 索引) */
@Query("select t from FlwTask t where t.schId = :schId and t.assigneeId = :assigneeId and t.status = 0 "
/** 我的待办(主路径,走 assignee 索引;appKey 可空=不按应用过滤,口径=办件定义归属应用 flw_def.app_key) */
@Query("select t from FlwTask t join FlwInstance i on i.id = t.instanceId join FlwDef d on d.id = i.defId "
+ "where t.schId = :schId and t.assigneeId = :assigneeId and t.status = 0 "
+ "and (:appKey is null or d.appKey = :appKey) "
+ "order by t.arriveTime asc, t.id asc")
Page<FlwTask> pageTodo(@Param("schId") Long schId, @Param("assigneeId") Integer assigneeId, Pageable pageable);
Page<FlwTask> pageTodo(@Param("schId") Long schId, @Param("assigneeId") Integer assigneeId,
@Param("appKey") String appKey, Pageable pageable);
/**
* 我的待办(oapi 版,bizType 可选过滤):join 办件表过滤业务类型,排序口径与 {@link #pageTodo}
@@ -75,14 +78,19 @@ public interface FlwTaskRepository extends JpaRepository<FlwTask, Long> {
*
* <p>状态集由调用方传 {@link com.yunhai.flowcenter.enums.FlwEnums.TaskStatus#handledCodes()}
* (勿硬编码字面量);留痕事件行 6/7 不在其中,故不会出现在已办里。
*
* <p>appKey 可空=不过滤,join 均为多对一(办件→定义各一行),不影响"一个办件一行"的分页口径。
*/
@Query("select t from FlwTask t where t.schId = :schId and t.assigneeId = :assigneeId "
@Query("select t from FlwTask t join FlwInstance i on i.id = t.instanceId join FlwDef d on d.id = i.defId "
+ "where t.schId = :schId and t.assigneeId = :assigneeId "
+ "and t.status in :statuses "
+ "and (:appKey is null or d.appKey = :appKey) "
+ "and t.id = (select max(t2.id) from FlwTask t2 where t2.instanceId = t.instanceId "
+ " and t2.assigneeId = :assigneeId and t2.status in :statuses) "
+ "order by t.handleTime desc, t.id desc")
Page<FlwTask> pageDone(@Param("schId") Long schId, @Param("assigneeId") Integer assigneeId,
@Param("statuses") List<Integer> statuses, Pageable pageable);
@Param("statuses") List<Integer> statuses, @Param("appKey") String appKey,
Pageable pageable);
/** 已办累计次数(按办件批量,配合 pageDone 的"一个办件一行") */
@Query("select t.instanceId, count(t) from FlwTask t where t.instanceId in :instanceIds "
@@ -50,8 +50,8 @@ public class FlowQueryService {
private final FlowEngine flowEngine;
public BasePageResult<FlowModels.InstanceItem> pageMy(Long schId, Integer submitterId,
Integer status, Integer page, Integer size) {
Page<FlwInstance> p = instanceRepository.pageMy(schId, submitterId, status,
Integer status, String appKey, Integer page, Integer size) {
Page<FlwInstance> p = instanceRepository.pageMy(schId, submitterId, status, emptyToNull(appKey),
PageRequest.of(normPage(page), normSize(size)));
return toResult(p.getTotalElements(), p.getContent().stream().map(this::toInstanceItem)
.collect(Collectors.toList()), page, size);
@@ -59,8 +59,8 @@ public class FlowQueryService {
/** 抄送我的(NOTIFY 落库行,sent_time 倒序;知会不占待办,独立于 todo/done 口径) */
public BasePageResult<FlowModels.CcItem> pageCc(Long schId, Integer userId,
Integer page, Integer size) {
Page<FlwCc> p = ccRepository.findBySchIdAndUserIdOrderBySentTimeDesc(schId, userId,
String appKey, Integer page, Integer size) {
Page<FlwCc> p = ccRepository.pageCc(schId, userId, emptyToNull(appKey),
PageRequest.of(normPage(page), normSize(size)));
List<FlowModels.CcItem> items = p.getContent().stream()
.map(this::toCcItem).collect(Collectors.toList());
@@ -92,9 +92,9 @@ public class FlowQueryService {
}
}
public BasePageResult<FlowModels.TaskItem> pageTodo(Long schId, Integer assigneeId,
public BasePageResult<FlowModels.TaskItem> pageTodo(Long schId, Integer assigneeId, String appKey,
Integer page, Integer size) {
Page<FlwTask> p = taskRepository.pageTodo(schId, assigneeId,
Page<FlwTask> p = taskRepository.pageTodo(schId, assigneeId, emptyToNull(appKey),
PageRequest.of(normPage(page), normSize(size)));
List<FlowModels.TaskItem> items = p.getContent().stream()
.map(this::toTaskItem).collect(Collectors.toList());
@@ -103,10 +103,10 @@ public class FlowQueryService {
}
/** 已办:一个办件一行(保留本人最后办理的那条),并回填累计办理次数 */
public BasePageResult<FlowModels.TaskItem> pageDone(Long schId, Integer assigneeId,
public BasePageResult<FlowModels.TaskItem> pageDone(Long schId, Integer assigneeId, String appKey,
Integer page, Integer size) {
List<Integer> handled = FlwEnums.TaskStatus.handledCodes();
Page<FlwTask> p = taskRepository.pageDone(schId, assigneeId, handled,
Page<FlwTask> p = taskRepository.pageDone(schId, assigneeId, handled, emptyToNull(appKey),
PageRequest.of(normPage(page), normSize(size)));
List<FlowModels.TaskItem> items = p.getContent().stream()
.map(this::toTaskItem).collect(Collectors.toList());
@@ -522,6 +522,11 @@ public class FlowQueryService {
return page == null || page < 1 ? 0 : page - 1;
}
/** 空串按「不过滤」处理(前端清筛选可能传空值,避免误筛成空列表) */
private static String emptyToNull(String s) {
return s == null || s.trim().isEmpty() ? null : s.trim();
}
private static int normSize(Integer size) {
return size == null || size < 1 || size > 100 ? 10 : size;
}
@@ -233,7 +233,7 @@ class FlowEngineIT extends FlowTestSupport {
// ② 已办去重:同一办件一行,结果是最后一次(同意),次数=2
com.yunhai.platform.utils.model.BasePageResult<com.yunhai.flowcenter.model.FlowModels.TaskItem>
done = queryService.pageDone(SCH, 101, 1, 10);
done = queryService.pageDone(SCH, 101, null, 1, 10);
assertEquals(1, done.getContent().size(), "一个办件只能有一条已办记录");
assertEquals(2, done.getContent().get(0).getDoneCount());
assertEquals(FlwEnums.TaskStatus.APPROVED.getCode(), done.getContent().get(0).getStatus());
@@ -509,7 +509,7 @@ class FlowEngineIT extends FlowTestSupport {
assertEquals(java.util.Arrays.asList("SUBMIT", "CC", "CC", "APPROVE"), kinds);
// 抄送我的:301 视角一行,回填办件信息
com.yunhai.platform.utils.model.BasePageResult<com.yunhai.flowcenter.model.FlowModels.CcItem>
ccPage = queryService.pageCc(SCH, 301, 1, 10);
ccPage = queryService.pageCc(SCH, 301, null, 1, 10);
assertEquals(1, ccPage.getContent().size());
assertEquals(instanceId, ccPage.getContent().get(0).getInstanceId());
assertEquals("张三的测试办件", ccPage.getContent().get(0).getBizTitle());