package com.xiaopeng.operation.service.impl;
|
|
import cn.hutool.core.bean.BeanUtil;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
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.model.account.XpOperationsUser;
|
import com.xiaopeng.api.model.account.XpOperationsUserNotification;
|
import com.xiaopeng.api.model.operation.XpAppVersion;
|
import com.xiaopeng.common.core.domain.R;
|
import com.xiaopeng.common.core.enums.NotificationTypeEnum;
|
import com.xiaopeng.common.core.exception.ServiceException;
|
import com.xiaopeng.common.core.web.page.BasePage;
|
import com.xiaopeng.common.core.web.page.PageInfo;
|
import com.xiaopeng.common.security.utils.SecurityUtils;
|
import com.xiaopeng.operation.mapper.XpAppVersionMapper;
|
import com.xiaopeng.operation.service.IXpAppVersionService;
|
import com.xiaopeng.operation.vo.XpAppVersionVO;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.TimeUnit;
|
import java.util.stream.Collectors;
|
|
/**
|
* App版本管理 服务实现类
|
*
|
* @author mitao
|
* @since 2026-02-14
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class XpAppVersionServiceImpl extends ServiceImpl<XpAppVersionMapper, XpAppVersion> implements IXpAppVersionService {
|
|
private final XpOperationUserClient xpOperationUserClient;
|
|
private final XpOperationUserNotificationClient xpOperationUserNotificationClient;
|
|
private final XpAliPushMessageClient xpAliPushMessageClient;
|
|
@Override
|
public XpAppVersionVO getLatestVersion() {
|
// 查询最新版本(按ID倒序,取第一条)
|
XpAppVersion latestVersion = this.lambdaQuery()
|
.orderByDesc(XpAppVersion::getVersionCode)
|
.last("LIMIT 1")
|
.one();
|
|
if (Objects.isNull(latestVersion)) {
|
log.warn("未找到App版本信息");
|
return null;
|
}
|
|
// 转换为 VO
|
XpAppVersionVO vo = BeanUtil.copyProperties(latestVersion, XpAppVersionVO.class);
|
log.info("获取最新App版本成功, version={}", vo.getVersion());
|
return vo;
|
}
|
|
@Override
|
public PageInfo<XpAppVersion> getPageList(BasePage basePage) {
|
Page<XpAppVersion> page = lambdaQuery().orderByDesc(XpAppVersion::getVersionCode).page(new Page<>(basePage.getPageNum(), basePage.getPageSize()));
|
PageInfo<XpAppVersion> pageInfo = new PageInfo<>();
|
BeanUtil.copyProperties(page, pageInfo);
|
return pageInfo;
|
}
|
|
@Override
|
public void release(XpAppVersion appVersion) {
|
Integer latestVersionCode = 0;
|
//获取最新版本信息
|
XpAppVersion latestVersion = this.lambdaQuery()
|
.orderByDesc(XpAppVersion::getVersionCode)
|
.last("LIMIT 1")
|
.one();
|
|
if (Objects.nonNull(latestVersion)) {
|
latestVersionCode = latestVersion.getVersionCode();
|
}
|
appVersion.setVersionCode(latestVersionCode + 1); // 版本号自增
|
save(appVersion);
|
|
// 异步发送版本更新通知
|
sendVersionUpdateNotification(appVersion);
|
}
|
|
/**
|
* 发送版本更新通知
|
* @param appVersion 版本信息
|
*/
|
private void sendVersionUpdateNotification(XpAppVersion appVersion) {
|
// 异步处理,避免阻塞主流程
|
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 message = "新版本上线啦!请前往更新。";
|
String bodyJson = buildPushBodyJson(message);
|
|
// 3. 构建并保存通知记录
|
List<XpOperationsUserNotification> notifications = enabledUsers.stream()
|
.map(user -> buildNotification(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 buildPushBodyJson(String message) {
|
JSONObject body = new JSONObject();
|
body.put("content", message);
|
body.put("strategy", NotificationTypeEnum.VERSION_UPDATE_TIP.getCode()); // 8
|
body.put("type", 0);
|
body.put("id", 0);
|
return body.toJSONString();
|
}
|
|
/**
|
* 构建通知实体
|
* @param message 消息内容
|
* @param bodyJson 推送body内容
|
* @param user 运维用户
|
* @return 通知实体
|
*/
|
private XpOperationsUserNotification buildNotification(String message, String bodyJson, XpOperationsUser user) {
|
XpOperationsUserNotification notification = new XpOperationsUserNotification();
|
notification.setNotificationType(NotificationTypeEnum.VERSION_UPDATE_TIP.getCode()); // 8
|
notification.setNotificationContent(message);
|
notification.setOperationsUserId(user.getId());
|
notification.setDataId(0L); // 版本更新通知 dataId 设置为 0
|
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);
|
}
|
}
|
}
|
}
|
|
/**
|
* 构建推送消息 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("APP版本更新提示");
|
dto.setBody(buildPushBodyJson(message));
|
dto.setPushType("NOTICE");
|
return dto;
|
}
|
|
/**
|
* 将列表按指定大小分批
|
* @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;
|
}
|
}
|