gongchunyi
5 小时以前 ef7458a6eb8ad72f45b65a4e417a9d04f5015246
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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);
        }
    }
}