feat: 提交流程中心后端代码
build / build-push (push) Successful in 28s

Spring Boot 流程引擎服务:流程定义/实例/任务、审批人解析、
回调分发、oapi 接口及单元测试。
保留远程 .gitea/workflows/build.yml 与 docker/Dockerfile,
新增 .gitignore 排除 target/ 构建产物。
This commit is contained in:
chenxing
2026-09-15 10:42:56 +08:00
parent 0296b2b5dd
commit c1103aeb29
65 changed files with 7119 additions and 0 deletions
@@ -0,0 +1,115 @@
package com.yunhai.flowcenter.notify;
import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.utils.http.IDHttp;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* UserMsg 站内信客户端(概览 §七 契约,M1 手工三件套路线的<b>直达版</b>):
* 经共享 IDHttp(yh-openapi 装配的出站自动签名 RestTemplate,X-App-Key/X-Timestamp/X-Signature
* = SignUtil.signRequest 请求级 canonical,与 svc-hall FlowCenterClient 同一套通道)直调
* UserMsg /oapi/msg/**。前提:UserMsg 侧 trusted-apps 白名单收本应用 appKey(本地占位 100019);
* SDK(yh-usermsg-spring-boot-starter)落地后整体切换为 SDK 调用,契约不变。
* 发送方不阻断主业务:失败只记日志(对齐 PartyLearn 容错语义),由调用方在 AFTER_COMMIT 监听器里容错。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserMsgClient {
private final IDHttp http;
@Value("${flow-center.usermsg.enabled:false}")
private boolean enabled;
@Value("${flow-center.usermsg.base-url:http://localhost:8080}")
private String baseUrl;
public boolean enabled() {
return enabled;
}
/** 发送站内信(content 必填;msgNo 幂等,重发安全) */
public void sendUserMsg(Integer schId, List<Integer> userIds, Integer msgType, String bizType,
String bizId, String title, String content, String jumpPath, String msgNo,
Integer expireDays) {
if (!enabled || userIds == null || userIds.isEmpty()) {
return;
}
SendMsgRequest body = new SendMsgRequest();
body.setUserIds(userIds);
body.setMsgType(msgType);
body.setBizType(bizType);
body.setBizId(bizId);
body.setTitle(title);
body.setContent(content);
if (jumpPath != null) {
body.setJumpType(1);
body.setJumpPath(jumpPath);
}
body.setMsgNo(msgNo);
body.setSchId(schId);
body.setExpireDays(expireDays);
try {
http.post(trimTrailing(baseUrl) + "/oapi/msg/sendUserMsg", body, Object.class);
} catch (BizException e) {
log.warn("[flow-center] UserMsg 发送被拒:msgNo={} msg={}", msgNo, e.getMessage());
} catch (Exception e) {
log.warn("[flow-center] UserMsg 发送失败(不阻断主业务):msgNo={} {}", msgNo, e.toString());
}
}
/** 待办角标增量(+1 到达 / -1 办结) */
public void todoInc(Integer schId, List<Integer> userIds, int count) {
if (!enabled || userIds == null || userIds.isEmpty()) {
return;
}
TodoCountRequest body = new TodoCountRequest();
body.setUserIds(userIds);
body.setCount(count);
body.setSchId(schId);
try {
http.post(trimTrailing(baseUrl) + "/oapi/msg/todo/inc", body, Object.class);
} catch (Exception e) {
log.warn("[flow-center] UserMsg 角标增量失败(不阻断主业务):userIds={} count={} {}",
userIds, count, e.toString());
}
}
private static String trimTrailing(String url) {
return url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
}
/** /oapi/msg/sendUserMsg 请求体(UserMsg SendMsgRequest 同构,字段逐字对齐) */
@Data
public static class SendMsgRequest {
private List<Integer> userIds;
private String title;
/** 必填(UserMsg @NotBlank) */
private String content;
private String msgNo;
private String bizType;
private String bizId;
/** 0系统通知/1业务提醒/2审批待办 */
private Integer msgType;
/** 0无跳转/1应用内路径/2外链 */
private Integer jumpType;
private String jumpPath;
private Integer schId;
private Integer expireDays;
}
/** /oapi/msg/todo/inc 请求体(UserMsg TodoCountRequest 同构) */
@Data
public static class TodoCountRequest {
private List<Integer> userIds;
private Integer count;
private Integer schId;
}
}