package com.xiaopeng.maintenance.service.impl;
|
|
import cn.hutool.core.collection.CollUtil;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.xiaopeng.api.dto.pushMessage.AliPushMessageDTO;
|
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
|
import com.xiaopeng.api.feignClient.account.XpOperationUserNotificationClient;
|
import com.xiaopeng.api.feignClient.pushMessage.XpAliPushMessageClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient;
|
import com.xiaopeng.api.model.account.SysRole;
|
import com.xiaopeng.api.model.account.XpOperationsRole;import com.xiaopeng.api.model.account.XpOperationsUser;
|
import com.xiaopeng.api.model.account.XpOperationsUserNotification;
|
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
|
import com.xiaopeng.api.model.maintenance.XpClockIn;
|
import com.xiaopeng.api.model.maintenance.XpSchedulingDetail;
|
import com.xiaopeng.api.model.maintenance.XpShift;
|
import com.xiaopeng.api.vo.account.XpOperationsUserVO;
|
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;import com.xiaopeng.common.core.constant.Constants;
|
import com.xiaopeng.common.core.domain.R;
|
import com.xiaopeng.common.core.enums.NotificationTypeEnum;
|
import com.xiaopeng.common.core.utils.DateUtils;
|
import com.xiaopeng.common.core.utils.GeodesyUtil;
|
import com.xiaopeng.common.core.utils.StringUtils;
|
import com.xiaopeng.common.core.utils.poi.ExcelUtil;
|
import com.xiaopeng.common.core.web.page.PageInfo;
|
import com.xiaopeng.maintenance.dto.*;
|
import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper;
|
import com.xiaopeng.maintenance.mapper.XpClockInMapper;
|
import com.xiaopeng.maintenance.mapper.XpShiftMapper;
|
import com.xiaopeng.maintenance.service.IXpClockInService;
|
import com.xiaopeng.maintenance.service.IXpSchedulingDetailService;
|
import com.xiaopeng.maintenance.vo.ClockInStatisticsDetailsVo;
|
import com.xiaopeng.maintenance.vo.ClockInStatisticsVo;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import javax.annotation.Resource;
|
import javax.servlet.http.HttpServletResponse;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.util.List;
|
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.TimeUnit;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
/**
|
* <p>
|
* 打卡明细 服务实现类
|
* </p>
|
*
|
* @author mitao
|
* @since 2026-02-03
|
*/
|
@Slf4j
|
@Service
|
public class XpClockInServiceImpl extends ServiceImpl<XpClockInMapper, XpClockIn> implements IXpClockInService {
|
|
@Resource
|
private XpCheckLocationMapper xpCheckLocationMapper;
|
@Resource
|
private XpShiftMapper xpShiftMapper;
|
@Resource
|
private XpClockInMapper xpClockInMapper;
|
|
@Resource
|
private XpOperationUserClient xpOperationUserClient;
|
|
@Resource
|
private IXpSchedulingDetailService xpSchedulingDetailService;
|
|
@Resource
|
private XpFleetOperationsClient fleetOperationsClient;
|
@Resource
|
private XpOperationUserNotificationClient xpOperationUserNotificationClient;
|
@Resource
|
private XpAliPushMessageClient xpAliPushMessageClient;
|
|
|
|
|
|
@Override
|
public Boolean verifyRange(VerifyRangeDTO dto) {
|
|
XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
|
|
Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat());
|
if (null != distance) {
|
Double distance1 = distance.get("WGS84");
|
if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) {
|
return false;
|
} else {
|
return true;
|
}
|
} else {
|
log.error("调用高德计算距离出错");
|
return false;
|
}
|
}
|
|
@Override
|
public R<String> clockIn(ClockDTO dto) {
|
|
XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
|
if (null == xpCheckLocation) {
|
return R.fail("打卡地址不存在");
|
}
|
if (xpCheckLocation.getStatus() == 0) {
|
return R.fail("打卡地址已禁用");
|
}
|
|
Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat());
|
if (null != distance) {
|
Double distance1 = distance.get("WGS84");
|
if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) {
|
return R.fail("不在打卡范围内");
|
}
|
} else {
|
return R.fail("打卡失败");
|
}
|
|
// 查询班次
|
XpShift xpShift = xpShiftMapper.selectById(dto.getShiftId());
|
if (null == xpShift) {
|
return R.fail("班次不存在");
|
}
|
// 开始打卡时间
|
String startTime = xpShift.getStartTime();
|
LocalDateTime startLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + startTime + ":00");
|
|
XpClockIn xpClockIn = new XpClockIn();
|
xpClockIn.setAppUserId(dto.getOperationsUserId());
|
LocalDateTime now = LocalDateTime.now();
|
xpClockIn.setClockInTime(now);
|
xpClockIn.setClockInLon(dto.getUserLon());
|
xpClockIn.setClockInLat(dto.getUserLat());
|
xpClockIn.setShiftId(dto.getShiftId());
|
xpClockIn.setClockInAddress(dto.getClockInAddress());
|
if (xpClockIn.getClockInTime().isAfter(startLocalDateTime)) {
|
xpClockIn.setClockInResult(2);
|
} else {
|
xpClockIn.setClockInResult(1);
|
}
|
this.save(xpClockIn);
|
sendAgreementUpdateNotification(now);
|
return R.ok();
|
}
|
|
@Override
|
public R<String> clockOut(ClockDTO dto) {
|
|
XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
|
if (null == xpCheckLocation) {
|
return R.fail("打卡地址不存在");
|
}
|
if (xpCheckLocation.getStatus() == 0) {
|
return R.fail("打卡地址已禁用");
|
}
|
Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat());
|
if (null != distance) {
|
Double distance1 = distance.get("WGS84");
|
if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) {
|
return R.fail("不在打卡范围内");
|
}
|
} else {
|
return R.fail("打卡失败");
|
}
|
|
// 查询上班打卡信息
|
XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class)
|
.eq(XpClockIn::getAppUserId, dto.getOperationsUserId())
|
.likeRight(XpClockIn::getClockInTime, LocalDate.now())
|
.last("LIMIT 1"));
|
if (Objects.isNull(xpClockIn)) {
|
return R.fail("请先上班打卡");
|
}
|
|
// 查询班次
|
XpShift xpShift = xpShiftMapper.selectById(xpClockIn.getShiftId());
|
if (null == xpShift) {
|
return R.fail("班次不存在");
|
}
|
// 开始打卡时间
|
String endTime = xpShift.getEndTime();
|
LocalDateTime endLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + endTime + ":00");
|
|
LocalDateTime now = LocalDateTime.now();
|
xpClockIn.setClockOutTime(now);
|
xpClockIn.setClockOutLon(dto.getUserLon());
|
xpClockIn.setClockOutLat(dto.getUserLat());
|
xpClockIn.setClockOutAddress(dto.getClockOutAddress());
|
if (xpClockIn.getClockOutTime().isBefore(endLocalDateTime)) {
|
xpClockIn.setClockOutResult(2);
|
} else {
|
xpClockIn.setClockOutResult(1);
|
}
|
xpClockIn.setAppUserId(null);
|
this.updateById(xpClockIn);
|
sendAgreementUpdateNotification(now);
|
return R.ok();
|
}
|
|
@Override
|
public Integer checkWorkStatus(Long appUserId) {
|
if (appUserId == null) {
|
log.warn("检查上班状态失败: appUserId 为空");
|
return 1;
|
}
|
|
try {
|
// 1. 计算当天时间范围
|
LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); // 00:00:00
|
LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX); // 23:59:59
|
|
// 2. 查询当天打卡记录
|
XpClockIn xpClockIn = baseMapper.selectOne(new LambdaQueryWrapper<XpClockIn>()
|
.eq(XpClockIn::getAppUserId, appUserId)
|
.between(XpClockIn::getClockInTime, startTime, endTime)
|
.last("limit 1"));
|
// 3. 判断上班状态
|
// 有上班打卡状态,无下班打卡状态
|
if (Objects.isNull(xpClockIn)) {
|
return 1;
|
} else if (Objects.nonNull(xpClockIn.getClockInResult()) && Objects.isNull(xpClockIn.getClockOutResult())) {
|
return 2;
|
} else {
|
return 3;
|
}
|
} catch (Exception e) {
|
log.error("检查用户上班状态异常, appUserId={}", appUserId, e);
|
// 异常情况返回 false
|
return 1;
|
}
|
}
|
|
|
/**
|
* 获取打卡统计
|
*
|
* @param query
|
* @return
|
*/
|
@Override
|
public PageInfo<ClockInStatisticsDto> getClockInStatistics(ClockInStatisticsVo query) {
|
PageInfo<ClockInStatisticsDto> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
List<Long> appUserIds1 = new ArrayList<>();
|
List<Long> appUserIds2 = new ArrayList<>();
|
List<Long> appUserIds3 = new ArrayList<>();
|
//处理查询条件
|
if(StringUtils.isNotEmpty(query.getName())){
|
R<List<XpOperationsUser>> listR = xpOperationUserClient.listByName(query.getName());
|
if(listR.getCode() == Constants.SUCCESS){
|
List<Long> collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList());
|
appUserIds1.addAll(collect);
|
}
|
appUserIds1.add(0L);
|
}
|
if(null != query.getRoleId()){
|
R<List<XpOperationsRole>> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId());
|
if(roleByRoleId.getCode() == Constants.SUCCESS){
|
List<Long> collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList());
|
appUserIds2.addAll(collect);
|
}
|
appUserIds2.add(0L);
|
}
|
if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){
|
for (Long l : appUserIds1) {
|
if(appUserIds2.contains(l)){
|
appUserIds3.add(l);
|
}
|
}
|
}else{
|
appUserIds3.addAll(appUserIds1);
|
appUserIds3.addAll(appUserIds2);
|
}
|
|
String startTime = "";
|
String endTime = "";
|
if(StringUtils.isNotEmpty(query.getStatisticDate())){
|
String statisticDate = query.getStatisticDate();
|
startTime = statisticDate.split(" - ")[0].split(" ")[0];
|
endTime = statisticDate.split(" - ")[1].split(" ")[0];
|
}
|
//查询数据
|
List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime);
|
List<String> appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList());
|
List<Long> ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList());
|
List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(ids).getData();
|
Map<Long, XpOperationsUserWithRoleVO> userMap = data.stream().collect(Collectors.toMap(XpOperationsUserWithRoleVO::getId, Function.identity()));
|
//将查询结果组装成map对象,用于循环中提取数据
|
for (ClockInStatisticsDto clockInStatistic : clockInStatistics) {
|
String appUserId = clockInStatistic.getAppUserId();
|
//运维账号可以拥有多个角色
|
if(CollUtil.isNotEmpty(userMap)){
|
XpOperationsUserWithRoleVO vo = userMap.get(Long.parseLong(appUserId));
|
if (Objects.nonNull(vo)) {
|
clockInStatistic.setName(vo.getNickName());
|
String roleName = vo.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";"));
|
clockInStatistic.setRoleName(roleName);
|
}
|
}
|
}
|
pageInfo.setRecords(clockInStatistics);
|
return pageInfo;
|
}
|
|
|
/**
|
* 导出打卡统计
|
* @param query
|
* @param response
|
*/
|
@Override
|
public void exportClockInStatistics(ClockInStatisticsVo query, HttpServletResponse response) {
|
PageInfo<ClockInStatisticsDto> pageInfo = new PageInfo<>(0, 10000);
|
List<Long> appUserIds1 = new ArrayList<>();
|
List<Long> appUserIds2 = new ArrayList<>();
|
List<Long> appUserIds3 = new ArrayList<>();
|
//处理查询条件
|
if(StringUtils.isNotEmpty(query.getName())){
|
R<List<XpOperationsUser>> listR = xpOperationUserClient.listByName(query.getName());
|
if(listR.getCode() == Constants.SUCCESS){
|
List<Long> collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList());
|
appUserIds1.addAll(collect);
|
}
|
appUserIds1.add(0L);
|
}
|
if(null != query.getRoleId()){
|
R<List<XpOperationsRole>> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId());
|
if(roleByRoleId.getCode() == Constants.SUCCESS){
|
List<Long> collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList());
|
appUserIds2.addAll(collect);
|
}
|
appUserIds2.add(0L);
|
}
|
if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){
|
for (Long l : appUserIds1) {
|
if(appUserIds2.contains(l)){
|
appUserIds3.add(l);
|
}
|
}
|
}else{
|
appUserIds3.addAll(appUserIds1);
|
appUserIds3.addAll(appUserIds2);
|
}
|
String startTime = "";
|
String endTime = "";
|
if(StringUtils.isNotEmpty(query.getStatisticDate())){
|
String statisticDate = query.getStatisticDate();
|
startTime = statisticDate.split(" - ")[0].split(" ")[0];
|
endTime = statisticDate.split(" - ")[1].split(" ")[0];
|
}
|
//查询数据
|
List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime);
|
List<String> appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList());
|
List<Long> ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList());
|
List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(ids).getData();
|
Map<Long, XpOperationsUserWithRoleVO> userMap = data.stream().collect(Collectors.toMap(XpOperationsUserWithRoleVO::getId, Function.identity()));
|
for (ClockInStatisticsDto clockInStatistic : clockInStatistics) {
|
String appUserId = clockInStatistic.getAppUserId();
|
//运维账号可以拥有多个角色
|
if(CollUtil.isNotEmpty(userMap)){
|
XpOperationsUserWithRoleVO vo = userMap.get(Long.parseLong(appUserId));
|
if (Objects.nonNull(vo)) {
|
clockInStatistic.setName(vo.getNickName());
|
String roleName = vo.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";"));
|
clockInStatistic.setRoleName(roleName);
|
}
|
}
|
}
|
|
ExcelUtil<ClockInStatisticsDto> excel = new ExcelUtil<ClockInStatisticsDto>(ClockInStatisticsDto.class);
|
excel.exportExcel(response, clockInStatistics, "打卡统计");
|
}
|
|
|
|
|
|
/**
|
* 获取打卡统计明细
|
* @param query
|
* @return
|
*/
|
@Override
|
public GetClockInStatisticsDetailsDto getClockInStatisticsDetails(ClockInStatisticsDetailsVo query) {
|
GetClockInStatisticsDetailsDto dto = new GetClockInStatisticsDetailsDto();
|
PageInfo<ClockInStatisticsDetailsDto> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
|
String startTime = "";
|
String endTime = "";
|
if(StringUtils.isNotEmpty(query.getStatisticDate())){
|
String statisticDate = query.getStatisticDate();
|
startTime = statisticDate.split(" - ")[0].split(" ")[0];
|
endTime = statisticDate.split(" - ")[1].split(" ")[0];
|
}
|
List<ClockInStatisticsDetailsDto> clockInStatisticsDetails = this.baseMapper.getClockInStatisticsDetails(pageInfo, query.getAppUserId(), startTime, endTime);
|
List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(Arrays.asList(query.getAppUserId())).getData();
|
XpOperationsUserWithRoleVO xpOperationsUserWithRoleVO = data.get(0);
|
//将查询结果组装成map对象,用于循环中提取数据
|
for (ClockInStatisticsDetailsDto clockInStatistic : clockInStatisticsDetails) {
|
clockInStatistic.setName(xpOperationsUserWithRoleVO.getNickName());
|
clockInStatistic.setRoleName(xpOperationsUserWithRoleVO.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";")));
|
}
|
pageInfo.setRecords(clockInStatisticsDetails);
|
dto.setList(pageInfo);
|
XpOperationsUser xpOperationsUser = xpOperationUserClient.queryById(query.getAppUserId()).getData();
|
List<Long> longList = fleetOperationsClient.getFleetIdsByUserId(xpOperationsUser.getId()).getData();
|
List<XpSchedulingDetail> list = xpSchedulingDetailService.list(new QueryWrapper<XpSchedulingDetail>().eq("fleet_id", longList.get(0)).between(StringUtils.isNotEmpty(startTime), "plan_time", startTime, endTime));
|
dto.setShouldClockInCount(list.size() * 2);
|
dto.setNormalClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(1, query.getAppUserId(), startTime, endTime));
|
dto.setLateClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(2, query.getAppUserId(), startTime, endTime));
|
dto.setMissingClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(3, query.getAppUserId(), startTime, endTime));
|
return dto;
|
}
|
|
|
/**
|
* 导出打卡统计明细
|
* @param query
|
* @param response
|
*/
|
@Override
|
public void exportClockInStatisticsDetails(ClockInStatisticsDetailsVo query, HttpServletResponse response) {
|
PageInfo<ClockInStatisticsDetailsDto> pageInfo = new PageInfo<>(0, 10000);
|
|
String startTime = "";
|
String endTime = "";
|
if(StringUtils.isNotEmpty(query.getStatisticDate())){
|
String statisticDate = query.getStatisticDate();
|
startTime = statisticDate.split(" - ")[0].split(" ")[0];
|
endTime = statisticDate.split(" - ")[1].split(" ")[0];
|
}
|
List<ClockInStatisticsDetailsDto> clockInStatisticsDetails = this.baseMapper.getClockInStatisticsDetails(pageInfo, query.getAppUserId(), startTime, endTime);
|
List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(Arrays.asList(query.getAppUserId())).getData();
|
XpOperationsUserWithRoleVO xpOperationsUserWithRoleVO = data.get(0);
|
//将查询结果组装成map对象,用于循环中提取数据
|
for (ClockInStatisticsDetailsDto clockInStatistic : clockInStatisticsDetails) {
|
clockInStatistic.setName(xpOperationsUserWithRoleVO.getNickName());
|
clockInStatistic.setRoleName(xpOperationsUserWithRoleVO.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";")));
|
}
|
|
ExcelUtil<ClockInStatisticsDetailsDto> excel = new ExcelUtil<ClockInStatisticsDetailsDto>(ClockInStatisticsDetailsDto.class);
|
excel.exportExcel(response, clockInStatisticsDetails, "打卡统计明细");
|
}
|
|
|
/**
|
* 发送协议更新通知
|
* @param clockTime 打卡时间
|
*/
|
private void sendAgreementUpdateNotification(LocalDateTime clockTime) {
|
// 异步处理,避免阻塞主流程
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
1, 1, 0L, TimeUnit.MILLISECONDS,
|
new LinkedBlockingQueue<Runnable>()
|
);
|
|
executor.execute(() -> {
|
try {
|
// 1. 获取所有启用状态的运维用户
|
R<List<XpOperationsUser>> result = xpOperationUserClient.listAllEnabled();
|
if (result == null || result.getCode() != 200 || result.getData() == null || result.getData().isEmpty()) {
|
log.warn("没有需要通知的启用状态用户");
|
return;
|
}
|
|
List<XpOperationsUser> enabledUsers = result.getData();
|
log.info("打卡成功通知,共需通知 {} 位用户", enabledUsers.size());
|
|
// 2. 构建通知内容
|
String format = clockTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
String message = String.format("你已于%s打卡成功!", format);
|
String bodyJson = buildAgreementPushBodyJson(message);
|
|
// 3. 构建并保存通知记录
|
List<XpOperationsUserNotification> notifications = enabledUsers.stream()
|
.map(user -> buildAgreementNotification(message, bodyJson, user))
|
.collect(Collectors.toList());
|
|
// 批量保存通知记录
|
xpOperationUserNotificationClient.addNotificationBatch(notifications);
|
log.info("协议更新通知记录保存成功,共 {} 条", notifications.size());
|
|
// 4. 按设备类型分组并批量推送
|
pushMessageByDeviceType(enabledUsers, message);
|
|
} catch (Exception e) {
|
log.error("发送协议更新通知失败", e);
|
}
|
});
|
|
executor.shutdown();
|
}
|
|
/**
|
* 构建推送 body JSON
|
* @param message 消息内容
|
* @return JSON 字符串
|
*/
|
private String buildAgreementPushBodyJson(String message) {
|
JSONObject body = new JSONObject();
|
body.put("content", message);
|
body.put("strategy", NotificationTypeEnum.CLOCK_IN_SUCCESS.getCode()); // 11
|
body.put("type", 0);
|
body.put("id", 0);
|
return body.toJSONString();
|
}
|
|
/**
|
* 构建通知实体
|
* @param message 消息内容
|
* @param bodyJson 推送body内容
|
* @param user 运维用户
|
* @return 通知实体
|
*/
|
private XpOperationsUserNotification buildAgreementNotification(String message, String bodyJson, XpOperationsUser user) {
|
XpOperationsUserNotification notification = new XpOperationsUserNotification();
|
notification.setNotificationType(NotificationTypeEnum.CLOCK_IN_SUCCESS.getCode()); // 11
|
notification.setNotificationContent(message);
|
notification.setOperationsUserId(user.getId());
|
notification.setDataId(0L);
|
notification.setReadStatus(0); // 未读
|
notification.setNotificationExt(bodyJson);
|
return notification;
|
}
|
|
/**
|
* 按设备类型分组推送消息
|
* @param users 用户列表
|
* @param message 消息内容
|
*/
|
private void pushMessageByDeviceType(List<XpOperationsUser> users, String message) {
|
// 按设备类型分组 (1=Android, 2=iOS)
|
Map<Integer, List<XpOperationsUser>> usersByDeviceType = users.stream()
|
.filter(u -> u.getDeviceType() != null && u.getPhoneNumber() != null)
|
.collect(Collectors.groupingBy(XpOperationsUser::getDeviceType));
|
|
// 分别推送 Android 和 iOS
|
for (Map.Entry<Integer, List<XpOperationsUser>> entry : usersByDeviceType.entrySet()) {
|
Integer deviceType = entry.getKey();
|
List<XpOperationsUser> deviceUsers = entry.getValue();
|
|
// 按 1000 个手机号一批进行推送
|
List<List<XpOperationsUser>> batches = partition(deviceUsers, 1000);
|
|
for (List<XpOperationsUser> batch : batches) {
|
String accounts = batch.stream()
|
.map(XpOperationsUser::getPhoneNumber)
|
.collect(Collectors.joining(","));
|
|
AliPushMessageDTO pushDTO = buildPushMessageDTO(deviceType, accounts, message);
|
|
try {
|
xpAliPushMessageClient.pushMessage(pushDTO);
|
log.info("打卡成功推送成功,设备类型={},用户数={}",
|
deviceType == 1 ? "Android" : "iOS", batch.size());
|
} catch (Exception e) {
|
log.error("打卡成功推送失败,设备类型={}", deviceType, e);
|
}
|
}
|
}
|
}
|
|
|
/**
|
* 将列表按指定大小分批
|
* @param list 原始列表
|
* @param batchSize 批次大小
|
* @return 分批后的列表
|
*/
|
private <T> List<List<T>> partition(List<T> list, int batchSize) {
|
List<List<T>> batches = new ArrayList<>();
|
for (int i = 0; i < list.size(); i += batchSize) {
|
batches.add(list.subList(i, Math.min(i + batchSize, list.size())));
|
}
|
return batches;
|
}
|
|
/**
|
* 构建推送消息 DTO
|
* @param deviceType 设备类型
|
* @param accounts 手机号(逗号分隔)
|
* @param message 消息内容
|
* @return 推送 DTO
|
*/
|
private AliPushMessageDTO buildPushMessageDTO(Integer deviceType, String accounts, String message) {
|
AliPushMessageDTO dto = new AliPushMessageDTO();
|
dto.setDeviceType(deviceType);
|
dto.setAccount(accounts);
|
dto.setTitle("打卡成功通知");
|
dto.setBody(buildAgreementPushBodyJson(message));
|
dto.setPushType("NOTICE");
|
return dto;
|
}
|
|
}
|