|
|
@@ -24,10 +24,12 @@ import jakarta.annotation.Resource;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.context.annotation.Lazy;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
import static cn.start.tz.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
|
|
|
|
|
|
@@ -70,31 +72,158 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
@Lazy
|
|
|
private PipeTaskOrderItemReportService pipeTaskOrderItemReportService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private StringRedisTemplate stringRedisTemplate;
|
|
|
+
|
|
|
@Value("${tz.fonts-folder-path:}")
|
|
|
private String fontsFolderPath;
|
|
|
|
|
|
+ /** Redis缓存key前缀:OA token */
|
|
|
+ private static final String OA_TOKEN_CACHE_KEY = "OA_TOKEN:";
|
|
|
+ /** Token缓存过期时间:30分钟 */
|
|
|
+ private static final long TOKEN_CACHE_TTL_SECONDS = 30 * 60;
|
|
|
+
|
|
|
/**
|
|
|
- * 外部接口调用获取OA系统token
|
|
|
+ * 外部接口调用获取OA系统token(带Redis缓存)
|
|
|
*
|
|
|
+ * @param oaLoginName 登录用户名,为null时使用默认配置
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
- public ExternalOATokenRes getExternalOAToken( String oaLoginName ) {
|
|
|
+ public ExternalOATokenRes getExternalOAToken(String oaLoginName) {
|
|
|
+ // 确定实际使用的登录名
|
|
|
+ String effectiveLoginName = oaLoginName != null ? oaLoginName : this.oaLoginName;
|
|
|
+ String cacheKey = OA_TOKEN_CACHE_KEY + effectiveLoginName;
|
|
|
+
|
|
|
+ // 1. 先从Redis缓存获取token
|
|
|
+ String cachedTokenJson = stringRedisTemplate.opsForValue().get(cacheKey);
|
|
|
+ if (StringUtils.isNotBlank(cachedTokenJson)) {
|
|
|
+ log.debug("从Redis缓存获取OA token,loginName={}", effectiveLoginName);
|
|
|
+ return JSON.parseObject(cachedTokenJson, ExternalOATokenRes.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 缓存未命中,调用OA接口获取新token
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
|
params.put("userName", oaUserName);
|
|
|
params.put("password", oaUserPassword);
|
|
|
- if (oaLoginName != null){
|
|
|
- params.put("loginName", oaLoginName);
|
|
|
- }else {
|
|
|
- params.put("loginName",this.oaLoginName);
|
|
|
- }
|
|
|
+ params.put("loginName", effectiveLoginName);
|
|
|
|
|
|
String res = HttpRequest.post(this.oaHost + "/seeyon/rest/token").body(JSON.toJSONString(params)).execute().body();
|
|
|
ExternalOATokenRes externalOATokenRes = JSON.parseObject(res, ExternalOATokenRes.class);
|
|
|
log.info("获取外部OA token结果:{}", externalOATokenRes);
|
|
|
+
|
|
|
+ // 3. 存入Redis缓存(仅当token有效时缓存)
|
|
|
+ if (externalOATokenRes != null && StringUtils.isNotBlank(externalOATokenRes.getId())) {
|
|
|
+ stringRedisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(externalOATokenRes),
|
|
|
+ TOKEN_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
return externalOATokenRes;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 强制刷新token缓存(清除缓存后重新获取)
|
|
|
+ *
|
|
|
+ * @param oaLoginName 登录用户名
|
|
|
+ * @return 新的token响应
|
|
|
+ */
|
|
|
+ private ExternalOATokenRes refreshToken(String oaLoginName) {
|
|
|
+ String effectiveLoginName = oaLoginName != null ? oaLoginName : this.oaLoginName;
|
|
|
+ stringRedisTemplate.delete(OA_TOKEN_CACHE_KEY + effectiveLoginName);
|
|
|
+ log.info("已清除OA token缓存并重新获取,loginName={}", effectiveLoginName);
|
|
|
+ return getExternalOAToken(oaLoginName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断OA接口响应是否表示token已失效
|
|
|
+ * 当响应为null、空字符串、或返回 {"code": 401, "success": false, "message": "Invalid token,..."} 格式时,认为token失效
|
|
|
+ *
|
|
|
+ * @param responseBody 接口响应体
|
|
|
+ * @return true表示token失效
|
|
|
+ */
|
|
|
+ private boolean isTokenInvalidResponse(String responseBody) {
|
|
|
+ // 响应为空,视为token失效
|
|
|
+ if (StringUtils.isBlank(responseBody)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ JSONObject json = JSON.parseObject(responseBody);
|
|
|
+ if (json.getInteger("code") != null && (json.getInteger("code") == 401 || json.getInteger("code") == 1010)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 解析异常不算token失效,正常返回
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行OA POST请求(带token过期自动重试)
|
|
|
+ *
|
|
|
+ * @param oaLoginName 登录用户名
|
|
|
+ * @param apiPathWithToken API路径(含token=前缀),如 "/seeyon/rest/myflow/sendflow?token="
|
|
|
+ * @param requestBody POST请求体
|
|
|
+ * @param tokenExtractor 从ExternalOATokenRes中提取token值的方式,如 ExternalOATokenRes::getId 或 r -> String.valueOf(r.getBindingUser().getId())
|
|
|
+ * @return 响应体字符串
|
|
|
+ */
|
|
|
+ private String doPostWithTokenRetry(String oaLoginName, String apiPathWithToken, String requestBody,
|
|
|
+ java.util.function.Function<ExternalOATokenRes, String> tokenExtractor) {
|
|
|
+ ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
|
|
|
+ String token = tokenExtractor.apply(tokenRes);
|
|
|
+ String result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
|
|
|
+ .body(requestBody)
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+
|
|
|
+ // token失效时,刷新token后重试一次
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA token失效,刷新token后重试POST请求");
|
|
|
+ tokenRes = refreshToken(oaLoginName);
|
|
|
+ token = tokenExtractor.apply(tokenRes);
|
|
|
+ log.info("url:{}", this.oaHost + apiPathWithToken + token);
|
|
|
+ result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
|
|
|
+ .body(requestBody)
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+ }
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA token失效,刷新token后重试POST请求");
|
|
|
+ token = tokenRes.getId();
|
|
|
+ log.info("url:{}", this.oaHost + apiPathWithToken + token);
|
|
|
+ result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
|
|
|
+ .body(requestBody)
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行OA GET请求(带token过期自动重试)
|
|
|
+ *
|
|
|
+ * @param oaLoginName 登录用户名
|
|
|
+ * @param apiPathWithToken API路径(含token=前缀),如 "/seeyon/rest/myflow/usercomment?token="
|
|
|
+ * @param extraParams 额外的URL参数,如 "&summaryId=123"
|
|
|
+ * @param tokenExtractor 从ExternalOATokenRes中提取token值的方式
|
|
|
+ * @return 响应体字符串
|
|
|
+ */
|
|
|
+ private String doGetWithTokenRetry(String oaLoginName, String apiPathWithToken, String extraParams,
|
|
|
+ java.util.function.Function<ExternalOATokenRes, String> tokenExtractor) {
|
|
|
+ ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
|
|
|
+ String token = tokenExtractor.apply(tokenRes);
|
|
|
+ String url = this.oaHost + apiPathWithToken + token + extraParams;
|
|
|
+ String result = HttpUtil.createGet(url).execute().body();
|
|
|
+
|
|
|
+ // token失效时,刷新token后重试一次
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA token失效,刷新token后重试GET请求");
|
|
|
+ tokenRes = refreshToken(oaLoginName);
|
|
|
+ token = tokenExtractor.apply(tokenRes);
|
|
|
+ url = this.oaHost + apiPathWithToken + token + extraParams;
|
|
|
+ result = HttpUtil.createGet(url).execute().body();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 发起OA流程
|
|
|
*
|
|
|
@@ -111,10 +240,12 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
req.setTemplateCode("jianwureport");
|
|
|
req.setDraft("0");
|
|
|
log.info("发起OA流程参数:{}", JSON.toJSONString(req));
|
|
|
- String result = HttpUtil.createPost(this.oaHost + "/seeyon/rest/myflow/sendflow?token=" + this.getExternalOAToken(oaLoginName).getBindingUser().getId())
|
|
|
- .body(JSON.toJSONString(req))
|
|
|
- .execute()
|
|
|
- .body();
|
|
|
+
|
|
|
+ // 调用OA接口,token失效时自动刷新重试一次(使用bindingUser.id作为token)
|
|
|
+ String result = doPostWithTokenRetry(oaLoginName,
|
|
|
+ "/seeyon/rest/myflow/sendflow?token=",
|
|
|
+ JSON.toJSONString(req),
|
|
|
+ r -> String.valueOf(r.getBindingUser().getId()));
|
|
|
log.info("发起OA流程结果:{}", result);
|
|
|
return JSON.parseObject(result, ExternalOACreateFlowRes.class);
|
|
|
}
|
|
|
@@ -127,9 +258,11 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
*/
|
|
|
@Override
|
|
|
public ExternalOACommentRes getExternalOAComment(String summaryid) {
|
|
|
- String url = this.oaHost + "/seeyon/rest/myflow/usercomment?token=" + this.getExternalOAToken(null).getBindingUser().getId() + "&summaryId=" + summaryid;
|
|
|
- String result = HttpUtil.createGet(url)
|
|
|
- .execute().body();
|
|
|
+ // 调用OA接口,token失效时自动刷新重试一次(使用token.id)
|
|
|
+ String result = doGetWithTokenRetry(null,
|
|
|
+ "/seeyon/rest/myflow/usercomment?token=",
|
|
|
+ "&summaryId=" + summaryid,
|
|
|
+ ExternalOATokenRes::getId);
|
|
|
log.info("获取OA流程状态结果:{}", result);
|
|
|
return JSON.parseObject(result, ExternalOACommentRes.class);
|
|
|
}
|
|
|
@@ -142,55 +275,79 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
*/
|
|
|
@Override
|
|
|
public ExternalOAUploadAttachmentRes externalOAUploadAttachment(InputStream inputStream, String fileName) {
|
|
|
- // 1. 获取token(保留你的原有逻辑)
|
|
|
- String token = this.getExternalOAToken(null).getId();
|
|
|
+ try {
|
|
|
+ // 1. 先读取文件字节数组(只能读一次,重试时复用)
|
|
|
+ byte[] fileBytes = IoUtil.readBytes(inputStream);
|
|
|
+ if (fileBytes.length == 0) {
|
|
|
+ log.error("上传文件为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 上传附件,token失效时自动刷新重试一次
|
|
|
+ return doUploadFileWithRetry(null, fileName, fileBytes);
|
|
|
+ } finally {
|
|
|
+ IoUtil.close(inputStream);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行文件上传(带token过期重试)
|
|
|
+ */
|
|
|
+ private ExternalOAUploadAttachmentRes doUploadFileWithRetry(String oaLoginName, String fileName, byte[] fileBytes) {
|
|
|
+ ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
|
|
|
+ String token = tokenRes != null ? tokenRes.getId() : null;
|
|
|
if (StringUtils.isBlank(token)) {
|
|
|
log.error("获取外部OA token失败");
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- // 2. 构建带token参数的URL(保留原有逻辑)
|
|
|
String url = this.oaHost + "/seeyon/rest/attachment?token=" + token;
|
|
|
log.info("OA上传附件地址:{},文件名:{}", url, fileName);
|
|
|
|
|
|
- try {
|
|
|
- // 3. 关键修复:用 Hutool 支持的 byte[] 代替 InputStreamResource(避免流关闭问题)
|
|
|
- byte[] fileBytes = IoUtil.readBytes(inputStream); // Hutool 的 IoUtil,安全读取流为字节数组
|
|
|
- if (fileBytes.length == 0) {
|
|
|
- log.error("上传文件为空");
|
|
|
- return null;
|
|
|
+ ExternalOAUploadAttachmentRes result = executeUpload(url, fileBytes, fileName);
|
|
|
+ if (result == null) {
|
|
|
+ // token可能失效,刷新后重试一次
|
|
|
+ log.warn("OA上传附件失败,刷新token后重试");
|
|
|
+ tokenRes = refreshToken(oaLoginName);
|
|
|
+ token = tokenRes != null ? tokenRes.getId() : null;
|
|
|
+ if (StringUtils.isNotBlank(token)) {
|
|
|
+ url = this.oaHost + "/seeyon/rest/attachment?token=" + token;
|
|
|
+ result = executeUpload(url, fileBytes, fileName);
|
|
|
}
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- // 4. 发起请求(核心修复:移除手动Content-Type,用form传byte[]+文件名)
|
|
|
- try (HttpResponse response = HttpUtil.createPost(url)
|
|
|
- // 移除手动设置的 Content-Type!Hutool 会自动生成 multipart/form-data + boundary
|
|
|
- .header("Accept", "application/json") // 告诉接口期望返回JSON(可选,根据对方接口调整)
|
|
|
- // 关键:用 byte[] 传文件,第三个参数是文件名(Hutool会自动封装为正确的表单文件格式)
|
|
|
- .form("file", fileBytes, fileName)
|
|
|
- .timeout(60000) // 延长超时(文件上传建议60秒)
|
|
|
- .execute()) {
|
|
|
-
|
|
|
- // 5. 新增:打印响应状态码和响应体(排查关键)
|
|
|
- int statusCode = response.getStatus();
|
|
|
- String result = response.body();
|
|
|
- log.info("OA上传附件响应状态码:{},响应体:{}", statusCode, result);
|
|
|
-
|
|
|
- // 6. 校验响应状态(非200直接返回失败)
|
|
|
- if (statusCode != HttpStatus.HTTP_OK) {
|
|
|
- log.error("上传失败,状态码:{},响应:{}", statusCode, result);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 执行单次文件上传请求
|
|
|
+ * 返回null表示请求失败(含token失效),触发调用方重试
|
|
|
+ */
|
|
|
+ private ExternalOAUploadAttachmentRes executeUpload(String url, byte[] fileBytes, String fileName) {
|
|
|
+ try (HttpResponse response = HttpUtil.createPost(url)
|
|
|
+ .header("Accept", "application/json")
|
|
|
+ .form("file", fileBytes, fileName)
|
|
|
+ .timeout(60000)
|
|
|
+ .execute()) {
|
|
|
+
|
|
|
+ int statusCode = response.getStatus();
|
|
|
+ String result = response.body();
|
|
|
+ log.info("OA上传附件响应状态码:{},响应体:{}", statusCode, result);
|
|
|
+
|
|
|
+ // 检查响应体是否指示token失效(如 {"code": 401})
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA上传附件响应指示token失效");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- // 7. 解析响应(保留原有逻辑)
|
|
|
- ExternalOAUploadAttachmentRes uploadRes = JSON.parseObject(result, ExternalOAUploadAttachmentRes.class);
|
|
|
- return uploadRes;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("OA上传附件请求异常", e);
|
|
|
+ if (statusCode != HttpStatus.HTTP_OK) {
|
|
|
+ log.error("上传失败,状态码:{},响应:{}", statusCode, result);
|
|
|
return null;
|
|
|
}
|
|
|
- } finally {
|
|
|
- // 关闭输入流(避免资源泄漏)
|
|
|
- IoUtil.close(inputStream);
|
|
|
+
|
|
|
+ return JSON.parseObject(result, ExternalOAUploadAttachmentRes.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("OA上传附件请求异常", e);
|
|
|
+ return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -198,12 +355,13 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
@Override
|
|
|
public CommonResult<Boolean> cancleflow(String summaryId) {
|
|
|
log.info("撤销OA流程,summaryId={}", summaryId);
|
|
|
- // 调用OA系统撤销流程接口
|
|
|
- String url = this.oaHost + "/seeyon/rest/myflow/cancleflow?token=" + this.getExternalOAToken("300801").getBindingUser().getId();
|
|
|
+ // 调用OA系统撤销流程接口(POST,summaryId放body,使用bindingUser.id作为token)
|
|
|
JSONObject params = new JSONObject();
|
|
|
params.put("summaryId", summaryId);
|
|
|
- String result = HttpUtil.createPost(url).body(params.toJSONString())
|
|
|
- .execute().body();
|
|
|
+ String result = doPostWithTokenRetry("300801",
|
|
|
+ "/seeyon/rest/myflow/cancleflow?token=",
|
|
|
+ params.toJSONString(),
|
|
|
+ r -> String.valueOf(r.getBindingUser().getId()));
|
|
|
log.info("撤销OA流程结果:{}", result);
|
|
|
JSONObject jsonObject = JSON.parseObject(result);
|
|
|
if (jsonObject.getString("data") != null && jsonObject.getString("data").contains("撤销成功!")) {
|
|
|
@@ -262,7 +420,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
}
|
|
|
|
|
|
// 构建请求并调用OA获取待办事项列表
|
|
|
- String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getBindingUser().getId();
|
|
|
+ String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
|
|
|
Map<String, String> requestBody = new HashMap<>();
|
|
|
requestBody.put("memberCode", checkedData.getUsername());
|
|
|
requestBody.put("templateCode", "jianwureport");
|
|
|
@@ -278,6 +436,19 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
.body();
|
|
|
log.info("获取OA待办事项响应:{}", result);
|
|
|
|
|
|
+ // token失效时,刷新后重试一次
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA token失效,刷新token后重试获取待办事项");
|
|
|
+ tokenRes = refreshToken(checkedData.getUsername());
|
|
|
+ url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
|
|
|
+ result = HttpUtil.createPost(url)
|
|
|
+ .body(JSON.toJSONString(requestBody))
|
|
|
+ .timeout(30000)
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+ log.info("重试获取OA待办事项响应:{}", result);
|
|
|
+ }
|
|
|
+
|
|
|
// 解析响应并校验格式
|
|
|
OAGetAffairsResVO getAffairsRes = JSON.parseObject(result, OAGetAffairsResVO.class);
|
|
|
if (getAffairsRes == null) {
|
|
|
@@ -311,6 +482,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
throw new RuntimeException("获取OA待办事项链接失败:" + e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
@Override
|
|
|
public List<Long> getAffairSummaryList() {
|
|
|
// 获取OA系统token
|
|
|
@@ -328,7 +500,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
}
|
|
|
|
|
|
// 构建请求并调用OA获取待办事项列表
|
|
|
- String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getBindingUser().getId();
|
|
|
+ String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
|
|
|
Map<String, String> requestBody = new HashMap<>();
|
|
|
requestBody.put("memberCode", checkedData.getUsername());
|
|
|
requestBody.put("templateCode", "jianwureport");
|
|
|
@@ -344,6 +516,19 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
.body();
|
|
|
log.info("获取OA待办事项响应:{}", result);
|
|
|
|
|
|
+ // token失效时,刷新后重试一次
|
|
|
+ if (isTokenInvalidResponse(result)) {
|
|
|
+ log.warn("OA token失效,刷新token后重试获取待办事项列表");
|
|
|
+ tokenRes = refreshToken(checkedData.getUsername());
|
|
|
+ url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
|
|
|
+ result = HttpUtil.createPost(url)
|
|
|
+ .body(JSON.toJSONString(requestBody))
|
|
|
+ .timeout(30000)
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+ log.info("重试获取OA待办事项列表响应:{}", result);
|
|
|
+ }
|
|
|
+
|
|
|
// 解析响应并校验格式
|
|
|
OAGetAffairsResVO getAffairsRes = JSON.parseObject(result, OAGetAffairsResVO.class);
|
|
|
if (getAffairsRes == null) {
|
|
|
@@ -363,8 +548,8 @@ public class ExternalOAServiceImpl implements ExternalOAService {
|
|
|
// 筛选SUMMARYID匹配的待办事项
|
|
|
return data.getList().stream().map(OAAffairItemVO::getSummaryId).toList();
|
|
|
} catch (Exception e) {
|
|
|
- log.error("获取OA待办事项链接异常,", e);
|
|
|
- throw new RuntimeException("获取OA待办事项链接失败:" + e.getMessage(), e);
|
|
|
+ log.error("获取OA待办事项列表异常", e);
|
|
|
+ throw new RuntimeException("获取OA待办事项列表失败:" + e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
}
|