package com.ruoyi.framework.util;
|
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import com.ruoyi.framework.config.AliDingConfig;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.lang.reflect.Field;
|
import java.lang.reflect.Method;
|
import java.nio.charset.StandardCharsets;
|
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
import java.time.ZoneId;
|
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeParseException;
|
|
/**
|
* <br>
|
* 根据宜搭表单 ID 获取数据
|
* </br>
|
*
|
* @author deslrey
|
* @version 1.0
|
* @since 2026/03/14 11:11
|
*/
|
@Slf4j
|
public class AliDingUtils {
|
|
|
/**
|
* 根据表单 ID 获取宜搭数据
|
*
|
* @param aliDingConfig 钉钉宜搭接口配置
|
* @param formUuid 获取数据的表单ID
|
* @param searchFieldJson 需要携带的查询条件
|
* @param service 获取最近表单更新的日期Service
|
* @param timeField 表单修改日期
|
* @param <T> 实体类
|
* @return 对应表单的数据
|
*/
|
public static <T> JSONArray getFormDataList(AliDingConfig aliDingConfig, String formUuid, String searchFieldJson, IService<T> service, SFunction<T, ?> timeField) {
|
// 获取 accessToken
|
String accessToken = getAccessToken(aliDingConfig);
|
// 获取最后同步时间
|
LocalDateTime lastSyncTime = getLastSyncTime(service, timeField);
|
log.info("开始同步数据,本地最后修改时间: {}", lastSyncTime);
|
|
JSONArray allData = new JSONArray();
|
int pageNumber = 1;
|
int pageSize = 50;
|
boolean hasMore = true;
|
|
while (hasMore) {
|
JSONObject searchParam = new JSONObject();
|
searchParam.put("appType", aliDingConfig.getAppType());
|
searchParam.put("systemToken", aliDingConfig.getSystemToken());
|
searchParam.put("userId", aliDingConfig.getUserId());
|
searchParam.put("formUuid", formUuid);
|
searchParam.put("currentPage", pageNumber);
|
searchParam.put("pageSize", pageSize);
|
|
if (StringUtils.isNotEmpty(searchFieldJson)) {
|
searchParam.put("searchFieldJson", searchFieldJson);
|
}
|
|
searchParam.put("orderConfigJson", "{\"gmt_modified\":\"+\"}");
|
|
if (lastSyncTime != null) {
|
String startTime = lastSyncTime.plusSeconds(1)
|
.atZone(ZoneId.systemDefault())
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
searchParam.put("modifiedFromTimeGMT", startTime);
|
}
|
|
String endTime = LocalDateTime.now()
|
.atZone(ZoneId.systemDefault())
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
searchParam.put("modifiedToTimeGMT", endTime);
|
|
String dataRes = HttpUtils.sendPostJson(
|
aliDingConfig.getSearchFormDataUrl(),
|
searchParam.toJSONString(),
|
StandardCharsets.UTF_8.name(),
|
null,
|
accessToken
|
);
|
|
if (StringUtils.isEmpty(dataRes)) {
|
log.warn("第 {} 页拉取数据为空", pageNumber);
|
break;
|
}
|
|
JSONObject resultObj = JSON.parseObject(dataRes);
|
JSONArray dataArr = resultObj.getJSONArray("data");
|
Integer totalCount = resultObj.getInteger("totalCount");
|
|
if (dataArr == null || dataArr.isEmpty()) {
|
log.info("没有更多新数据需要同步");
|
break;
|
}
|
allData.addAll(dataArr);
|
hasMore = (pageNumber * pageSize) < totalCount;
|
pageNumber++;
|
|
log.info("正在提取宜搭分页数据,第 {} 页已处理,当前提取数: {}/{}", pageNumber - 1, allData.size(), totalCount);
|
}
|
|
return allData;
|
}
|
|
/**
|
* 获取钉钉 AccessToken
|
*/
|
private static String getAccessToken(AliDingConfig aliDingConfig) {
|
String params = "appkey=" + aliDingConfig.getAppKey() + "&appsecret=" + aliDingConfig.getAppSecret();
|
String tokenRes = HttpUtils.sendGet(aliDingConfig.getAccessTokenUrl(), params);
|
JSONObject tokenObj = JSON.parseObject(tokenRes);
|
String accessToken = tokenObj.getString("access_token");
|
if (StringUtils.isEmpty(accessToken)) {
|
log.error("获取钉钉AccessToken失败: {}", tokenRes);
|
}
|
return accessToken;
|
}
|
|
/**
|
* 日期格式化
|
*
|
* @param utcString 日期
|
* @return LocalDateTime
|
*/
|
public static LocalDateTime parseUtcTime(String utcString) {
|
if (StringUtils.isEmpty(utcString)) {
|
return null;
|
}
|
try {
|
OffsetDateTime odt = OffsetDateTime.parse(utcString);
|
return odt.toLocalDateTime();
|
} catch (DateTimeParseException ex) {
|
log.warn("解析时间 {} 失败: {}", utcString, ex.getMessage());
|
return null;
|
}
|
}
|
|
private static <T> LocalDateTime getLastSyncTime(IService<T> service, SFunction<T, ?> timeField) {
|
LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
|
queryWrapper.orderByDesc(timeField).last("LIMIT 1");
|
T lastRecord = service.getOne(queryWrapper);
|
if (lastRecord == null) {
|
return null;
|
}
|
try {
|
Method writeReplace = timeField.getClass().getDeclaredMethod("writeReplace");
|
writeReplace.setAccessible(true);
|
SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(timeField);
|
// 获取方法名
|
String methodName = lambda.getImplMethodName();
|
// 转字段名
|
String fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
|
Field field = lastRecord.getClass().getDeclaredField(fieldName);
|
field.setAccessible(true);
|
return (LocalDateTime) field.get(lastRecord);
|
} catch (Exception e) {
|
throw new RuntimeException("获取最后同步时间失败", e);
|
}
|
}
|
}
|