|
@@ -8,11 +8,13 @@ import com.alibaba.nacos.shaded.com.google.gson.JsonObject;
|
|
|
import com.alibaba.nacos.shaded.com.google.gson.JsonParser;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
+import org.apache.http.client.config.RequestConfig;
|
|
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
|
|
import org.apache.http.client.methods.HttpGet;
|
|
|
import org.apache.http.client.methods.HttpPost;
|
|
|
import org.apache.http.entity.StringEntity;
|
|
|
import org.apache.http.impl.client.CloseableHttpClient;
|
|
|
+import org.apache.http.impl.client.HttpClientBuilder;
|
|
|
import org.apache.http.impl.client.HttpClients;
|
|
|
import org.apache.http.util.EntityUtils;
|
|
|
import org.springblade.core.log.exception.ServiceException;
|
|
@@ -21,6 +23,7 @@ import org.springblade.dingding.vo.MeetingSchedule;
|
|
|
import org.springblade.dingding.vo.MeetingVo;
|
|
|
import org.springblade.dingding.vo.ScheduleItem;
|
|
|
import org.springframework.http.*;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.web.client.HttpClientErrorException;
|
|
|
import org.springframework.web.client.HttpServerErrorException;
|
|
@@ -37,6 +40,9 @@ import java.time.ZonedDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.time.format.TextStyle;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Service
|
|
@@ -47,97 +53,203 @@ public class MeetingServiceImpl implements MeetingService {
|
|
|
private static final String APP_SECRET = "TctTp-Qmyh9r20bdeHcLWZ8qfxSEP8R29qo53GZH2elWV-yDZMqgqFKEjp5PtlXZ";
|
|
|
private static final String USER_ID = "01141506681633389467";
|
|
|
|
|
|
+ // 缓存用户信息,避免重复调用
|
|
|
+ private final Map<String, Object> userInfoCache = new ConcurrentHashMap<>();
|
|
|
+ private final Map<String, String> departmentCache = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ // 创建带超时配置的HttpClient
|
|
|
+ private CloseableHttpClient createHttpClientWithTimeout() {
|
|
|
+ RequestConfig config = RequestConfig.custom()
|
|
|
+ .setConnectTimeout(10000) // 10秒连接超时
|
|
|
+ .setSocketTimeout(15000) // 15秒socket超时
|
|
|
+ .build();
|
|
|
+
|
|
|
+ return HttpClientBuilder.create()
|
|
|
+ .setDefaultRequestConfig(config)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
|
|
|
@Override
|
|
|
public List<MeetingVo> getMeetingInfo() {
|
|
|
- List<MeetingVo> vos=new ArrayList<>();
|
|
|
- String accessToken = getAccessToken();
|
|
|
- String unionId = (String) getUserInfo(accessToken,"unionid",USER_ID);
|
|
|
- String meetingsJson = getMeetings(accessToken, unionId);
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
try {
|
|
|
- // 解析JSON字符串
|
|
|
- com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(meetingsJson);
|
|
|
- // 获取result数组
|
|
|
+ String accessToken = getAccessToken();
|
|
|
+ String unionId = (String) getUserInfo(accessToken, "unionid", USER_ID);
|
|
|
+ String meetingsJson = getMeetings(accessToken, unionId);
|
|
|
+
|
|
|
+ List<MeetingVo> vos = parseMeetingRooms(meetingsJson);
|
|
|
+ if (vos.isEmpty()) {
|
|
|
+ return vos;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备时间范围参数
|
|
|
+ String[] timeRange = getTodayTimeRange();
|
|
|
+
|
|
|
+ // 并行处理每个会议室
|
|
|
+ List<CompletableFuture<Void>> futures = vos.stream()
|
|
|
+ .map(vo -> processMeetingRoomAsync(vo, accessToken, unionId, timeRange[0], timeRange[1]))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 等待所有完成,设置超时
|
|
|
+ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
|
|
+ .get(25, TimeUnit.SECONDS); // 25秒总超时
|
|
|
+
|
|
|
+ return vos;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new ServiceException("获取会议室信息失败: " + e.getMessage());
|
|
|
+ } finally {
|
|
|
+ long duration = System.currentTimeMillis() - startTime;
|
|
|
+ System.out.println("getMeetingInfo执行耗时: " + duration + "ms");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String[] getTodayTimeRange() {
|
|
|
+ LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIDNIGHT);
|
|
|
+ ZonedDateTime startOfDayZoned = startOfDay.atZone(ZoneId.systemDefault());
|
|
|
+ String startTime = startOfDayZoned.withZoneSameInstant(ZoneId.of("UTC"))
|
|
|
+ .format(DateTimeFormatter.ISO_INSTANT);
|
|
|
+
|
|
|
+ LocalDateTime endOfDay = LocalDateTime.now().with(LocalTime.MAX);
|
|
|
+ ZonedDateTime endOfDayZoned = endOfDay.atZone(ZoneId.systemDefault());
|
|
|
+ String endTime = endOfDayZoned.withZoneSameInstant(ZoneId.of("UTC"))
|
|
|
+ .format(DateTimeFormatter.ISO_INSTANT);
|
|
|
+
|
|
|
+ return new String[]{startTime, endTime};
|
|
|
+ }
|
|
|
+ private List<MeetingVo> parseMeetingRooms(String meetingsJson) {
|
|
|
+ List<MeetingVo> vos = new ArrayList<>();
|
|
|
+ try {
|
|
|
+ JSONObject jsonObject = JSON.parseObject(meetingsJson);
|
|
|
JSONArray resultArray = jsonObject.getJSONArray("result");
|
|
|
- // 遍历数组,提取roomId和roomName
|
|
|
+
|
|
|
for (int i = 0; i < resultArray.size(); i++) {
|
|
|
- MeetingVo vo=new MeetingVo();
|
|
|
JSONObject roomObject = resultArray.getJSONObject(i);
|
|
|
- String roomId = roomObject.getString("roomId");
|
|
|
- String roomName = roomObject.getString("roomName");
|
|
|
- vo.setRoomId(roomId);
|
|
|
- vo.setFixedData(roomName);
|
|
|
+ MeetingVo vo = new MeetingVo();
|
|
|
+ vo.setRoomId(roomObject.getString("roomId"));
|
|
|
+ vo.setFixedData(roomObject.getString("roomName"));
|
|
|
+ vo.setStatus(1); // 默认状态
|
|
|
vos.add(vo);
|
|
|
}
|
|
|
- LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIDNIGHT);
|
|
|
- ZonedDateTime startOfDayZoned = startOfDay.atZone(ZoneId.systemDefault());
|
|
|
- String startTime=startOfDayZoned.withZoneSameInstant(ZoneId.of("UTC"))
|
|
|
- .format(DateTimeFormatter.ISO_INSTANT);
|
|
|
- LocalDateTime endOfDay = LocalDateTime.now().with(LocalTime.MAX);
|
|
|
- ZonedDateTime endOfDayZoned = endOfDay.atZone(ZoneId.systemDefault());
|
|
|
- String endTime= endOfDayZoned.withZoneSameInstant(ZoneId.of("UTC"))
|
|
|
- .format(DateTimeFormatter.ISO_INSTANT);
|
|
|
- for (MeetingVo vo : vos) {
|
|
|
- String[] roomIds = new String[]{vo.getRoomId()};
|
|
|
- String meetingHistory = getMeetingHistory(accessToken, unionId,roomIds,startTime,endTime);
|
|
|
- List<ScheduleItem> scheduleItems = parseScheduleItemsWithStream(meetingHistory);
|
|
|
- List<MeetingSchedule>list=new ArrayList<>();
|
|
|
- if(!scheduleItems.isEmpty()){
|
|
|
- //循环查出预定的会议是否正在进行
|
|
|
- for (ScheduleItem item : scheduleItems) {
|
|
|
- String stringStartDateTime = extractTimeFromDateTime(item.getStartDateTime());
|
|
|
- String stringEndDateTime = extractTimeFromDateTime(item.getEndDateTime());
|
|
|
- //正在进行的会议
|
|
|
- if(isCurrentTimeInRange(item.getStartDateTime(),item.getEndDateTime())){
|
|
|
- try {
|
|
|
- vo.setStatus(2);
|
|
|
- String meetingList = getMeetingList(item.getOrganizerId(), accessToken);
|
|
|
- JSONObject meetingListjsonObject = JSON.parseObject(meetingList);
|
|
|
- JSONArray onGoingConfIdList = meetingListjsonObject.getJSONArray("onGoingConfIdList");
|
|
|
- String tatil = getMeetingDetail(accessToken, onGoingConfIdList.getString(0));
|
|
|
- String userID = getUserID(accessToken, item.getOrganizerId());
|
|
|
- Object o = getUserInfo(accessToken, "dept_id_list", userID);
|
|
|
- List<String> deptIdList = JSON.parseArray(o.toString(), String.class);
|
|
|
- String departmentInfo = getDepartmentInfo(accessToken, deptIdList.get(0));
|
|
|
- vo.setMeetingTheme(tatil);
|
|
|
- vo.setMeetingDept(departmentInfo);
|
|
|
- String name = (String)getUserInfo(accessToken, "name", userID);
|
|
|
- vo.setMeetingBooker(name);
|
|
|
- vo.setMeetingTime(formatDateTimeRangeWithFormatter(item.getStartDateTime(),item.getEndDateTime()));
|
|
|
- String allUser = getAllUser(onGoingConfIdList.getString(0), accessToken);
|
|
|
- vo.setParticipants(allUser);
|
|
|
- break;
|
|
|
- }catch (Exception e){
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- //今日预约的会议
|
|
|
- MeetingSchedule schedule = new MeetingSchedule();
|
|
|
- String userID = getUserID(accessToken, item.getOrganizerId());
|
|
|
- Object o = getUserInfo(accessToken, "dept_id_list", userID);
|
|
|
- List<String> deptIdList = JSON.parseArray(o.toString(), String.class);
|
|
|
- String departmentInfo = getDepartmentInfo(accessToken, deptIdList.get(0));
|
|
|
- schedule.setReservationTime(stringStartDateTime+"~"+stringEndDateTime);
|
|
|
- schedule.setReservationDept(departmentInfo);
|
|
|
- vo.setStatus(1);
|
|
|
- list.add(schedule);
|
|
|
- }
|
|
|
- }else {
|
|
|
- vo.setStatus(1);
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("解析会议室列表失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return vos;
|
|
|
+ }
|
|
|
+ @Async
|
|
|
+ protected CompletableFuture<Void> processMeetingRoomAsync(MeetingVo vo, String accessToken,
|
|
|
+ String unionId, String startTime, String endTime) {
|
|
|
+ return CompletableFuture.runAsync(() -> {
|
|
|
+ try {
|
|
|
+ processMeetingRoom(vo, accessToken, unionId, startTime, endTime);
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("处理会议室 " + vo.getFixedData() + " 失败: " + e.getMessage());
|
|
|
+ // 设置默认值,避免影响其他会议室
|
|
|
+ vo.setStatus(1);
|
|
|
+ vo.setMeetings(new ArrayList<>());
|
|
|
+ vo.setMeetingSession(0);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ private void processMeetingRoom(MeetingVo vo, String accessToken, String unionId,
|
|
|
+ String startTime, String endTime) {
|
|
|
+ String[] roomIds = new String[]{vo.getRoomId()};
|
|
|
+ String meetingHistory = getMeetingHistory(accessToken, unionId, roomIds, startTime, endTime);
|
|
|
+ List<ScheduleItem> scheduleItems = parseScheduleItemsWithStream(meetingHistory);
|
|
|
+
|
|
|
+ List<MeetingSchedule> meetings = new ArrayList<>();
|
|
|
+ boolean hasOngoingMeeting = false;
|
|
|
+
|
|
|
+ for (ScheduleItem item : scheduleItems) {
|
|
|
+ if (hasOngoingMeeting) {
|
|
|
+ break; // 如果已经有进行中的会议,跳过后续处理
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isCurrentTimeInRange(item.getStartDateTime(), item.getEndDateTime())) {
|
|
|
+ // 处理进行中的会议
|
|
|
+ processOngoingMeeting(vo, item, accessToken);
|
|
|
+ hasOngoingMeeting = true;
|
|
|
+ } else {
|
|
|
+ // 处理预约的会议
|
|
|
+ MeetingSchedule schedule = processScheduledMeeting(item, accessToken);
|
|
|
+ if (schedule != null) {
|
|
|
+ meetings.add(schedule);
|
|
|
}
|
|
|
- vo.setMeetingSession(list.size());
|
|
|
- vo.setMeetings(list);
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setMeetings(meetings);
|
|
|
+ vo.setMeetingSession(meetings.size());
|
|
|
+ if (!hasOngoingMeeting) {
|
|
|
+ vo.setStatus(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void processOngoingMeeting(MeetingVo vo, ScheduleItem item, String accessToken) {
|
|
|
+ try {
|
|
|
+ vo.setStatus(2);
|
|
|
+
|
|
|
+ String meetingList = getMeetingList(item.getOrganizerId(), accessToken);
|
|
|
+ JSONObject meetingListJson = JSON.parseObject(meetingList);
|
|
|
+ JSONArray onGoingConfIdList = meetingListJson.getJSONArray("onGoingConfIdList");
|
|
|
+
|
|
|
+ if (onGoingConfIdList != null && !onGoingConfIdList.isEmpty()) {
|
|
|
+ String conferenceId = onGoingConfIdList.getString(0);
|
|
|
+ String meetingTitle = getMeetingDetail(accessToken, conferenceId);
|
|
|
+ vo.setMeetingTheme(meetingTitle);
|
|
|
+
|
|
|
+ String allUsers = getAllUser(conferenceId, accessToken);
|
|
|
+ vo.setParticipants(allUsers);
|
|
|
+ }
|
|
|
+
|
|
|
+ String userId = getUserID(accessToken, item.getOrganizerId());
|
|
|
+ String userName = (String) getUserInfo(accessToken, "name", userId);
|
|
|
+ vo.setMeetingBooker(userName);
|
|
|
+
|
|
|
+ Object deptInfo = getUserInfo(accessToken, "dept_id_list", userId);
|
|
|
+ if (deptInfo != null) {
|
|
|
+ List<String> deptIdList = JSON.parseArray(deptInfo.toString(), String.class);
|
|
|
+ if (!deptIdList.isEmpty()) {
|
|
|
+ String departmentName = getDepartmentInfo(accessToken, deptIdList.get(0));
|
|
|
+ vo.setMeetingDept(departmentName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setMeetingTime(formatDateTimeRangeWithFormatter(item.getStartDateTime(), item.getEndDateTime()));
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
+ System.err.println("处理进行中会议失败: " + e.getMessage());
|
|
|
+ vo.setStatus(1); // 出错时回退到预约状态
|
|
|
}
|
|
|
- return vos;
|
|
|
}
|
|
|
+ private MeetingSchedule processScheduledMeeting(ScheduleItem item, String accessToken) {
|
|
|
+ try {
|
|
|
+ String startTime = extractTimeFromDateTime(item.getStartDateTime());
|
|
|
+ String endTime = extractTimeFromDateTime(item.getEndDateTime());
|
|
|
+
|
|
|
+ String userId = getUserID(accessToken, item.getOrganizerId());
|
|
|
+ Object deptInfo = getUserInfo(accessToken, "dept_id_list", userId);
|
|
|
|
|
|
- public String getAllUser(String CONFERENCE_ID,String ACCESS_TOKEN){
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
- try {
|
|
|
+ if (deptInfo != null) {
|
|
|
+ List<String> deptIdList = JSON.parseArray(deptInfo.toString(), String.class);
|
|
|
+ if (!deptIdList.isEmpty()) {
|
|
|
+ String departmentName = getDepartmentInfo(accessToken, deptIdList.get(0));
|
|
|
+
|
|
|
+ MeetingSchedule schedule = new MeetingSchedule();
|
|
|
+ schedule.setReservationTime(startTime + "~" + endTime);
|
|
|
+ schedule.setReservationDept(departmentName);
|
|
|
+ return schedule;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("处理预约会议失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getAllUser(String CONFERENCE_ID,String ACCESS_TOKEN){
|
|
|
+ try(CloseableHttpClient httpClient = createHttpClientWithTimeout()) {
|
|
|
// 构建请求 URL
|
|
|
StringBuilder urlBuilder = new StringBuilder("https://api.dingtalk.com/v1.0/conference/videoConferences/")
|
|
|
.append(CONFERENCE_ID)
|
|
@@ -221,8 +333,7 @@ public class MeetingServiceImpl implements MeetingService {
|
|
|
}
|
|
|
|
|
|
public String getMeetingList(String UNION_ID, String ACCESS_TOKEN){
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
- try {
|
|
|
+ try(CloseableHttpClient httpClient = createHttpClientWithTimeout()) {
|
|
|
// 构建请求 URL
|
|
|
String url = "https://api.dingtalk.com/v1.0/conference/users/lists?unionId=" + UNION_ID;
|
|
|
HttpGet httpGet = new HttpGet(url);
|
|
@@ -248,8 +359,7 @@ public class MeetingServiceImpl implements MeetingService {
|
|
|
}
|
|
|
|
|
|
public String getMeetingDetail(String ACCESS_TOKEN,String CONFERENCE_ID){
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
- try {
|
|
|
+ try(CloseableHttpClient httpClient = createHttpClientWithTimeout()) {
|
|
|
// 构建请求 URL,包含会议 ID
|
|
|
String url = "https://api.dingtalk.com/v1.0/conference/videoConferences/" + CONFERENCE_ID;
|
|
|
HttpGet httpGet = new HttpGet(url);
|
|
@@ -278,8 +388,7 @@ public class MeetingServiceImpl implements MeetingService {
|
|
|
}
|
|
|
|
|
|
public String getUserID(String ACCESS_TOKEN,String UNION_ID) throws Exception {
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
- try {
|
|
|
+ try(CloseableHttpClient httpClient = createHttpClientWithTimeout()) {
|
|
|
// 构建请求 URL,包含 access_token 和 unionid 参数
|
|
|
String url = "https://oapi.dingtalk.com/user/getUseridByUnionid?access_token=" + ACCESS_TOKEN + "&unionid=" + UNION_ID;
|
|
|
HttpGet httpGet = new HttpGet(url);
|
|
@@ -332,7 +441,7 @@ public class MeetingServiceImpl implements MeetingService {
|
|
|
* 查询部门信息
|
|
|
*/
|
|
|
public String getDepartmentInfo(String accessToken, String deptId) throws Exception {
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
+ CloseableHttpClient httpClient = createHttpClientWithTimeout();
|
|
|
String url = "https://oapi.dingtalk.com/topapi/v2/department/get?access_token=" + accessToken;
|
|
|
|
|
|
HttpPost httpPost = new HttpPost(url);
|