From 9bc37286b7cce5c43f8c58d143fdb9e435577e70 Mon Sep 17 00:00:00 2001 From: TongTongStudio Date: Fri, 21 Aug 2026 17:12:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E6=96=B0=E5=A2=9E=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E6=8B=8D=E7=85=A7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增设备端与家属端远程拍照接口 - 新增截图实体字段(原始文件名、MIME类型、扩展名) - 补充截图上传信息记录 - 提取设备操作指令常量 --- sql/sn_screenshot.sql | 31 +++ .../controller/ClientContactController.java | 182 ++++++++++++++++++ .../controller/ClientDeviceOpsController.java | 18 ++ .../controller/DeviceOpsController.java | 8 + .../device/controller/MobileController.java | 5 + .../device/model/entity/SnCameraPhoto.java | 72 +++++++ .../device/model/entity/SnScreenshot.java | 20 ++ .../boot/device/model/vo/ScreenshotVO.java | 9 + .../boot/device/service/DeviceService.java | 1 + .../service/impl/DeviceServiceImpl.java | 53 +++-- 10 files changed, 388 insertions(+), 11 deletions(-) create mode 100644 sql/sn_screenshot.sql create mode 100644 src/main/java/com/youlai/boot/client/controller/ClientContactController.java create mode 100644 src/main/java/com/youlai/boot/device/model/entity/SnCameraPhoto.java diff --git a/sql/sn_screenshot.sql b/sql/sn_screenshot.sql new file mode 100644 index 00000000..c8db844a --- /dev/null +++ b/sql/sn_screenshot.sql @@ -0,0 +1,31 @@ +-- ============================================================================= +-- 设备截图表(sys_sn_screenshot) +-- 说明:设备【截图】记录表,存储设备上报的截图文件信息。 +-- ============================================================================= +CREATE TABLE IF NOT EXISTS `sys_sn_screenshot` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `sn` VARCHAR(64) NOT NULL COMMENT '设备序列号', + `file_name` VARCHAR(255) DEFAULT NULL COMMENT '文件名称', + `origin_name` VARCHAR(255) DEFAULT NULL COMMENT '原始文件名(上传时的原始文件名)', + `mime_type` VARCHAR(64) DEFAULT NULL COMMENT '文件MIME类型', + `file_extension` VARCHAR(32) DEFAULT NULL COMMENT '文件扩展名', + `file_path` VARCHAR(500) DEFAULT NULL COMMENT '文件路径', + `file_size` BIGINT DEFAULT NULL COMMENT '文件大小(字节)', + `file_md5` VARCHAR(64) DEFAULT NULL COMMENT '文件MD5值', + `file_sha1` VARCHAR(64) DEFAULT NULL COMMENT '文件Sha1值', + `file_sha256` VARCHAR(64) DEFAULT NULL COMMENT '文件Sha256值', + `upload_time` DATETIME DEFAULT NULL COMMENT '上传时间', + + -- ===================== 通用字段 ===================== + `create_time` DATETIME DEFAULT NULL COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', + `is_deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除(0未删 1已删)', + + PRIMARY KEY (`id`), + KEY `idx_sn` (`sn`), + KEY `idx_upload_time` (`upload_time`), + KEY `idx_is_deleted` (`is_deleted`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COMMENT = '设备截图表'; diff --git a/src/main/java/com/youlai/boot/client/controller/ClientContactController.java b/src/main/java/com/youlai/boot/client/controller/ClientContactController.java new file mode 100644 index 00000000..46b6e547 --- /dev/null +++ b/src/main/java/com/youlai/boot/client/controller/ClientContactController.java @@ -0,0 +1,182 @@ +package com.youlai.boot.client.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.youlai.boot.client.model.entity.ClientUser; +import com.youlai.boot.client.service.ClientUserService; +import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.common.result.Result; +import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.device.model.entity.SnDeviceInfo; +import com.youlai.boot.device.model.form.ContactForm; +import com.youlai.boot.device.model.vo.ContactVO; +import com.youlai.boot.device.service.ContactService; +import com.youlai.boot.device.service.DeviceService; +import com.youlai.boot.framework.security.util.SecurityUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 客户端(家属端)联系人控制层 + * + *

将后台管理端 {@code ContactController} 的联系人管理能力移植到客户端: + * 家属端用户登录后可对已绑定设备查询、新增、修改、删除联系人,路径统一挂在 + * {@code /api/v1/client/sn/contact} 下。 + * + *

与 {@link ClientController} 一致:所有接口先通过 {@link #checkBound(String)} + * 校验当前登录的 client 用户是否绑定传入的设备 SN,再执行操作。 + * + * @author TTSTD + * @since 2026/08/19 + */ +@Tag(name = "客户端-联系人") +@RestController +@RequestMapping("/api/v1/client/sn/contact") +@RequiredArgsConstructor +@Slf4j +public class ClientContactController { + + private final ClientUserService clientUserService; + private final DeviceService deviceService; + private final ContactService contactService; + + /** + * 校验当前登录的 client 用户是否绑定了传入的设备 SN。 + *

+ * 绑定关系:设备表 {@code sys_sn.snMobile} 与 client 用户 {@code app_user.mobile} 一致即视为已绑定。 + * 校验未通过时抛出 {@link BusinessException},由调用方转换为失败结果返回。 + * + * @param sn 设备序列号 + * @return 已绑定的设备记录 + */ + private SnDeviceInfo checkBound(String sn) { + if (!StringUtils.hasText(sn)) { + throw new BusinessException("设备序列号不能为空"); + } + + Long userId = SecurityUtils.getUserId(); + ClientUser clientUser = clientUserService.getById(userId); + if (clientUser == null || !StringUtils.hasText(clientUser.getMobile())) { + throw new BusinessException("当前用户未绑定手机号,无法校验设备"); + } + + SnDeviceInfo deviceInfo = deviceService.getOne( + new LambdaQueryWrapper().eq(SnDeviceInfo::getSerialno, sn) + ); + if (deviceInfo == null) { + throw new BusinessException("设备不存在"); + } + + if (!StringUtils.hasText(deviceInfo.getSnMobile()) + || !deviceInfo.getSnMobile().equals(clientUser.getMobile())) { + log.warn("client用户 {} 未绑定设备 sn: {}", userId, sn); + throw new BusinessException("当前用户未绑定该设备"); + } + + return deviceInfo; + } + + @Operation(summary = "联系人列表(家属端)") + @PreAuthorize("isAuthenticated()") + @GetMapping("/list") + public Result> getContactList( + @Parameter(description = "设备序列号") @RequestParam("sn") String sn + ) { + try { + checkBound(sn); + List result = contactService.getAllContacts(sn); + return Result.success(result); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("getContactList error, sn: {}", sn, e); + return Result.failed("获取联系人列表失败: " + e.getMessage()); + } + } + + @Operation(summary = "新增联系人(家属端)") + @PreAuthorize("isAuthenticated()") + @PostMapping("/insert") + public Result saveContact( + @Parameter(description = "设备序列号") @RequestParam("sn") String sn, + @Valid @RequestBody ContactForm contactForm + ) { + try { + checkBound(sn); + Long contactId = contactService.saveContact(sn, contactForm); + if (contactId == null) { + return Result.failed(ResultCode.DUPLICATE_SUBMISSION, "该手机号已存在于当前设备的联系人中", contactForm); + } + return Result.success(contactId); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("saveContact error, sn: {}", sn, e); + return Result.failed("新增联系人失败: " + e.getMessage()); + } + } + + @Operation(summary = "获取联系人表单数据(家属端)") + @PreAuthorize("isAuthenticated()") + @GetMapping("/form") + public Result getContactForm( + @Parameter(description = "联系人ID") @RequestParam Long id, + @Parameter(description = "设备序列号") @RequestParam("sn") String sn + ) { + try { + checkBound(sn); + ContactForm formData = contactService.getContactForm(id, sn); + return Result.success(formData); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("getContactForm error, sn: {}, id: {}", sn, id, e); + return Result.failed("获取联系人信息失败: " + e.getMessage()); + } + } + + @Operation(summary = "修改联系人(家属端)") + @PreAuthorize("isAuthenticated()") + @PutMapping("/update") + public Result updateContact( + @Parameter(description = "联系人ID") @RequestParam Long id, + @Parameter(description = "设备序列号") @RequestParam("sn") String sn, + @Valid @RequestBody ContactForm contactForm + ) { + try { + checkBound(sn); + return Result.judge(contactService.updateContact(id, sn, contactForm)); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("updateContact error, sn: {}, id: {}", sn, id, e); + return Result.failed("修改联系人失败: " + e.getMessage()); + } + } + + @Operation(summary = "删除联系人(家属端)") + @PreAuthorize("isAuthenticated()") + @DeleteMapping("/delete") + public Result deleteContact( + @Parameter(description = "联系人ID") @RequestParam Long id, + @Parameter(description = "设备序列号") @RequestParam("sn") String sn + ) { + try { + checkBound(sn); + return Result.judge(contactService.deleteContact(id, sn)); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("deleteContact error, sn: {}, id: {}", sn, id, e); + return Result.failed("删除联系人失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/youlai/boot/client/controller/ClientDeviceOpsController.java b/src/main/java/com/youlai/boot/client/controller/ClientDeviceOpsController.java index b3b8e45c..83ffcda1 100644 --- a/src/main/java/com/youlai/boot/client/controller/ClientDeviceOpsController.java +++ b/src/main/java/com/youlai/boot/client/controller/ClientDeviceOpsController.java @@ -143,6 +143,24 @@ public class ClientDeviceOpsController { } } + @Operation(summary = "远程拍照(家属端)") + @PreAuthorize("isAuthenticated()") + @PostMapping("/take_photo") + public Result takePhoto( + @Parameter(description = "设备序列号") @RequestParam("sn") String sn + ) { + try { + checkBound(sn); + boolean result = deviceService.takePhoto(sn); + return Result.judge(result); + } catch (BusinessException be) { + return Result.failed(be.getMessage()); + } catch (Exception e) { + log.error("remoteCamera error, sn: {}", sn, e); + return Result.failed("设备截图失败: " + e.getMessage()); + } + } + @Operation(summary = "设备重启(家属端)") @PreAuthorize("isAuthenticated()") @PostMapping("/reboot") diff --git a/src/main/java/com/youlai/boot/device/controller/DeviceOpsController.java b/src/main/java/com/youlai/boot/device/controller/DeviceOpsController.java index 46cf82c3..54721d2d 100644 --- a/src/main/java/com/youlai/boot/device/controller/DeviceOpsController.java +++ b/src/main/java/com/youlai/boot/device/controller/DeviceOpsController.java @@ -81,6 +81,14 @@ public class DeviceOpsController { return Result.judge(result); } + @Operation(summary = "相机拍照") + @PostMapping("/take_photo") + @Log(module = LogModuleEnum.DEVICE, value = ActionTypeEnum.SCREENSHOT) + public Result takePhoto(@RequestParam String sn) { + boolean result = deviceService.takePhoto(sn); + return Result.judge(result); + } + @Operation(summary = "设备重启") @PostMapping("/reboot") @Log(module = LogModuleEnum.DEVICE, value = ActionTypeEnum.REBOOT) diff --git a/src/main/java/com/youlai/boot/device/controller/MobileController.java b/src/main/java/com/youlai/boot/device/controller/MobileController.java index fcd57b72..31e22b23 100644 --- a/src/main/java/com/youlai/boot/device/controller/MobileController.java +++ b/src/main/java/com/youlai/boot/device/controller/MobileController.java @@ -397,6 +397,11 @@ public class MobileController { SnScreenshot screenshotInfo = new SnScreenshot(); screenshotInfo.setSn(sn); + // 保存原始文件名,便于追溯/调试 + screenshotInfo.setOriginName(safeBaseName); + // 保存MIME类型与扩展名,便于前端筛选/预览 + screenshotInfo.setMimeType(file.getContentType()); + screenshotInfo.setFileExtension(fileExtension); // fileName 仅存纯文件名;前端访问 URL = /static/screenshot/{fileName}(由 WebMvcConfig 静态映射) screenshotInfo.setFileName(fileName); // filePath 仅存相对子目录,避免耦合绝对路径 diff --git a/src/main/java/com/youlai/boot/device/model/entity/SnCameraPhoto.java b/src/main/java/com/youlai/boot/device/model/entity/SnCameraPhoto.java new file mode 100644 index 00000000..5961eccf --- /dev/null +++ b/src/main/java/com/youlai/boot/device/model/entity/SnCameraPhoto.java @@ -0,0 +1,72 @@ +package com.youlai.boot.device.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.youlai.boot.common.base.BaseEntity; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** + * 设备截图实体 + */ +@TableName("sys_sn_camera_photo") +@Getter +@Setter +public class SnCameraPhoto extends BaseEntity { + + /** + * 设备序列号 + */ + private String sn; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 原始文件名(上传时的原始文件名) + */ + private String originName; + + /** + * 文件MIME类型 + */ + private String mimeType; + + /** + * 文件扩展名 + */ + private String fileExtension; + + /** + * 文件路径 + */ + private String filePath; + + /** + * 文件大小(字节) + */ + private Long fileSize; + + /** + * 文件MD5值 + */ + private String fileMd5; + /** + * 文件Sha1值 + */ + + private String fileSha1; + + /** + * 文件Sha256值 + */ + private String fileSha256; + + /** + * 上传时间戳 + */ + private LocalDateTime uploadTime; +} diff --git a/src/main/java/com/youlai/boot/device/model/entity/SnScreenshot.java b/src/main/java/com/youlai/boot/device/model/entity/SnScreenshot.java index a9b68324..75a8d43d 100644 --- a/src/main/java/com/youlai/boot/device/model/entity/SnScreenshot.java +++ b/src/main/java/com/youlai/boot/device/model/entity/SnScreenshot.java @@ -25,6 +25,21 @@ public class SnScreenshot extends BaseEntity { */ private String fileName; + /** + * 原始文件名(上传时的原始文件名) + */ + private String originName; + + /** + * 文件MIME类型 + */ + private String mimeType; + + /** + * 文件扩展名 + */ + private String fileExtension; + /** * 文件路径 */ @@ -54,4 +69,9 @@ public class SnScreenshot extends BaseEntity { * 上传时间戳 */ private LocalDateTime uploadTime; + + /** + * 逻辑删除标志(0未删除 1已删除) + */ + private boolean isDeleted; } diff --git a/src/main/java/com/youlai/boot/device/model/vo/ScreenshotVO.java b/src/main/java/com/youlai/boot/device/model/vo/ScreenshotVO.java index 092d79d0..265a7738 100644 --- a/src/main/java/com/youlai/boot/device/model/vo/ScreenshotVO.java +++ b/src/main/java/com/youlai/boot/device/model/vo/ScreenshotVO.java @@ -24,6 +24,15 @@ public class ScreenshotVO { @Schema(description = "文件名称") private String fileName; + @Schema(description = "原始文件名(上传时的原始文件名)") + private String originName; + + @Schema(description = "文件MIME类型") + private String mimeType; + + @Schema(description = "文件扩展名") + private String fileExtension; + @Schema(description = "文件大小(字节)") private Long fileSize; diff --git a/src/main/java/com/youlai/boot/device/service/DeviceService.java b/src/main/java/com/youlai/boot/device/service/DeviceService.java index 04b0ef7b..51b56260 100644 --- a/src/main/java/com/youlai/boot/device/service/DeviceService.java +++ b/src/main/java/com/youlai/boot/device/service/DeviceService.java @@ -97,6 +97,7 @@ public interface DeviceService extends IService { * @return 是否推送成功 */ boolean screenSnapshot(String sn); + boolean takePhoto(String sn); boolean deviceReboot(String sn); boolean deviceShutdown(String sn); diff --git a/src/main/java/com/youlai/boot/device/service/impl/DeviceServiceImpl.java b/src/main/java/com/youlai/boot/device/service/impl/DeviceServiceImpl.java index ef90c182..d88b00a1 100644 --- a/src/main/java/com/youlai/boot/device/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/youlai/boot/device/service/impl/DeviceServiceImpl.java @@ -55,6 +55,20 @@ import java.util.Set; @RequiredArgsConstructor @Slf4j public class DeviceServiceImpl extends ServiceImpl implements DeviceService { + + private static final String DEVICE_REFRESH = "1"; + private static final String DEVICE_SCREEN_SNAPSHOT = "2"; + private static final String DEVICE_REBOOT = "3"; + private static final String DEVICE_SHUTDOWN = "4"; + private static final String DEVICE_LOCATE = "5"; + private static final String DEVICE_RESTORE = "6"; + private static final String DEVICE_DEVELOPER = "7"; + private static final String DEVICE_APP_LAUNCH = "8"; + private static final String DEVICE_APP_CLEAR_DATA = "9"; + private static final String DEVICE_APP_UNINSTALL = "10"; + private static final String DEVICE_APP_STOP = "11"; + private static final String DEVICE_TAKE_PHOTO = "12"; + PushApi pushApi = new PushApi.Builder() .setAppKey("d779178d9900d4fb5d633678") .setMasterSecret("be0e197d30fec7bec118a70d") @@ -254,7 +268,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean deviceRefresh(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "1", "deviceRefresh", "refresh"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_REFRESH, "deviceRefresh", "refresh"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -272,7 +286,24 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean screenSnapshot(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "2", "screenSnapshot", "screenshot"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_SCREEN_SNAPSHOT, "screenSnapshot", "screenshot"); + try { + PushSendResult result = pushApi.send(pushSendParam); + log.info("send success:{}", result); + return true; + } catch (ApiErrorException e) { + // 错误信息 + int httpStatus = e.getStats(); // HTTP状态码 + int errorCode = e.getApiError().getError().getCode(); // 错误码 + String errorMessage = e.getApiError().getError().getMessage(); // 错误信息 + log.error("send error, httpStatus:{} code:{}, message:{}", httpStatus, errorCode, errorMessage); + return false; + } + } + + @Override + public boolean takePhoto(String sn) { + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_TAKE_PHOTO, "takePhoto", "camera"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -289,7 +320,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean deviceReboot(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "3", "deviceReboot", "reboot"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_REBOOT, "deviceReboot", "reboot"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -306,7 +337,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean deviceShutdown(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "4", "deviceShutdown", "shutdown"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_SHUTDOWN, "deviceShutdown", "shutdown"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -323,7 +354,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean deviceLocate(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "5", "deviceLocate", "locate"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_LOCATE, "deviceLocate", "locate"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -340,7 +371,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean restore(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "6", "deviceRestore", "restore"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_RESTORE, "deviceRestore", "restore"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -357,7 +388,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean setDeviceDeveloper(String sn) { - PushSendParam pushSendParam = getSinglePushSendParam(sn, "7", "deviceDeveloper", "developer"); + PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_DEVELOPER, "deviceDeveloper", "developer"); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -385,7 +416,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean launchApp(String sn, String packageName) { - PushSendParam pushSendParam = getAppPushSendParam(sn, "8", "appLaunch", "launch", packageName); + PushSendParam pushSendParam = getAppPushSendParam(sn, DEVICE_APP_LAUNCH, "appLaunch", "launch", packageName); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -401,7 +432,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean clearAppData(String sn, String packageName) { - PushSendParam pushSendParam = getAppPushSendParam(sn, "9", "appClearData", "clearData", packageName); + PushSendParam pushSendParam = getAppPushSendParam(sn, DEVICE_APP_CLEAR_DATA, "appClearData", "clearData", packageName); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -417,7 +448,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean uninstallApp(String sn, String packageName) { - PushSendParam pushSendParam = getAppPushSendParam(sn, "10", "appUninstall", "uninstall", packageName); + PushSendParam pushSendParam = getAppPushSendParam(sn, DEVICE_APP_UNINSTALL, "appUninstall", "uninstall", packageName); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result); @@ -433,7 +464,7 @@ public class DeviceServiceImpl extends ServiceImpl i @Override public boolean stopApp(String sn, String packageName) { - PushSendParam pushSendParam = getAppPushSendParam(sn, "11", "appStop", "stop", packageName); + PushSendParam pushSendParam = getAppPushSendParam(sn, DEVICE_APP_STOP, "appStop", "stop", packageName); try { PushSendResult result = pushApi.send(pushSendParam); log.info("send success:{}", result);