| | |
| | | package com.xiaopeng.operation.service.impl; |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import com.alibaba.fastjson.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.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版本管理 服务实现类 |
| | |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor(onConstructor_ = @Lazy) |
| | | 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() { |
| | |
| | | appVersion.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName()); |
| | | 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; |
| | | } |
| | | } |