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;
/**
*
* 根据宜搭表单 ID 获取数据
*
*
* @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 实体类
* @return 对应表单的数据
*/
public static JSONArray getFormDataList(AliDingConfig aliDingConfig, String formUuid, String searchFieldJson, IService service, SFunction 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 LocalDateTime getLastSyncTime(IService service, SFunction timeField) {
LambdaQueryWrapper 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);
}
}
}