package com.onekeycall.videotablet.controller.user; import com.google.gson.JsonObject; import com.onekeycall.videotablet.config.PushIdConfig; import com.onekeycall.videotablet.controller.pub.LoginController; import com.onekeycall.videotablet.dto.TokenPair; import com.onekeycall.videotablet.entity.*; import com.onekeycall.videotablet.gson.GsonUtils; import com.onekeycall.videotablet.result.Result; import com.onekeycall.videotablet.service.*; import com.onekeycall.videotablet.utils.DevicePushUtils; import com.onekeycall.videotablet.utils.JwtUtil; import com.onekeycall.videotablet.utils.TextUtils; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @RestController @RequestMapping("/user") public class UserController { private final UserService userService; private final AuthenticationManager authenticationManager; @Autowired private RedisTemplate redisTemplate; @Autowired private JwtUtil jwtUtil; @Autowired private DeviceSnService deviceSnService; @Autowired private DeviceLocationService deviceLocationService; @Autowired private DeviceApkInfoService deviceApkInfoService; @Autowired private ApkIconService apkIconService; @Autowired private ScreenshotService screenshotService; Logger logger = LoggerFactory.getLogger(LoginController.class); @Autowired public UserController(UserService userService, AuthenticationManager authenticationManager) { this.userService = userService; this.authenticationManager = authenticationManager; } /** * 用户app发送绑定推送到手机 * * @param authHeader * @param deviceId * @param userId * @param sn * @return */ @PostMapping("/bind_sn") public Result bindSn( @RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId, @RequestParam(value = "user_id") String userId, @RequestParam(value = "sn") String sn) { logger.info("bindSn: authHeader={}, deviceId={}, userId={}, sn={}", authHeader, deviceId, userId, sn); if (!authHeader.startsWith("Bearer ")) { return Result.error().message("Invalid Authorization header"); } String token = authHeader.substring(7); // 去掉 "Bearer " 前缀 if (!jwtUtil.validateAccessToken(userId, token, deviceId)) { return Result.error().message("Invalid token"); } User user = userService.getUserByUserId(userId); String userPhone = user.getPhone(); DeviceInfo deviceInfo = deviceSnService.findBySn(sn); if (deviceInfo == null) { return Result.error().message("sn not found"); } if (!TextUtils.isEmpty(deviceInfo.getBindPhone())) { return Result.error().message("sn already bind"); } try { String verifyKey = RandomStringUtils.randomAlphanumeric(32); JsonObject params = new JsonObject(); params.addProperty("verify_key", verifyKey); params.addProperty("phone", userPhone); params.addProperty("expire_time", System.currentTimeMillis() + 60 * 1000); // PushUtils.aliyunAsyncPush("1", params.toString(), sn); DevicePushUtils.aliyunAsyncPush(PushIdConfig.BIND_DEVICE, sn, params.toString()); redisTemplate.opsForValue().set(sn, verifyKey, 1, TimeUnit.MINUTES); return Result.ok().message("send message success"); } catch (Exception e) { e.printStackTrace(); return Result.error().message(e.getMessage()); } } @PostMapping("/refresh_token") public Result refreshToken( @RequestHeader(value = "Authorization", required = false) String authHeader, @RequestHeader("Device-ID") String deviceId, @RequestParam(value = "user_id") String userId, @RequestParam("refresh_token") String refreshToken) { logger.info("refreshToken: Authorization={} userId={} deviceId={} refreshToken={}", authHeader, userId, deviceId, refreshToken); try { // 验证refreshToken的有效性 if (!jwtUtil.validateRefreshToken(refreshToken, userId)) { return Result.error().message("无效的refresh token"); } // 从refreshToken中获取用户ID TokenPair tokenPair = jwtUtil.refreshAccessToken(refreshToken, deviceId); // 构建返回结果 Map tokenMap = new HashMap<>(); tokenMap.put("access_token", tokenPair.getAccess_token()); return Result.ok().data(tokenMap); } catch (Exception e) { logger.error("刷新token失败", e); return Result.error().message("刷新token失败: " + e.getMessage()); } } @GetMapping("/get_user_info") public Result getUserInfo( @RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId, @RequestParam(value = "user_id") String userId ) { // 1. 校验 Authorization 头 if (!authHeader.startsWith("Bearer ")) { return Result.error().message("Invalid Authorization header"); } String token = authHeader.substring(7); // 去掉 "Bearer " 前缀 // 2. 校验 Token if (!jwtUtil.validateAccessToken(userId, token, deviceId)) { return Result.error().message("Invalid token"); } User user = userService.getUserByUserId(userId); Map userInfo = new HashMap<>(); userInfo.put("user_id", user.getUserId()); userInfo.put("phone", user.getPhone()); userInfo.put("nickname", user.getNickname()); userInfo.put("avatar", user.getAvatar()); userInfo.put("set_password", !TextUtils.isEmpty(user.getPassword())); return Result.ok().data("user_info", userInfo); } @GetMapping("/get_sn_list") public Result register( @RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId, @RequestParam(value = "user_id") String userId, @RequestParam(value = "sn", required = false) String sn) { // 1. 校验 Authorization 头 if (!authHeader.startsWith("Bearer ")) { return Result.error().message("Invalid Authorization header"); } String token = authHeader.substring(7); // 去掉 "Bearer " 前缀 // 2. 校验 Token if (!jwtUtil.validateAccessToken(userId, token, deviceId)) { return Result.error().message("Invalid token"); } if (TextUtils.isEmpty(sn)) { List deviceInfos = deviceSnService.findByUserId(userId); if (deviceInfos == null || deviceInfos.isEmpty()) { return Result.notFound().message("sn not found"); } else { return Result.ok().data("deviceInfos", deviceInfos); } } else { DeviceInfo deviceInfo = deviceSnService.findBySn(sn); if (deviceInfo == null) { return Result.notFound().message("sn not found"); } if (!deviceInfo.getUserId().equals(userId)) { return Result.error().message("sn not belong to user"); } return Result.ok().data("deviceInfo", deviceInfo); } } @GetMapping("/get_sn_location") public Result getSnLocation( @RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId, @RequestParam(value = "user_id") String userId, @RequestParam(value = "sn") String sn ) { // 1. 校验 Authorization 头 if (!authHeader.startsWith("Bearer ")) { return Result.error().message("Invalid Authorization header"); } String token = authHeader.substring(7); // 去掉 "Bearer " 前缀 // 2. 校验 Token if (!jwtUtil.validateAccessToken(userId, token, deviceId)) { return Result.error().message("Invalid token"); } User user = userService.getUserByUserId(userId); if (user == null) { return Result.error().message("User not found"); } DeviceInfo deviceInfo = deviceSnService.findBySn(sn); if (deviceInfo == null) { return Result.error().message("Device not found"); } if (!deviceInfo.getUserId().equals(userId)) { return Result.error().message("Device not belong to user"); } DeviceLocation deviceLocation = deviceLocationService.getDeviceLocation(sn); if (deviceLocation == null) { return Result.error().message("Device location not found"); } return Result.ok().data("devices_location", deviceLocation); } @GetMapping("/get_device_apk_list") public Result getDeviceApkList(@RequestParam String sn) { DeviceApkInfo deviceApkInfo = deviceApkInfoService.getDeviceApkInfoBySn(sn); if (deviceApkInfo == null || deviceApkInfo.getApkList() == null) { return Result.notFound().message("未找到设备APK信息"); } List apkList = deviceApkInfo.getApkList(); apkList.stream().forEach(new Consumer() { @Override public void accept(ApkInfo apkInfo) { Optional apkIconFileInfo = apkIconService.findMaxVersionCodeByPackageName(apkInfo.getPackageName()); apkIconFileInfo.ifPresent(iconFileInfo -> apkInfo.setIconUrl(iconFileInfo.getFileName())); } }); return Result.ok().data(deviceApkInfo.getApkList()); } @PostMapping("/control_app") public Result controlApp(@RequestParam String sn, @RequestParam(value = "package_name") String packageName, @RequestParam String action ) throws ExecutionException, InterruptedException { Map params = new HashMap<>(); params.put("package_name", packageName); switch (action) { case "open": DevicePushUtils.aliyunAsyncPush(PushIdConfig.OPEN_APP, sn, GsonUtils.toJSONString(params)); break; case "kill": DevicePushUtils.aliyunAsyncPush(PushIdConfig.KILL_APP, sn, GsonUtils.toJSONString(params)); break; case "clear": DevicePushUtils.aliyunAsyncPush(PushIdConfig.CLEAR_APP, sn, GsonUtils.toJSONString(params)); break; case "uninstall": DevicePushUtils.aliyunAsyncPush(PushIdConfig.UNINSTALL_APP, sn, GsonUtils.toJSONString(params)); break; default: return Result.error().message("action is empty"); } return Result.ok().message("success"); } @PostMapping("/screen_snapshot") public Result screenSnapshot(@RequestParam String sn) throws ExecutionException, InterruptedException { Map params = new HashMap<>(); params.put("sn", sn); params.put("timestamp", System.currentTimeMillis()); DevicePushUtils.aliyunAsyncPush(PushIdConfig.SCREEN_SNAPSHOT, sn, GsonUtils.toJSONString(params)); return Result.ok().message("success"); } @GetMapping("/get_snapshot") public Result getScreenSnapshot(@RequestParam String sn) { List screenSnapshot = screenshotService.findBySn(sn); if (screenSnapshot == null) { return Result.notFound().message("Screen snapshot not found"); } return Result.ok().data(screenSnapshot); } @PostMapping("/delete_all_snapshot") public Result deleteAllScreenSnapshot(@RequestParam String sn) { screenshotService.deleteAllBySn(sn); return Result.ok().message("success"); } @PostMapping("/delete_snapshot") public Result deleteScreenSnapshot(@RequestParam String sn, @RequestParam Long id) { if (screenshotService.existsBySnAndId(sn, id)) { boolean deleteSuccess = screenshotService.deleteBySnAndId(sn, id); if (deleteSuccess) { logger.info("deleteScreenSnapshot success, sn: {}, id: {}", sn, id); } else { logger.info("deleteScreenSnapshot fail, sn: {}, id: {}", sn, id); } } else { logger.info("deleteScreenSnapshot not found, sn: {}, id: {}", sn, id); } return Result.ok().message("success"); } }