feat(device): 添加设备策略信息管理功能
新增设备策略信息表 sys_sn_device_policy 及对应实体、Mapper、Converter、VO、Req 模型,并在设备端、家属端、管理端分别提供策略查询、全量更新和单独开关设置接口。同时修正设备菜单按钮权限命名(add/edit → create/update),并调整删除设备与解绑接口的权限和返回值。
This commit is contained in:
@@ -31,7 +31,14 @@ SET @menu_id = (SELECT `id` FROM `sys_menu` WHERE `component` = 'devices/sn/in
|
||||
-- 计算按钮的 tree_path = 菜单 tree_path + ',' + 菜单 id
|
||||
SET @tree_path = (SELECT CONCAT(`tree_path`, ',', `id`) FROM `sys_menu` WHERE `id` = @menu_id);
|
||||
|
||||
-- 1. 插入按钮权限(不存在才插入);权限字符串用 CONCAT 拼接,避免 :token 被客户端误判
|
||||
-- 1. 清理旧命名按钮权限(add/edit → create/update),保证幂等可重跑
|
||||
-- 旧的 sys:device:add / sys:device:edit 若存在则删除,避免与标准命名并存
|
||||
DELETE FROM `sys_menu`
|
||||
WHERE `perm` IN (CONCAT('sys', ':', 'device', ':', 'add'), CONCAT('sys', ':', 'device', ':', 'edit'))
|
||||
AND `type` = 'B';
|
||||
|
||||
-- 2. 插入按钮权限(不存在才插入);权限字符串用 CONCAT 拼接,避免 :token 被客户端误判
|
||||
-- 命名遵循系统标准(前端 buttonConfig 与后端代码生成器统一):list / create / update / delete
|
||||
INSERT INTO `sys_menu` (`parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`,
|
||||
`perm`, `always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`,
|
||||
`create_time`, `update_time`, `params`)
|
||||
@@ -39,14 +46,14 @@ SELECT @menu_id, @tree_path, t.`name`, 'B', NULL, NULL, NULL,
|
||||
t.`perm`, NULL, NULL, 1, t.`sort`, NULL, NULL, now(), now(), NULL
|
||||
FROM (
|
||||
SELECT '查询' AS `name`, CONCAT('sys', ':', 'device', ':', 'list') AS `perm`, 1 AS `sort` UNION ALL
|
||||
SELECT '新增', CONCAT('sys', ':', 'device', ':', 'add'), 2 UNION ALL
|
||||
SELECT '编辑', CONCAT('sys', ':', 'device', ':', 'edit'), 3 UNION ALL
|
||||
SELECT '新增', CONCAT('sys', ':', 'device', ':', 'create'), 2 UNION ALL
|
||||
SELECT '编辑', CONCAT('sys', ':', 'device', ':', 'update'), 3 UNION ALL
|
||||
SELECT '删除', CONCAT('sys', ':', 'device', ':', 'delete'), 4
|
||||
) t
|
||||
WHERE @menu_id IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM `sys_menu` m WHERE m.`perm` = t.`perm`);
|
||||
|
||||
-- 2. 授权给 ADMIN 角色(role_id = 2;如实际角色 id 不同请修改)
|
||||
-- 3. 授权给 ADMIN 角色(role_id = 2;如实际角色 id 不同请修改)
|
||||
-- 直接授权该菜单下的全部按钮,避免 SQL 文本中出现带冒号的权限字符串
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT 2, `id`
|
||||
|
||||
42
sql/sn_device_policy.sql
Normal file
42
sql/sn_device_policy.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- =============================================================================
|
||||
-- 设备策略信息表(sys_sn_device_policy)
|
||||
-- 说明:存储设备的策略开关状态,与设备 SN 绑定,每个 SN 唯一一条。
|
||||
-- 所有开关字段默认值均为 1(开启)。
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS `sys_sn_device_policy`
|
||||
(
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`serialno` VARCHAR(64) NOT NULL COMMENT '设备序列号(关联 sys_sn.serialno)',
|
||||
|
||||
-- ===================== 策略开关 =====================
|
||||
`usb_data` TINYINT NOT NULL DEFAULT 1 COMMENT 'USB偏好设置(0关闭 1开启)',
|
||||
`time_setting` TINYINT NOT NULL DEFAULT 1 COMMENT '时间设置管控(0关闭 1开启)',
|
||||
`storage_card` TINYINT NOT NULL DEFAULT 1 COMMENT '存储卡(0关闭 1开启)',
|
||||
`factory_reset` TINYINT NOT NULL DEFAULT 1 COMMENT '恢复出厂(0关闭 1开启)',
|
||||
`wifi_hotspot` TINYINT NOT NULL DEFAULT 1 COMMENT 'WiFi热点开关(0关闭 1开启)',
|
||||
`bluetooth_switch` TINYINT NOT NULL DEFAULT 1 COMMENT '蓝牙开关(0关闭 1开启)',
|
||||
`wallpaper` TINYINT NOT NULL DEFAULT 1 COMMENT '壁纸功能(0关闭 1开启)',
|
||||
`notification_bar` TINYINT NOT NULL DEFAULT 1 COMMENT '通知栏显示(0关闭 1开启)',
|
||||
`status_bar_pull_down` TINYINT NOT NULL DEFAULT 1 COMMENT '状态栏下拉开关(0关闭 1开启)',
|
||||
`system_nav_bar_show` TINYINT NOT NULL DEFAULT 1 COMMENT '系统导航条显示(0关闭 1开启)',
|
||||
`system_nav_bar_setting` TINYINT NOT NULL DEFAULT 1 COMMENT '系统导航栏设置(0关闭 1开启)',
|
||||
`nav_bar_options` TINYINT NOT NULL DEFAULT 1 COMMENT '导航条选项(0关闭 1开启)',
|
||||
`bluetooth_function` TINYINT NOT NULL DEFAULT 1 COMMENT '蓝牙功能开关(0关闭 1开启)',
|
||||
`ota_upgrade` TINYINT NOT NULL DEFAULT 1 COMMENT 'OTA升级管控(0关闭 1开启)',
|
||||
`app_install` TINYINT NOT NULL DEFAULT 1 COMMENT '应用安装开关(0关闭 1开启)',
|
||||
`auto_rotate` TINYINT NOT NULL DEFAULT 1 COMMENT '自动旋转(0关闭 1开启)',
|
||||
`auto_brightness` TINYINT NOT NULL DEFAULT 1 COMMENT '自动亮度(0关闭 1开启)',
|
||||
`eye_protection` TINYINT NOT NULL DEFAULT 1 COMMENT '护眼模式(0关闭 1开启)',
|
||||
`dark_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '深色模式(0关闭 1开启)',
|
||||
|
||||
-- ===================== 通用字段 =====================
|
||||
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除(0未删 1已删)',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_serialno` (`serialno`),
|
||||
KEY `idx_is_deleted` (`is_deleted`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COMMENT = '设备策略信息表';
|
||||
@@ -5,13 +5,19 @@ 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.device.converter.DevicePolicyConverter;
|
||||
import com.youlai.boot.device.model.entity.SnDeviceInfo;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyReq;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyToggleReq;
|
||||
import com.youlai.boot.device.model.vo.DeviceApkInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceBriefVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceLocationVO;
|
||||
import com.youlai.boot.device.model.vo.DevicePolicyVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceSystemInfoVO;
|
||||
import com.youlai.boot.device.model.vo.ScreenshotVO;
|
||||
import com.youlai.boot.device.service.ApkInstallService;
|
||||
import com.youlai.boot.device.service.DevicePolicyService;
|
||||
import com.youlai.boot.device.service.DeviceService;
|
||||
import com.youlai.boot.device.service.LocationService;
|
||||
import com.youlai.boot.device.service.ScreenshotService;
|
||||
@@ -26,6 +32,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -53,6 +61,8 @@ public class ClientController {
|
||||
private final LocationService locationService;
|
||||
private final ApkInstallService apkInstallService;
|
||||
private final ScreenshotService screenshotService;
|
||||
private final DevicePolicyService devicePolicyService;
|
||||
private final DevicePolicyConverter devicePolicyConverter;
|
||||
|
||||
/**
|
||||
* 校验当前登录的 client 用户是否绑定了传入的设备 SN。
|
||||
@@ -286,4 +296,106 @@ public class ClientController {
|
||||
return Result.failed("解绑设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 设备策略信息(家属端) =====================
|
||||
|
||||
@Operation(summary = "获取已绑定设备策略信息", description = "SN 合法且无记录时自动新建默认策略(默认全开)")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/policy")
|
||||
public Result<DevicePolicyVO> getPolicy(
|
||||
@Parameter(description = "设备序列号") @RequestParam("sn") String sn
|
||||
) {
|
||||
try {
|
||||
checkBound(sn);
|
||||
SnDevicePolicy policy = devicePolicyService.getOrInitBySn(sn);
|
||||
if (policy == null) {
|
||||
return Result.failed("设备SN不合法或不存在");
|
||||
}
|
||||
return Result.success(devicePolicyConverter.toVo(policy));
|
||||
} catch (BusinessException be) {
|
||||
return Result.failed(be.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("getPolicy error, sn: {}", sn, e);
|
||||
return Result.failed("获取策略信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "全量设置已绑定设备策略信息", description = "SN 合法且无记录时新建,存在时更新(空字段保持原值)")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PutMapping("/policy")
|
||||
public Result<?> updatePolicy(
|
||||
@Parameter(description = "设备序列号") @RequestParam("sn") String sn,
|
||||
@RequestBody DevicePolicyReq req
|
||||
) {
|
||||
try {
|
||||
checkBound(sn);
|
||||
if (req == null) {
|
||||
return Result.failed("策略信息不能为空");
|
||||
}
|
||||
SnDevicePolicy policy = new SnDevicePolicy();
|
||||
policy.setSerialno(sn);
|
||||
copyPolicy(req, policy);
|
||||
boolean success = devicePolicyService.saveOrUpdateBySn(sn, policy);
|
||||
if (!success) {
|
||||
return Result.failed("保存策略信息失败(设备SN不合法或参数异常)");
|
||||
}
|
||||
// 全量保存后推送策略状态到对应设备
|
||||
devicePolicyService.pushPolicyToDevice(sn);
|
||||
return Result.success();
|
||||
} catch (BusinessException be) {
|
||||
return Result.failed(be.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("updatePolicy error, sn: {}", sn, e);
|
||||
return Result.failed("保存策略信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "单独设置已绑定设备策略开关", description = "通过 field 指定策略字段,value 设置 0/1")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PutMapping("/policy/toggle")
|
||||
public Result<?> togglePolicy(
|
||||
@Parameter(description = "设备序列号") @RequestParam("sn") String sn,
|
||||
@RequestBody DevicePolicyToggleReq req
|
||||
) {
|
||||
try {
|
||||
checkBound(sn);
|
||||
if (req == null || req.getField() == null || req.getValue() == null) {
|
||||
return Result.failed("field 与 value 不能为空");
|
||||
}
|
||||
boolean success = devicePolicyService.updateSingleBySn(sn, req.getField(), req.getValue());
|
||||
if (!success) {
|
||||
return Result.failed("设置策略开关失败(设备SN不合法或字段不支持)");
|
||||
}
|
||||
// 单独保存后推送策略状态到对应设备
|
||||
devicePolicyService.pushPolicyToDevice(sn);
|
||||
return Result.success();
|
||||
} catch (BusinessException be) {
|
||||
return Result.failed(be.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("togglePolicy error, sn: {}", sn, e);
|
||||
return Result.failed("设置策略开关失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPolicy(DevicePolicyReq src, SnDevicePolicy target) {
|
||||
target.setUsbData(src.getUsbData());
|
||||
target.setTimeSetting(src.getTimeSetting());
|
||||
target.setStorageCard(src.getStorageCard());
|
||||
target.setFactoryReset(src.getFactoryReset());
|
||||
target.setWifiHotspot(src.getWifiHotspot());
|
||||
target.setBluetoothSwitch(src.getBluetoothSwitch());
|
||||
target.setWallpaper(src.getWallpaper());
|
||||
target.setNotificationBar(src.getNotificationBar());
|
||||
target.setStatusBarPullDown(src.getStatusBarPullDown());
|
||||
target.setSystemNavBarShow(src.getSystemNavBarShow());
|
||||
target.setSystemNavBarSetting(src.getSystemNavBarSetting());
|
||||
target.setNavBarOptions(src.getNavBarOptions());
|
||||
target.setBluetoothFunction(src.getBluetoothFunction());
|
||||
target.setOtaUpgrade(src.getOtaUpgrade());
|
||||
target.setAppInstall(src.getAppInstall());
|
||||
target.setAutoRotate(src.getAutoRotate());
|
||||
target.setAutoBrightness(src.getAutoBrightness());
|
||||
target.setEyeProtection(src.getEyeProtection());
|
||||
target.setDarkMode(src.getDarkMode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,22 @@ import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.common.result.PageResult;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.device.converter.DevicePolicyConverter;
|
||||
import com.youlai.boot.device.model.entity.SnDeviceInfo;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import com.youlai.boot.device.model.query.DeviceQuery;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyReq;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyToggleReq;
|
||||
import com.youlai.boot.device.model.vo.DeviceApkInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceHardwareInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceNetworkInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceOtherInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DevicePageVO;
|
||||
import com.youlai.boot.device.model.vo.DevicePolicyVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceSecurityInfoVO;
|
||||
import com.youlai.boot.device.model.vo.DeviceSystemInfoVO;
|
||||
import com.youlai.boot.device.service.ApkInstallService;
|
||||
import com.youlai.boot.device.service.DevicePolicyService;
|
||||
import com.youlai.boot.device.service.DeviceService;
|
||||
import com.youlai.boot.device.service.HardwareInfoService;
|
||||
import com.youlai.boot.device.service.NetworkInfoService;
|
||||
@@ -57,6 +63,8 @@ public class DeviceController {
|
||||
private final SecurityInfoService securityInfoService;
|
||||
private final OtherInfoService otherInfoService;
|
||||
private final ApkInstallService apkInstallService;
|
||||
private final DevicePolicyService devicePolicyService;
|
||||
private final DevicePolicyConverter devicePolicyConverter;
|
||||
|
||||
@Operation(summary = "设备分页列表")
|
||||
@GetMapping("/page")
|
||||
@@ -145,4 +153,74 @@ public class DeviceController {
|
||||
return Result.success(apkInstallService.getDeviceApkInfo(sn));
|
||||
}
|
||||
|
||||
// ===================== 设备策略信息 =====================
|
||||
|
||||
@Operation(summary = "获取设备策略信息", description = "SN 合法且无记录时自动新建默认策略(默认全开)")
|
||||
@GetMapping("/{sn}/policy")
|
||||
public Result<DevicePolicyVO> getDevicePolicy(
|
||||
@Parameter(description = "设备序列号") @PathVariable("sn") String sn) {
|
||||
SnDevicePolicy policy = devicePolicyService.getOrInitBySn(sn);
|
||||
if (policy == null) {
|
||||
return Result.failed("设备SN不合法或不存在");
|
||||
}
|
||||
return Result.success(devicePolicyConverter.toVo(policy));
|
||||
}
|
||||
|
||||
@Operation(summary = "全量设置设备策略信息", description = "SN 合法且无记录时新建,存在时更新(空字段保持原值)")
|
||||
@PutMapping("/{sn}/policy")
|
||||
public Result<Void> updateDevicePolicy(
|
||||
@Parameter(description = "设备序列号") @PathVariable("sn") String sn,
|
||||
@RequestBody DevicePolicyReq req) {
|
||||
if (req == null) {
|
||||
return Result.failed("策略信息不能为空");
|
||||
}
|
||||
SnDevicePolicy policy = new SnDevicePolicy();
|
||||
policy.setSerialno(sn);
|
||||
copyPolicy(req, policy);
|
||||
boolean success = devicePolicyService.saveOrUpdateBySn(sn, policy);
|
||||
if (!success) {
|
||||
return Result.failed("保存策略信息失败(设备SN不合法或参数异常)");
|
||||
}
|
||||
// 全量保存后推送策略状态到对应设备
|
||||
devicePolicyService.pushPolicyToDevice(sn);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "单独设置设备策略开关", description = "通过 field 指定策略字段,value 设置 0/1")
|
||||
@PutMapping("/{sn}/policy/toggle")
|
||||
public Result<Void> toggleDevicePolicy(
|
||||
@Parameter(description = "设备序列号") @PathVariable("sn") String sn,
|
||||
@RequestBody DevicePolicyToggleReq req) {
|
||||
if (req == null || req.getField() == null || req.getValue() == null) {
|
||||
return Result.failed("field 与 value 不能为空");
|
||||
}
|
||||
boolean success = devicePolicyService.updateSingleBySn(sn, req.getField(), req.getValue());
|
||||
if (!success) {
|
||||
return Result.failed("设置策略开关失败(设备SN不合法或字段不支持)");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
private void copyPolicy(DevicePolicyReq src, SnDevicePolicy target) {
|
||||
target.setUsbData(src.getUsbData());
|
||||
target.setTimeSetting(src.getTimeSetting());
|
||||
target.setStorageCard(src.getStorageCard());
|
||||
target.setFactoryReset(src.getFactoryReset());
|
||||
target.setWifiHotspot(src.getWifiHotspot());
|
||||
target.setBluetoothSwitch(src.getBluetoothSwitch());
|
||||
target.setWallpaper(src.getWallpaper());
|
||||
target.setNotificationBar(src.getNotificationBar());
|
||||
target.setStatusBarPullDown(src.getStatusBarPullDown());
|
||||
target.setSystemNavBarShow(src.getSystemNavBarShow());
|
||||
target.setSystemNavBarSetting(src.getSystemNavBarSetting());
|
||||
target.setNavBarOptions(src.getNavBarOptions());
|
||||
target.setBluetoothFunction(src.getBluetoothFunction());
|
||||
target.setOtaUpgrade(src.getOtaUpgrade());
|
||||
target.setAppInstall(src.getAppInstall());
|
||||
target.setAutoRotate(src.getAutoRotate());
|
||||
target.setAutoBrightness(src.getAutoBrightness());
|
||||
target.setEyeProtection(src.getEyeProtection());
|
||||
target.setDarkMode(src.getDarkMode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,10 +59,19 @@ public class DeviceOpsController {
|
||||
@Operation(summary = "删除SN")
|
||||
@DeleteMapping("/{sn}")
|
||||
@Log(module = LogModuleEnum.DEVICE, value = ActionTypeEnum.DELETE)
|
||||
@PreAuthorize("@ss.hasPerm('sys:sn:delete')")
|
||||
public Result<Void> deleteSn(@PathVariable String sn) {
|
||||
deviceService.deleteSn(sn);
|
||||
return Result.success();
|
||||
@PreAuthorize("@ss.hasPerm('sys:device:delete')")
|
||||
public Result<Boolean> deleteSn(@PathVariable String sn) {
|
||||
boolean deleted = deviceService.deleteSn(sn);
|
||||
return Result.judge(deleted);
|
||||
}
|
||||
|
||||
@Operation(summary = "解除设备绑定", description = "清除设备绑定的手机号,并下发推送通知设备端标记解绑状态")
|
||||
@PostMapping("/{sn}/unbind")
|
||||
@Log(module = LogModuleEnum.DEVICE, value = ActionTypeEnum.UPDATE)
|
||||
@PreAuthorize("@ss.hasPerm('sys:device:update')")
|
||||
public Result<Boolean> unbindMobile(@PathVariable String sn) {
|
||||
boolean result = deviceService.unbindMobile(sn);
|
||||
return Result.judge(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "设备刷新")
|
||||
|
||||
@@ -22,8 +22,13 @@ import com.youlai.boot.device.model.req.SnSecurityInfoReq;
|
||||
import com.youlai.boot.device.model.req.SnOtherInfoReq;
|
||||
import com.youlai.boot.device.model.req.SnLocationReq;
|
||||
import com.youlai.boot.device.model.entity.SnLocation;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import com.youlai.boot.device.model.entity.SnScreenshot;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyReq;
|
||||
import com.youlai.boot.device.model.req.DevicePolicyToggleReq;
|
||||
import com.youlai.boot.device.model.vo.DeveloperOptionsVO;
|
||||
import com.youlai.boot.device.model.vo.DevicePolicyVO;
|
||||
import com.youlai.boot.device.converter.DevicePolicyConverter;
|
||||
import com.youlai.boot.device.service.*;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -68,6 +73,8 @@ public class MobileController {
|
||||
private final NetworkInfoService networkInfoService;
|
||||
private final SecurityInfoService securityInfoService;
|
||||
private final OtherInfoService otherInfoService;
|
||||
private final DevicePolicyService devicePolicyService;
|
||||
private final DevicePolicyConverter devicePolicyConverter;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MobileController.class);
|
||||
|
||||
@@ -565,4 +572,100 @@ public class MobileController {
|
||||
return Result.failed("上传应用图标失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 设备策略信息(设备端) =====================
|
||||
|
||||
@Operation(summary = "获取设备策略信息", description = "SN 合法且无记录时自动新建默认策略(默认全开)")
|
||||
@GetMapping("/get_device_policy")
|
||||
public Result<DevicePolicyVO> getDevicePolicy(
|
||||
@RequestHeader(value = "X-Device-SN") String sn
|
||||
) {
|
||||
try {
|
||||
if (sn == null || sn.trim().isEmpty()) {
|
||||
return Result.failed("设备序列号不能为空");
|
||||
}
|
||||
SnDevicePolicy policy = devicePolicyService.getOrInitBySn(sn);
|
||||
if (policy == null) {
|
||||
return Result.failed("设备SN不合法或不存在");
|
||||
}
|
||||
return Result.success(devicePolicyConverter.toVo(policy));
|
||||
} catch (Exception e) {
|
||||
logger.error("getDevicePolicy error, sn: {}", sn, e);
|
||||
return Result.failed("获取设备策略失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "上传/全量设置设备策略信息", description = "SN 合法且无记录时新建,存在时更新(空字段保持原值)")
|
||||
@PostMapping("/update_device_policy")
|
||||
public Result<Void> updateDevicePolicy(
|
||||
@RequestHeader(value = "X-Device-SN") String sn,
|
||||
@RequestBody DevicePolicyReq req
|
||||
) {
|
||||
try {
|
||||
if (sn == null || sn.trim().isEmpty()) {
|
||||
return Result.failed("设备序列号不能为空");
|
||||
}
|
||||
if (req == null) {
|
||||
return Result.failed("策略信息不能为空");
|
||||
}
|
||||
SnDevicePolicy policy = new SnDevicePolicy();
|
||||
policy.setSerialno(sn);
|
||||
copyPolicy(req, policy);
|
||||
boolean success = devicePolicyService.saveOrUpdateBySn(sn, policy);
|
||||
if (!success) {
|
||||
return Result.failed("保存设备策略失败(设备SN不合法或参数异常)");
|
||||
}
|
||||
logger.info("设备策略上传成功, sn: {}", sn);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
logger.error("updateDevicePolicy error, sn: {}", sn, e);
|
||||
return Result.failed("上传设备策略失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "单独设置设备策略开关", description = "通过 field 指定策略字段,value 设置 0/1")
|
||||
@PostMapping("/update_device_policy_toggle")
|
||||
public Result<Void> toggleDevicePolicy(
|
||||
@RequestHeader(value = "X-Device-SN") String sn,
|
||||
@RequestBody DevicePolicyToggleReq req
|
||||
) {
|
||||
try {
|
||||
if (sn == null || sn.trim().isEmpty()) {
|
||||
return Result.failed("设备序列号不能为空");
|
||||
}
|
||||
if (req == null || req.getField() == null || req.getValue() == null) {
|
||||
return Result.failed("field 与 value 不能为空");
|
||||
}
|
||||
boolean success = devicePolicyService.updateSingleBySn(sn, req.getField(), req.getValue());
|
||||
if (!success) {
|
||||
return Result.failed("设置设备策略失败(设备SN不合法或字段不支持)");
|
||||
}
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
logger.error("toggleDevicePolicy error, sn: {}", sn, e);
|
||||
return Result.failed("设置设备策略失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPolicy(DevicePolicyReq src, SnDevicePolicy target) {
|
||||
target.setUsbData(src.getUsbData());
|
||||
target.setTimeSetting(src.getTimeSetting());
|
||||
target.setStorageCard(src.getStorageCard());
|
||||
target.setFactoryReset(src.getFactoryReset());
|
||||
target.setWifiHotspot(src.getWifiHotspot());
|
||||
target.setBluetoothSwitch(src.getBluetoothSwitch());
|
||||
target.setWallpaper(src.getWallpaper());
|
||||
target.setNotificationBar(src.getNotificationBar());
|
||||
target.setStatusBarPullDown(src.getStatusBarPullDown());
|
||||
target.setSystemNavBarShow(src.getSystemNavBarShow());
|
||||
target.setSystemNavBarSetting(src.getSystemNavBarSetting());
|
||||
target.setNavBarOptions(src.getNavBarOptions());
|
||||
target.setBluetoothFunction(src.getBluetoothFunction());
|
||||
target.setOtaUpgrade(src.getOtaUpgrade());
|
||||
target.setAppInstall(src.getAppInstall());
|
||||
target.setAutoRotate(src.getAutoRotate());
|
||||
target.setAutoBrightness(src.getAutoBrightness());
|
||||
target.setEyeProtection(src.getEyeProtection());
|
||||
target.setDarkMode(src.getDarkMode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.youlai.boot.device.converter;
|
||||
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import com.youlai.boot.device.model.vo.DevicePolicyVO;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
/**
|
||||
* 设备策略信息 实体与 VO 转换器
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface DevicePolicyConverter {
|
||||
|
||||
/**
|
||||
* 实体 → VO
|
||||
*/
|
||||
DevicePolicyVO toVo(SnDevicePolicy entity);
|
||||
|
||||
/**
|
||||
* 请求对象 → 实体
|
||||
*/
|
||||
SnDevicePolicy toEntity(DevicePolicyVO vo);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.youlai.boot.device.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface DevicePolicyMapper extends BaseMapper<SnDevicePolicy> {
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 设备策略信息实体(策略信息面板,与设备 SN 绑定,每个 SN 唯一一条)
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("sys_sn_device_policy")
|
||||
public class SnDevicePolicy extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 设备序列号(关联 sys_sn.serialno)
|
||||
*/
|
||||
private String serialno;
|
||||
|
||||
// ===================== 策略开关(0关闭 1开启) =====================
|
||||
|
||||
/** USB偏好设置 */
|
||||
private Integer usbData;
|
||||
|
||||
/** 时间设置管控 */
|
||||
private Integer timeSetting;
|
||||
|
||||
/** 存储卡 */
|
||||
private Integer storageCard;
|
||||
|
||||
/** 恢复出厂 */
|
||||
private Integer factoryReset;
|
||||
|
||||
/** WiFi热点开关 */
|
||||
private Integer wifiHotspot;
|
||||
|
||||
/** 蓝牙开关 */
|
||||
private Integer bluetoothSwitch;
|
||||
|
||||
/** 壁纸功能 */
|
||||
private Integer wallpaper;
|
||||
|
||||
/** 通知栏显示 */
|
||||
private Integer notificationBar;
|
||||
|
||||
/** 状态栏下拉开关 */
|
||||
private Integer statusBarPullDown;
|
||||
|
||||
/** 系统导航条显示 */
|
||||
private Integer systemNavBarShow;
|
||||
|
||||
/** 系统导航栏设置 */
|
||||
private Integer systemNavBarSetting;
|
||||
|
||||
/** 导航条选项 */
|
||||
private Integer navBarOptions;
|
||||
|
||||
/** 蓝牙功能开关 */
|
||||
private Integer bluetoothFunction;
|
||||
|
||||
/** OTA升级管控 */
|
||||
private Integer otaUpgrade;
|
||||
|
||||
/** 应用安装开关 */
|
||||
private Integer appInstall;
|
||||
|
||||
/** 自动旋转 */
|
||||
private Integer autoRotate;
|
||||
|
||||
/** 自动亮度 */
|
||||
private Integer autoBrightness;
|
||||
|
||||
/** 护眼模式 */
|
||||
private Integer eyeProtection;
|
||||
|
||||
/** 深色模式 */
|
||||
private Integer darkMode;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.youlai.boot.device.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备策略信息请求对象(全量设置 / 设备端上报)
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Schema(description = "设备策略信息请求对象")
|
||||
@Data
|
||||
public class DevicePolicyReq {
|
||||
|
||||
@Schema(description = "设备序列号")
|
||||
private String serialno;
|
||||
|
||||
// ===================== 策略开关(0关闭 1开启) =====================
|
||||
|
||||
@Schema(description = "USB偏好设置(0关闭 1开启)")
|
||||
private Integer usbData;
|
||||
|
||||
@Schema(description = "时间设置管控(0关闭 1开启)")
|
||||
private Integer timeSetting;
|
||||
|
||||
@Schema(description = "存储卡(0关闭 1开启)")
|
||||
private Integer storageCard;
|
||||
|
||||
@Schema(description = "恢复出厂(0关闭 1开启)")
|
||||
private Integer factoryReset;
|
||||
|
||||
@Schema(description = "WiFi热点开关(0关闭 1开启)")
|
||||
private Integer wifiHotspot;
|
||||
|
||||
@Schema(description = "蓝牙开关(0关闭 1开启)")
|
||||
private Integer bluetoothSwitch;
|
||||
|
||||
@Schema(description = "壁纸功能(0关闭 1开启)")
|
||||
private Integer wallpaper;
|
||||
|
||||
@Schema(description = "通知栏显示(0关闭 1开启)")
|
||||
private Integer notificationBar;
|
||||
|
||||
@Schema(description = "状态栏下拉开关(0关闭 1开启)")
|
||||
private Integer statusBarPullDown;
|
||||
|
||||
@Schema(description = "系统导航条显示(0关闭 1开启)")
|
||||
private Integer systemNavBarShow;
|
||||
|
||||
@Schema(description = "系统导航栏设置(0关闭 1开启)")
|
||||
private Integer systemNavBarSetting;
|
||||
|
||||
@Schema(description = "导航条选项(0关闭 1开启)")
|
||||
private Integer navBarOptions;
|
||||
|
||||
@Schema(description = "蓝牙功能开关(0关闭 1开启)")
|
||||
private Integer bluetoothFunction;
|
||||
|
||||
@Schema(description = "OTA升级管控(0关闭 1开启)")
|
||||
private Integer otaUpgrade;
|
||||
|
||||
@Schema(description = "应用安装开关(0关闭 1开启)")
|
||||
private Integer appInstall;
|
||||
|
||||
@Schema(description = "自动旋转(0关闭 1开启)")
|
||||
private Integer autoRotate;
|
||||
|
||||
@Schema(description = "自动亮度(0关闭 1开启)")
|
||||
private Integer autoBrightness;
|
||||
|
||||
@Schema(description = "护眼模式(0关闭 1开启)")
|
||||
private Integer eyeProtection;
|
||||
|
||||
@Schema(description = "深色模式(0关闭 1开启)")
|
||||
private Integer darkMode;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.youlai.boot.device.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备策略单独设置请求对象
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Schema(description = "设备策略单独设置请求对象")
|
||||
@Data
|
||||
public class DevicePolicyToggleReq {
|
||||
|
||||
@Schema(description = "策略字段名(如 usbData、appInstall)")
|
||||
private String field;
|
||||
|
||||
@Schema(description = "开关值(0关闭 1开启)")
|
||||
private Integer value;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.youlai.boot.device.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备策略信息视图对象(策略信息面板)
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Schema(description = "设备策略信息视图对象")
|
||||
@Data
|
||||
public class DevicePolicyVO {
|
||||
|
||||
@Schema(description = "ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "设备序列号")
|
||||
private String serialno;
|
||||
|
||||
// ===================== 策略开关(0关闭 1开启) =====================
|
||||
|
||||
@Schema(description = "USB偏好设置(0关闭 1开启)")
|
||||
private Integer usbData;
|
||||
|
||||
@Schema(description = "时间设置管控(0关闭 1开启)")
|
||||
private Integer timeSetting;
|
||||
|
||||
@Schema(description = "存储卡(0关闭 1开启)")
|
||||
private Integer storageCard;
|
||||
|
||||
@Schema(description = "恢复出厂(0关闭 1开启)")
|
||||
private Integer factoryReset;
|
||||
|
||||
@Schema(description = "WiFi热点开关(0关闭 1开启)")
|
||||
private Integer wifiHotspot;
|
||||
|
||||
@Schema(description = "蓝牙开关(0关闭 1开启)")
|
||||
private Integer bluetoothSwitch;
|
||||
|
||||
@Schema(description = "壁纸功能(0关闭 1开启)")
|
||||
private Integer wallpaper;
|
||||
|
||||
@Schema(description = "通知栏显示(0关闭 1开启)")
|
||||
private Integer notificationBar;
|
||||
|
||||
@Schema(description = "状态栏下拉开关(0关闭 1开启)")
|
||||
private Integer statusBarPullDown;
|
||||
|
||||
@Schema(description = "系统导航条显示(0关闭 1开启)")
|
||||
private Integer systemNavBarShow;
|
||||
|
||||
@Schema(description = "系统导航栏设置(0关闭 1开启)")
|
||||
private Integer systemNavBarSetting;
|
||||
|
||||
@Schema(description = "导航条选项(0关闭 1开启)")
|
||||
private Integer navBarOptions;
|
||||
|
||||
@Schema(description = "蓝牙功能开关(0关闭 1开启)")
|
||||
private Integer bluetoothFunction;
|
||||
|
||||
@Schema(description = "OTA升级管控(0关闭 1开启)")
|
||||
private Integer otaUpgrade;
|
||||
|
||||
@Schema(description = "应用安装开关(0关闭 1开启)")
|
||||
private Integer appInstall;
|
||||
|
||||
@Schema(description = "自动旋转(0关闭 1开启)")
|
||||
private Integer autoRotate;
|
||||
|
||||
@Schema(description = "自动亮度(0关闭 1开启)")
|
||||
private Integer autoBrightness;
|
||||
|
||||
@Schema(description = "护眼模式(0关闭 1开启)")
|
||||
private Integer eyeProtection;
|
||||
|
||||
@Schema(description = "深色模式(0关闭 1开启)")
|
||||
private Integer darkMode;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.youlai.boot.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
|
||||
/**
|
||||
* 设备策略信息服务
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
public interface DevicePolicyService extends IService<SnDevicePolicy> {
|
||||
|
||||
/**
|
||||
* 校验设备 SN 是否合法(存在于 sys_sn)
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @return 合法返回 true
|
||||
*/
|
||||
boolean isSnValid(String sn);
|
||||
|
||||
/**
|
||||
* 根据序列号查询设备策略信息(无记录则返回 null)
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @return 策略信息实体(无则返回 null)
|
||||
*/
|
||||
SnDevicePolicy getBySn(String sn);
|
||||
|
||||
/**
|
||||
* 获取设备策略信息;若 SN 合法且数据库无记录,则新建一条(所有开关默认 1)
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @return 策略信息实体;SN 不合法时返回 null
|
||||
*/
|
||||
SnDevicePolicy getOrInitBySn(String sn);
|
||||
|
||||
/**
|
||||
* 全量保存设备策略信息;SN 合法且无记录时新建,存在时更新
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @param policy 策略开关信息(空值字段保持原值)
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveOrUpdateBySn(String sn, SnDevicePolicy policy);
|
||||
|
||||
/**
|
||||
* 单独设置某个策略开关
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @param field 策略字段名(如 usbData)
|
||||
* @param value 开关值(0关闭 1开启)
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateSingleBySn(String sn, String field, Integer value);
|
||||
|
||||
/**
|
||||
* 推送策略信息到对应设备(contentType = 19)
|
||||
* <p>
|
||||
* 推送透传 payload 携带全部策略开关状态,供设备端解析并应用。
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @return 是否推送成功
|
||||
*/
|
||||
boolean pushPolicyToDevice(String sn);
|
||||
}
|
||||
@@ -145,6 +145,17 @@ public interface DeviceService extends IService<SnDeviceInfo> {
|
||||
*/
|
||||
boolean screenLock(String sn);
|
||||
|
||||
/**
|
||||
* 解除设备绑定(管理员端发起)
|
||||
* <p>
|
||||
* 清除设备表 {@code sys_sn.snMobile} 的绑定手机号,并下发推送通知设备端
|
||||
* 在本地标记解绑状态,避免设备下次上报手机号时重新建立绑定。
|
||||
*
|
||||
* @param sn 设备序列号
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean unbindMobile(String sn);
|
||||
|
||||
/**
|
||||
* 打开应用
|
||||
*
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
package com.youlai.boot.device.service.impl;
|
||||
|
||||
import cn.jiguang.sdk.api.PushApi;
|
||||
import cn.jiguang.sdk.bean.push.PushSendParam;
|
||||
import cn.jiguang.sdk.bean.push.PushSendResult;
|
||||
import cn.jiguang.sdk.bean.push.message.custom.CustomMessage;
|
||||
import cn.jiguang.sdk.exception.ApiErrorException;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.device.mapper.DevicePolicyMapper;
|
||||
import com.youlai.boot.device.model.entity.SnDeviceInfo;
|
||||
import com.youlai.boot.device.model.entity.SnDevicePolicy;
|
||||
import com.youlai.boot.device.service.DevicePolicyService;
|
||||
import com.youlai.boot.device.service.DeviceService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 设备策略信息服务实现
|
||||
*
|
||||
* @author TTSTD
|
||||
* @since 2026-08-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DevicePolicyServiceImpl extends ServiceImpl<DevicePolicyMapper, SnDevicePolicy>
|
||||
implements DevicePolicyService {
|
||||
|
||||
/** 策略推送业务码(与设备端 PushExecutor.DEVICE_POLICY 保持一致) */
|
||||
private static final String DEVICE_POLICY = "19";
|
||||
|
||||
private static final String PUSH_APP_KEY = "d779178d9900d4fb5d633678";
|
||||
private static final String PUSH_MASTER_SECRET = "be0e197d30fec7bec118a70d";
|
||||
|
||||
private final DeviceService deviceService;
|
||||
|
||||
private final PushApi pushApi = new PushApi.Builder()
|
||||
.setAppKey(PUSH_APP_KEY)
|
||||
.setMasterSecret(PUSH_MASTER_SECRET)
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public boolean isSnValid(String sn) {
|
||||
if (StringUtils.isBlank(sn)) {
|
||||
return false;
|
||||
}
|
||||
return deviceService.count(
|
||||
new LambdaQueryWrapper<SnDeviceInfo>().eq(SnDeviceInfo::getSerialno, sn)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnDevicePolicy getBySn(String sn) {
|
||||
if (StringUtils.isBlank(sn)) {
|
||||
return null;
|
||||
}
|
||||
return this.lambdaQuery()
|
||||
.eq(SnDevicePolicy::getSerialno, sn)
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnDevicePolicy getOrInitBySn(String sn) {
|
||||
if (StringUtils.isBlank(sn)) {
|
||||
return null;
|
||||
}
|
||||
SnDevicePolicy policy = getBySn(sn);
|
||||
if (policy != null) {
|
||||
return policy;
|
||||
}
|
||||
if (!isSnValid(sn)) {
|
||||
log.warn("策略信息:设备 SN 不合法,不新建记录, sn: {}", sn);
|
||||
return null;
|
||||
}
|
||||
// SN 合法但无记录,新建一条默认策略(默认全开 = 1)
|
||||
policy = buildDefaultPolicy(sn);
|
||||
boolean saved = this.save(policy);
|
||||
if (saved) {
|
||||
log.info("策略信息:新建默认策略成功, sn: {}", sn);
|
||||
return policy;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveOrUpdateBySn(String sn, SnDevicePolicy policy) {
|
||||
if (StringUtils.isBlank(sn)) {
|
||||
return false;
|
||||
}
|
||||
if (!isSnValid(sn)) {
|
||||
log.warn("策略信息:设备 SN 不合法,保存失败, sn: {}", sn);
|
||||
return false;
|
||||
}
|
||||
SnDevicePolicy existing = getBySn(sn);
|
||||
if (existing == null) {
|
||||
SnDevicePolicy entity = new SnDevicePolicy();
|
||||
entity.setSerialno(sn);
|
||||
fillNonNull(entity, policy);
|
||||
boolean saved = this.save(entity);
|
||||
log.info("策略信息:全量新增成功, sn: {}", sn);
|
||||
return saved;
|
||||
}
|
||||
// 只更新非空字段,保留原值
|
||||
fillNonNull(existing, policy);
|
||||
boolean updated = this.updateById(existing);
|
||||
log.info("策略信息:全量更新成功, sn: {}", sn);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSingleBySn(String sn, String field, Integer value) {
|
||||
if (StringUtils.isBlank(sn) || StringUtils.isBlank(field) || value == null) {
|
||||
log.warn("策略信息:单独设置参数不合法, sn: {}, field: {}, value: {}", sn, field, value);
|
||||
return false;
|
||||
}
|
||||
if (!isSnValid(sn)) {
|
||||
log.warn("策略信息:设备 SN 不合法,单独设置失败, sn: {}", sn);
|
||||
return false;
|
||||
}
|
||||
SnDevicePolicy existing = getOrInitBySn(sn);
|
||||
if (existing == null) {
|
||||
return false;
|
||||
}
|
||||
// 仅允许 0/1
|
||||
if (value != 0 && value != 1) {
|
||||
log.warn("策略信息:开关值非法, field: {}, value: {}", field, value);
|
||||
return false;
|
||||
}
|
||||
boolean updated;
|
||||
switch (field) {
|
||||
case "usbData" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getUsbData, value).update();
|
||||
case "timeSetting" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getTimeSetting, value).update();
|
||||
case "storageCard" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getStorageCard, value).update();
|
||||
case "factoryReset" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getFactoryReset, value).update();
|
||||
case "wifiHotspot" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getWifiHotspot, value).update();
|
||||
case "bluetoothSwitch" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getBluetoothSwitch, value).update();
|
||||
case "wallpaper" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getWallpaper, value).update();
|
||||
case "notificationBar" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getNotificationBar, value).update();
|
||||
case "statusBarPullDown" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getStatusBarPullDown, value).update();
|
||||
case "systemNavBarShow" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getSystemNavBarShow, value).update();
|
||||
case "systemNavBarSetting" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getSystemNavBarSetting, value).update();
|
||||
case "navBarOptions" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getNavBarOptions, value).update();
|
||||
case "bluetoothFunction" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getBluetoothFunction, value).update();
|
||||
case "otaUpgrade" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getOtaUpgrade, value).update();
|
||||
case "appInstall" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getAppInstall, value).update();
|
||||
case "autoRotate" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getAutoRotate, value).update();
|
||||
case "autoBrightness" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getAutoBrightness, value).update();
|
||||
case "eyeProtection" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getEyeProtection, value).update();
|
||||
case "darkMode" -> updated = this.lambdaUpdate().eq(SnDevicePolicy::getSerialno, sn)
|
||||
.set(SnDevicePolicy::getDarkMode, value).update();
|
||||
default -> {
|
||||
log.warn("策略信息:不支持的字段, field: {}", field);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
log.info("策略信息:单独设置成功, sn: {}, field: {}, value: {}", sn, field, value);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushPolicyToDevice(String sn) {
|
||||
if (StringUtils.isBlank(sn)) {
|
||||
log.warn("策略推送:设备序列号不能为空");
|
||||
return false;
|
||||
}
|
||||
SnDevicePolicy policy = getBySn(sn);
|
||||
if (policy == null) {
|
||||
log.warn("策略推送:设备策略不存在,sn: {}", sn);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
PushSendParam pushSendParam = buildPolicyPushParam(sn, policy);
|
||||
PushSendResult result = pushApi.send(pushSendParam);
|
||||
log.info("策略推送成功, sn: {}, result: {}", sn, result);
|
||||
return true;
|
||||
} catch (ApiErrorException e) {
|
||||
int httpStatus = e.getStats();
|
||||
int errorCode = e.getApiError().getError().getCode();
|
||||
String errorMessage = e.getApiError().getError().getMessage();
|
||||
log.error("策略推送失败, sn: {}, httpStatus: {}, code: {}, message: {}",
|
||||
sn, httpStatus, errorCode, errorMessage);
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.error("策略推送异常, sn: {}", sn, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建携带全部策略开关的推送参数(contentType = 19,extras 承载各开关状态)
|
||||
*/
|
||||
private PushSendParam buildPolicyPushParam(String sn, SnDevicePolicy policy) {
|
||||
PushSendParam pushSendParam = new PushSendParam();
|
||||
pushSendParam.setPlatform("android");
|
||||
|
||||
Map<String, Set<String>> audience = new HashMap<>();
|
||||
Set<String> aliasSet = new HashSet<>();
|
||||
aliasSet.add(sn);
|
||||
audience.put("alias", aliasSet);
|
||||
pushSendParam.setAudience(audience);
|
||||
|
||||
CustomMessage customMessage = new CustomMessage();
|
||||
customMessage.setTitle("devicePolicy");
|
||||
customMessage.setContentType(DEVICE_POLICY);
|
||||
customMessage.setContent("policy");
|
||||
|
||||
Map<String, Object> extras = new HashMap<>();
|
||||
extras.put("serialno", policy.getSerialno());
|
||||
extras.put("usbData", policy.getUsbData());
|
||||
extras.put("timeSetting", policy.getTimeSetting());
|
||||
extras.put("storageCard", policy.getStorageCard());
|
||||
extras.put("factoryReset", policy.getFactoryReset());
|
||||
extras.put("wifiHotspot", policy.getWifiHotspot());
|
||||
extras.put("bluetoothSwitch", policy.getBluetoothSwitch());
|
||||
extras.put("wallpaper", policy.getWallpaper());
|
||||
extras.put("notificationBar", policy.getNotificationBar());
|
||||
extras.put("statusBarPullDown", policy.getStatusBarPullDown());
|
||||
extras.put("systemNavBarShow", policy.getSystemNavBarShow());
|
||||
extras.put("systemNavBarSetting", policy.getSystemNavBarSetting());
|
||||
extras.put("navBarOptions", policy.getNavBarOptions());
|
||||
extras.put("bluetoothFunction", policy.getBluetoothFunction());
|
||||
extras.put("otaUpgrade", policy.getOtaUpgrade());
|
||||
extras.put("appInstall", policy.getAppInstall());
|
||||
extras.put("autoRotate", policy.getAutoRotate());
|
||||
extras.put("autoBrightness", policy.getAutoBrightness());
|
||||
extras.put("eyeProtection", policy.getEyeProtection());
|
||||
extras.put("darkMode", policy.getDarkMode());
|
||||
customMessage.setExtras(extras);
|
||||
|
||||
pushSendParam.setCustom(customMessage);
|
||||
return pushSendParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建默认策略(所有开关默认开启 = 1)
|
||||
*/
|
||||
private SnDevicePolicy buildDefaultPolicy(String sn) {
|
||||
SnDevicePolicy policy = new SnDevicePolicy();
|
||||
policy.setSerialno(sn);
|
||||
policy.setUsbData(1);
|
||||
policy.setTimeSetting(1);
|
||||
policy.setStorageCard(1);
|
||||
policy.setFactoryReset(1);
|
||||
policy.setWifiHotspot(1);
|
||||
policy.setBluetoothSwitch(1);
|
||||
policy.setWallpaper(1);
|
||||
policy.setNotificationBar(1);
|
||||
policy.setStatusBarPullDown(1);
|
||||
policy.setSystemNavBarShow(1);
|
||||
policy.setSystemNavBarSetting(1);
|
||||
policy.setNavBarOptions(1);
|
||||
policy.setBluetoothFunction(1);
|
||||
policy.setOtaUpgrade(1);
|
||||
policy.setAppInstall(1);
|
||||
policy.setAutoRotate(1);
|
||||
policy.setAutoBrightness(1);
|
||||
policy.setEyeProtection(1);
|
||||
policy.setDarkMode(1);
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 src 中的非空字段填充到 target(用于部分更新,保留原值)
|
||||
*/
|
||||
private void fillNonNull(SnDevicePolicy target, SnDevicePolicy src) {
|
||||
if (src.getUsbData() != null) target.setUsbData(src.getUsbData());
|
||||
if (src.getTimeSetting() != null) target.setTimeSetting(src.getTimeSetting());
|
||||
if (src.getStorageCard() != null) target.setStorageCard(src.getStorageCard());
|
||||
if (src.getFactoryReset() != null) target.setFactoryReset(src.getFactoryReset());
|
||||
if (src.getWifiHotspot() != null) target.setWifiHotspot(src.getWifiHotspot());
|
||||
if (src.getBluetoothSwitch() != null) target.setBluetoothSwitch(src.getBluetoothSwitch());
|
||||
if (src.getWallpaper() != null) target.setWallpaper(src.getWallpaper());
|
||||
if (src.getNotificationBar() != null) target.setNotificationBar(src.getNotificationBar());
|
||||
if (src.getStatusBarPullDown() != null) target.setStatusBarPullDown(src.getStatusBarPullDown());
|
||||
if (src.getSystemNavBarShow() != null) target.setSystemNavBarShow(src.getSystemNavBarShow());
|
||||
if (src.getSystemNavBarSetting() != null) target.setSystemNavBarSetting(src.getSystemNavBarSetting());
|
||||
if (src.getNavBarOptions() != null) target.setNavBarOptions(src.getNavBarOptions());
|
||||
if (src.getBluetoothFunction() != null) target.setBluetoothFunction(src.getBluetoothFunction());
|
||||
if (src.getOtaUpgrade() != null) target.setOtaUpgrade(src.getOtaUpgrade());
|
||||
if (src.getAppInstall() != null) target.setAppInstall(src.getAppInstall());
|
||||
if (src.getAutoRotate() != null) target.setAutoRotate(src.getAutoRotate());
|
||||
if (src.getAutoBrightness() != null) target.setAutoBrightness(src.getAutoBrightness());
|
||||
if (src.getEyeProtection() != null) target.setEyeProtection(src.getEyeProtection());
|
||||
if (src.getDarkMode() != null) target.setDarkMode(src.getDarkMode());
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, SnDeviceInfo> i
|
||||
private static final String DEVICE_CLEAN = "15";
|
||||
private static final String DEVICE_MIRROR = "16";
|
||||
private static final String DEVICE_SCREEN_LOCK = "17";
|
||||
private static final String DEVICE_UNBIND_MOBILE = "18";
|
||||
|
||||
PushApi pushApi = new PushApi.Builder()
|
||||
.setAppKey("d779178d9900d4fb5d633678")
|
||||
@@ -250,8 +251,43 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, SnDeviceInfo> i
|
||||
|
||||
@Override
|
||||
public boolean deleteSn(String sn) {
|
||||
if (sn == null || sn.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
// 前端 deleteByIds 调用 DELETE /{ids},路径参数实际为主键 id
|
||||
Long id = Long.valueOf(sn);
|
||||
boolean deleted = this.removeById(id);
|
||||
if (!deleted) {
|
||||
log.warn("删除设备失败, id: {}", id);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unbindMobile(String sn) {
|
||||
if (sn == null || sn.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
// 1. 清除设备表绑定的手机号
|
||||
boolean updated = this.lambdaUpdate()
|
||||
.set(SnDeviceInfo::getSnMobile, null)
|
||||
.eq(SnDeviceInfo::getSerialno, sn)
|
||||
.update();
|
||||
if (!updated) {
|
||||
log.warn("解除绑定失败, sn: {}", sn);
|
||||
return false;
|
||||
}
|
||||
// 2. 推送解绑命令到设备端,通知其在本地标记解绑状态,
|
||||
// 避免设备下次上报 SIM 卡手机号时重新建立绑定
|
||||
try {
|
||||
PushSendParam pushSendParam = getSinglePushSendParam(sn, DEVICE_UNBIND_MOBILE, "解除绑定", "设备已解除手机号绑定");
|
||||
PushSendResult result = pushApi.send(pushSendParam);
|
||||
log.info("解除绑定推送成功, sn: {}, result: {}", sn, result);
|
||||
} catch (Exception e) {
|
||||
log.warn("解除绑定推送失败, sn: {}, error: {}", sn, e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private PushSendParam getSinglePushSendParam(String sn, String type, String title, String content) {
|
||||
PushSendParam pushSendParam = new PushSendParam();
|
||||
|
||||
Reference in New Issue
Block a user