feat: 办事大厅后端源码首次入库(事项/字典/表单/申报 + 流程回调)
build / build-push (push) Failing after 39s

This commit is contained in:
chenxing
2026-09-15 11:00:00 +08:00
parent 1eeb7a4276
commit d47951abd7
70 changed files with 7569 additions and 0 deletions
@@ -0,0 +1,66 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceApply;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import javax.persistence.LockModeType;
import java.util.List;
import java.util.Optional;
public interface ServiceApplyRepository extends JpaRepository<ServiceApply, Integer>,
JpaSpecificationExecutor<ServiceApply> {
List<ServiceApply> findBySchIdAndSubmitterIdOrderByCreateTimeDesc(Long schId, Integer submitterId, Pageable pageable);
Optional<ServiceApply> findByIdAndSchId(Integer id, Long schId);
Optional<ServiceApply> findBySchIdAndFlowInstanceId(Long schId, Long flowInstanceId);
/** 回调侧行锁串行化:并发推送(flow-center 推送池)对同一办件的处理排队,防丢更新 */
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from ServiceApply a where a.id = :id")
Optional<ServiceApply> findByIdForUpdate(@Param("id") Integer id);
/** 对账:在途办件(待提交/审批中) */
List<ServiceApply> findBySchIdAndFlowStatusIn(Long schId, List<Integer> statuses);
/** 全租户对账:审批中的在途办件(分页上限防御) */
List<ServiceApply> findByFlowStatus(Integer flowStatus, Pageable pageable);
/** 待提交且已产生 flowInstanceId 之外的挂起单(代发起未返回的重试候选) */
List<ServiceApply> findBySchIdAndFlowStatus(Long schId, Integer flowStatus, Pageable pageable);
/**
* 代发起成功回填(窄列原子更新,Notice markApproving 同构):
* 置 flow_instance_id + flow_status=10;若回调已并发先行落定终态(20 完成/30 拒绝/40 撤回,
* code 对齐 ApplyFlowStatusEnum)则保持终态不回退(验收报告 §四.2:旧快照盲写会把状态打回);
* submitTime 仅首次回填(coalesce 保留既有值)。
*/
@Modifying
@Query("update ServiceApply a set a.flowInstanceId = :instanceId,"
+ " a.flowStatus = (case when a.flowStatus in (20, 30, 40) then a.flowStatus else 10 end),"
+ " a.submitTime = coalesce(a.submitTime, :submitTime)"
+ " where a.id = :id")
int markApproving(@Param("id") Integer id, @Param("instanceId") Long instanceId,
@Param("submitTime") java.time.LocalDateTime submitTime);
/**
* 一键重提回填(P2-5/R4):驳回/撤回(30/40)行经 flow-center R4 重启推进后回开为 10。
* 与 {@link #markApproving} 的差别:30/40 → 10 是合法回开(markApproving 的终态守卫
* 语义是"回调已落定终态不被提交侧盲写打回",重提是用户显式动作);重提是新的提交动作,
* submitTime 覆盖为当下、finishTime 清空。WHERE 限定 30/40,并发窗口内若状态已被推进
* (如新一轮回调先置 10)则本次为幂等空更新。
*/
@Modifying
@Query("update ServiceApply a set a.flowInstanceId = :instanceId, a.flowStatus = 10,"
+ " a.submitTime = :submitTime, a.finishTime = null"
+ " where a.id = :id and a.flowStatus in (30, 40)")
int markResubmitted(@Param("id") Integer id, @Param("instanceId") Long instanceId,
@Param("submitTime") java.time.LocalDateTime submitTime);
}
@@ -0,0 +1,18 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceAttachment;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ServiceAttachmentRepository extends JpaRepository<ServiceAttachment, Integer> {
List<ServiceAttachment> findBySchIdAndTopicTypeAndTopicIdOrderByIdAsc(Long schId, String topicType,
Integer topicId);
List<ServiceAttachment> findBySchIdAndTopicTypeAndTopicId(Long schId, String topicType, Integer topicId);
List<ServiceAttachment> findAllById(Iterable<Integer> ids);
void deleteBySchIdAndTopicTypeAndTopicId(Long schId, String topicType, Integer topicId);
}
@@ -0,0 +1,26 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceDict;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface ServiceDictRepository extends JpaRepository<ServiceDict, Integer> {
List<ServiceDict> findBySchIdAndSiteCodeAndDictTypeOrderBySortNoAscIdAsc(Long schId, String siteCode,
String dictType);
List<ServiceDict> findBySchIdAndSiteCodeOrderBySortNoAscIdAsc(Long schId, String siteCode);
Optional<ServiceDict> findByIdAndSchIdAndSiteCode(Integer id, Long schId, String siteCode);
Optional<ServiceDict> findBySchIdAndSiteCodeAndDictTypeAndDictKey(Long schId, String siteCode, String dictType,
String dictKey);
boolean existsBySchIdAndSiteCodeAndDictTypeAndDictKey(Long schId, String siteCode, String dictType,
String dictKey);
boolean existsBySchIdAndSiteCodeAndDictTypeAndDictKeyAndIdNot(Long schId, String siteCode, String dictType,
String dictKey, Integer id);
}
@@ -0,0 +1,9 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceFlowEvent;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ServiceFlowEventRepository extends JpaRepository<ServiceFlowEvent, Integer> {
boolean existsByEventId(String eventId);
}
@@ -0,0 +1,15 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceFlowReg;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ServiceFlowRegRepository extends JpaRepository<ServiceFlowReg, Integer> {
Optional<ServiceFlowReg> findBySchIdAndFlowGuid(Long schId, String flowGuid);
Optional<ServiceFlowReg> findByFlowGuid(String flowGuid);
void deleteBySchIdAndFlowGuid(Long schId, String flowGuid);
}
@@ -0,0 +1,26 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceForm;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface ServiceFormRepository extends JpaRepository<ServiceForm, Integer> {
List<ServiceForm> findBySchIdOrderByCodeAscVersionDescIdAsc(Long schId);
List<ServiceForm> findBySchIdAndCodeOrderByVersionDesc(Long schId, String code);
Optional<ServiceForm> findByIdAndSchId(Integer id, Long schId);
Optional<ServiceForm> findBySchIdAndCodeAndVersion(Long schId, String code, Integer version);
/** 事项绑定校验/发起取数:code 的最新已发布版本 */
Optional<ServiceForm> findFirstBySchIdAndCodeAndStatusOrderByVersionDesc(Long schId, String code, Integer status);
boolean existsBySchIdAndCode(Long schId, String code);
/** 事项引用计数(删除守护:任一版本被绑定即算被引用,按 code 判) */
long countBySchIdAndCode(Long schId, String code);
}
@@ -0,0 +1,74 @@
package com.yunhai.svchall.repository;
import com.yunhai.svchall.entity.ServiceItem;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface ServiceItemRepository extends JpaRepository<ServiceItem, Integer>,
JpaSpecificationExecutor<ServiceItem> {
Optional<ServiceItem> findByIdAndSchIdAndSiteCode(Integer id, Long schId, String siteCode);
Optional<ServiceItem> findBySchIdAndSiteCodeAndItemCode(Long schId, String siteCode, String itemCode);
boolean existsBySchIdAndSiteCodeAndItemCode(Long schId, String siteCode, String itemCode);
boolean existsBySchIdAndSiteCodeAndItemCodeAndIdNot(Long schId, String siteCode, String itemCode, Integer id);
/** 字典键被事项引用计数(字典改键/停用守护用) */
long countBySchIdAndSiteCodeAndObjectKey(Long schId, String siteCode, String objectKey);
long countBySchIdAndSiteCodeAndCategoryKey(Long schId, String siteCode, String categoryKey);
/** 关联事项:同类别已发布,排除自身(Pageable 控制条数与排序) */
List<ServiceItem> findBySchIdAndSiteCodeAndStatusAndCategoryKeyAndIdNot(Long schId, String siteCode,
Integer status, String categoryKey,
Integer id, Pageable pageable);
/** 管理端选择器/部门名同步:全状态按排序取(条数由 Pageable 控制) */
List<ServiceItem> findBySchIdAndSiteCodeOrderBySortNoAscIdAsc(Long schId, String siteCode, Pageable pageable);
List<ServiceItem> findBySchIdAndFlowGuid(Long schId, String flowGuid);
/** 表单被事项绑定计数(表单删除守护,按 form_code 判) */
long countBySchIdAndFormCode(Long schId, String formCode);
// ===== 门户筛选项计数(已发布口径,group by 一次往返/维度,承袭 CMS 口径)=====
@Query("select i.objectKey, count(i) from ServiceItem i"
+ " where i.schId = :schId and i.siteCode = :siteCode and i.status = 1 and i.objectKey is not null"
+ " group by i.objectKey")
List<Object[]> countPublishedGroupByObjectKey(@Param("schId") Long schId, @Param("siteCode") String siteCode);
@Query("select i.categoryKey, count(i) from ServiceItem i"
+ " where i.schId = :schId and i.siteCode = :siteCode and i.status = 1 and i.categoryKey is not null"
+ " group by i.categoryKey")
List<Object[]> countPublishedGroupByCategoryKey(@Param("schId") Long schId, @Param("siteCode") String siteCode);
@Query("select i.deptGroupId, max(i.deptName), count(i) from ServiceItem i"
+ " where i.schId = :schId and i.siteCode = :siteCode and i.status = 1 and i.deptGroupId is not null"
+ " group by i.deptGroupId")
List<Object[]> countPublishedGroupByDept(@Param("schId") Long schId, @Param("siteCode") String siteCode);
/** 指南页浏览量异步累加(fire-and-forget,失败忽略) */
@Modifying
@Query("update ServiceItem i set i.viewCount = i.viewCount + 1 where i.id = :id")
int incrementViewCount(@Param("id") Integer id);
// ===== 字典引用计数(全状态口径,字典守护与列表"被引用"展示用)=====
@Query("select i.objectKey, count(i) from ServiceItem i"
+ " where i.schId = :schId and i.siteCode = :siteCode and i.objectKey is not null group by i.objectKey")
List<Object[]> countGroupByObjectKey(@Param("schId") Long schId, @Param("siteCode") String siteCode);
@Query("select i.categoryKey, count(i) from ServiceItem i"
+ " where i.schId = :schId and i.siteCode = :siteCode and i.categoryKey is not null group by i.categoryKey")
List<Object[]> countGroupByCategoryKey(@Param("schId") Long schId, @Param("siteCode") String siteCode);
}