move api
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
package com.mir4updater.backend;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@MapperScan({"com.mir4updater.backend.mapper", "com.mir4updater.backend.api"})
|
||||
@EnableCaching
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.mir4updater.backend.controller;
|
||||
|
||||
import com.mir4updater.backend.result.Result;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class HelloController {
|
||||
//引入 redis
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@GetMapping("/")
|
||||
public Result getMethodName() {
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/set")
|
||||
public String setRedis(@RequestParam(value = "username") String username) {
|
||||
//存储 key-value 键值对: "username"-"jaychou"
|
||||
stringRedisTemplate.opsForValue().set("username", username);
|
||||
return "redis 存储成功!";
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/get")
|
||||
public String getRedis() {
|
||||
//通过 key 值读取 value
|
||||
String result = stringRedisTemplate.opsForValue().get("username");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.mir4updater.backend.controller.apk;
|
||||
|
||||
import com.mir4updater.backend.entity.ApkInfo;
|
||||
import com.mir4updater.backend.result.Result;
|
||||
import com.mir4updater.backend.service.ApkInfoService;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
public class CheckUpdateController {
|
||||
public static Logger logger = LogManager.getLogger(UploadApkController.class);
|
||||
|
||||
@Autowired
|
||||
private ApkInfoService apkInfoService;
|
||||
|
||||
@GetMapping("/android/check_update")
|
||||
public Result checkUpdate(@RequestParam("pkg") String pkg, @RequestParam(value = "version_code", required = false, defaultValue = "0") Long versionCode) {
|
||||
List<ApkInfo> apkInfoList = apkInfoService.getApkInfo(pkg);
|
||||
Optional<ApkInfo> apkInfoOptional = apkInfoList.stream().max(new Comparator<ApkInfo>() {
|
||||
@Override
|
||||
public int compare(ApkInfo o1, ApkInfo o2) {
|
||||
return Long.compare(o1.getVersionCode(), o2.getVersionCode());
|
||||
}
|
||||
});
|
||||
if (apkInfoOptional.isPresent()) {
|
||||
if (versionCode == 0) {
|
||||
return Result.ok().data("apkInfo", apkInfoOptional.get());
|
||||
} else {
|
||||
if (apkInfoOptional.get().getVersionCode() > versionCode) {
|
||||
return Result.ok().data("apkInfo", apkInfoOptional.get());
|
||||
} else {
|
||||
return Result.ok().message("没有更新");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Result.notFound();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/android/get_all")
|
||||
public Result getAll() {
|
||||
List<ApkInfo> apkInfoList = apkInfoService.getAll();
|
||||
return Result.ok().data("apkInfo", apkInfoList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package com.mir4updater.backend.controller.apk;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.mir4updater.backend.entity.ApkInfo;
|
||||
import com.mir4updater.backend.entity.XapkManifest;
|
||||
import com.mir4updater.backend.result.Result;
|
||||
import com.mir4updater.backend.service.ApkInfoService;
|
||||
import com.mir4updater.backend.utils.FileUtils;
|
||||
import com.mir4updater.backend.utils.HashUtils;
|
||||
import net.dongliu.apk.parser.ApkFile;
|
||||
import net.dongliu.apk.parser.bean.ApkMeta;
|
||||
import net.dongliu.apk.parser.bean.IconFace;
|
||||
import net.dongliu.apk.parser.bean.UseFeature;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tomcat.util.http.fileupload.FileUploadException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@RestController
|
||||
public class UploadApkController {
|
||||
public static Logger logger = LogManager.getLogger(UploadApkController.class);
|
||||
|
||||
@Autowired
|
||||
private ApkInfoService apkInfoService;
|
||||
private static final String UPLOAD_PATH = "uploadApk";
|
||||
private static final String ApkFilePath = "file";
|
||||
private static final String ICON_PATH = "icon";
|
||||
|
||||
@PostMapping("/android/upload_apk")
|
||||
public Result uploadApk(@RequestParam("file") MultipartFile multipartFile) throws Exception {
|
||||
if (multipartFile != null && !multipartFile.isEmpty()) {
|
||||
String projectPath = System.getProperty("user.dir");
|
||||
logger.info("当前项目路径为:" + projectPath);
|
||||
String dirPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ApkFilePath;
|
||||
String iconPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ICON_PATH + File.separator;
|
||||
File dirFile = new File(dirPath);
|
||||
if (!dirFile.exists()) {
|
||||
dirFile.mkdirs();
|
||||
}
|
||||
File iconFilePath = new File(iconPath);
|
||||
if (!iconFilePath.exists()) {
|
||||
iconFilePath.mkdirs();
|
||||
}
|
||||
//1.接收上传的应用,写入到硬盘
|
||||
String originalFilename = multipartFile.getOriginalFilename();
|
||||
File file = new File(dirPath + File.separator + originalFilename);
|
||||
try {
|
||||
multipartFile.transferTo(file);
|
||||
} catch (FileUploadException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
|
||||
if (!isAPK(file) && !isXAPK(file)) {
|
||||
return Result.error().message("请上传apk或xapk文件");
|
||||
}
|
||||
String md5 = HashUtils.getFileMD5(file);
|
||||
logger.info("file md5 = " + md5);
|
||||
String sha1 = HashUtils.calculateSHA1(file);
|
||||
logger.info("file sha1 = " + sha1);
|
||||
String sha256 = HashUtils.calculateSHA256(file);
|
||||
logger.info("file sha256 = " + sha256);
|
||||
|
||||
File iconFile = new File(iconPath + md5 + ".png");
|
||||
|
||||
if (isAPK(file)) {
|
||||
logger.info("file type is apk");
|
||||
//调用工具类解析apk
|
||||
//analysisAPK(dirFile.getAbsolutePath(), file.getAbsolutePath());
|
||||
ApkFile apkFile = new ApkFile(file);
|
||||
ApkMeta apkMeta = apkFile.getApkMeta();
|
||||
|
||||
logger.info(apkMeta.getLabel());
|
||||
logger.info(apkMeta.getPackageName());
|
||||
logger.info(apkMeta.getVersionCode());
|
||||
logger.info(apkMeta.getVersionName());
|
||||
for (UseFeature feature : apkMeta.getUsesFeatures()) {
|
||||
logger.info(feature.getName());
|
||||
}
|
||||
|
||||
ApkInfo apkInfo = new ApkInfo();
|
||||
apkInfo.setAppName(apkMeta.getLabel());
|
||||
apkInfo.setPackageName(apkMeta.getPackageName());
|
||||
apkInfo.setVersionCode(apkMeta.getVersionCode());
|
||||
apkInfo.setVersionName(apkMeta.getVersionName());
|
||||
apkInfo.setSha1(sha1);
|
||||
apkInfo.setFilePath(file.getPath());
|
||||
apkInfo.setIconPath(iconFile.getPath());
|
||||
apkInfo.setSize(file.length());
|
||||
apkInfo.setXapk(false);
|
||||
|
||||
try {
|
||||
apkInfoService.addApk(apkInfo);
|
||||
|
||||
List<IconFace> allIcons = apkFile.getAllIcons();
|
||||
Optional<IconFace> maxNumber = allIcons.stream().filter(iconFace -> iconFace.getPath().endsWith(".png"))
|
||||
.max(Comparator.comparingInt(o -> o.getData().length));
|
||||
if (maxNumber.isPresent()) {
|
||||
logger.info(maxNumber.get().getPath());
|
||||
FileUtils.byteToFile(maxNumber.get().getData(), iconFile);
|
||||
}
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
e.printStackTrace();
|
||||
logger.info(e.getMessage());
|
||||
return Result.error().message("apk文件无效");
|
||||
}
|
||||
|
||||
return Result.ok().message("上传成功");
|
||||
} else if (isXAPK(file)) {
|
||||
logger.info("file type is xapk");
|
||||
try (ZipFile zipFile = new ZipFile(file)) {
|
||||
// 遍历ZIP文件中的所有条目
|
||||
zipFile.stream().forEach(zipEntry -> {
|
||||
logger.info(zipEntry.getName());
|
||||
});
|
||||
Optional<? extends ZipEntry> manifestOptional = zipFile.stream().filter(zipEntry -> "manifest.json".equals(zipEntry.getName())).findFirst();
|
||||
if (manifestOptional.isPresent()) {
|
||||
ZipEntry manifestZipEntry = manifestOptional.get();
|
||||
try {
|
||||
InputStream manifestInputStream = zipFile.getInputStream(manifestZipEntry);
|
||||
// 读取文件内容
|
||||
String content = new String(manifestInputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
// 创建ObjectMapper实例
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
// 将Java对象转换为JSON字符串
|
||||
String jsonString = objectMapper.writeValueAsString(content);
|
||||
XapkManifest xapkManifest = objectMapper.readValue(content, XapkManifest.class);
|
||||
// logger.info(xapkManifest);
|
||||
logger.info(xapkManifest.getName());
|
||||
logger.info(xapkManifest.getPackage_name());
|
||||
logger.info(xapkManifest.getVersion_code());
|
||||
logger.info(xapkManifest.getVersion_name());
|
||||
|
||||
ApkInfo apkInfo = new ApkInfo();
|
||||
apkInfo.setAppName(xapkManifest.getName());
|
||||
apkInfo.setPackageName(xapkManifest.getPackage_name());
|
||||
apkInfo.setVersionCode(xapkManifest.getVersion_code());
|
||||
apkInfo.setVersionName(xapkManifest.getVersion_name());
|
||||
apkInfo.setSha1(sha1);
|
||||
apkInfo.setFilePath(file.getPath());
|
||||
apkInfo.setIconPath(iconFile.getPath());
|
||||
apkInfo.setSize(file.length());
|
||||
apkInfo.setXapk(true);
|
||||
apkInfo.setXapk_version(xapkManifest.getXapk_version());
|
||||
|
||||
try {
|
||||
apkInfoService.addApk(apkInfo);
|
||||
|
||||
String iconName = xapkManifest.getIcon();
|
||||
Optional<? extends ZipEntry> iconNameOptional = zipFile.stream().filter(zipEntry -> iconName.equals(zipEntry.getName())).findFirst();
|
||||
if (iconNameOptional.isPresent()) {
|
||||
ZipEntry iconZipEntry = iconNameOptional.get();
|
||||
InputStream iconInputStream = zipFile.getInputStream(iconZipEntry);
|
||||
FileUtils.byteToFile(iconInputStream.readAllBytes(), iconFile);
|
||||
}
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
logger.info(e.getMessage());
|
||||
// 手动回滚事务
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return Result.error().message("xapk文件重复");
|
||||
}
|
||||
|
||||
return Result.ok().message("上传成功");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("读取文件失败: " + manifestZipEntry.getName());
|
||||
return Result.error().message("读取文件失败");
|
||||
}
|
||||
} else {
|
||||
return Result.error().message("读取xapk配置失败");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("打开ZIP文件失败: " + e.getMessage());
|
||||
return Result.error().message("打开ZIP文件失败");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Result.error().message("文件为空");
|
||||
}
|
||||
return Result.error().message("未知错误");
|
||||
}
|
||||
|
||||
// 判断是否为APK
|
||||
public static boolean isAPK(File file) {
|
||||
// 检查扩展名是否为.apk
|
||||
if (file.getName().toLowerCase().endsWith(".apk")) {
|
||||
// 进一步验证是否为有效的APK文件
|
||||
try (ZipFile zipFile = new ZipFile(file)) {
|
||||
return zipFile.getEntry("AndroidManifest.xml") != null;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断是否为XAPK
|
||||
public static boolean isXAPK(File file) {
|
||||
// 检查扩展名是否为.xapk
|
||||
if (file.getName().toLowerCase().endsWith(".xapk")) {
|
||||
return true; // 扩展名为.xapk通常直接视为XAPK
|
||||
}
|
||||
|
||||
// 若扩展名不明确,检查内容结构
|
||||
try (ZipFile zipFile = new ZipFile(file)) {
|
||||
boolean hasJson = zipFile.getEntry("manifest.json") != null;
|
||||
|
||||
boolean hasAPK = false;
|
||||
// boolean hasOBB = false;
|
||||
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
// 检查是否包含APK文件
|
||||
if (name.endsWith(".apk")) {
|
||||
hasAPK = true;
|
||||
}
|
||||
// 检查是否包含OBB目录
|
||||
// if (name.startsWith("Android/obb/")) {
|
||||
// hasOBB = true;
|
||||
// }
|
||||
}
|
||||
// XAPK通常包含至少一个APK或OBB数据
|
||||
return hasAPK || hasJson;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void analysisAPK(String dirPath, String filePath) throws Exception {
|
||||
logger.info(filePath);
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
Process process = runtime.exec("aapt d --values badging " + filePath, null, new File(dirPath));
|
||||
InputStream inputStream = process.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
inputStream.close();
|
||||
String strOutput = sb.toString();
|
||||
logger.info(strOutput);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.mir4updater.backend.controller.apk;
|
||||
|
||||
import com.mir4updater.backend.dto.PakFileInfo;
|
||||
import com.mir4updater.backend.result.Result;
|
||||
import com.mir4updater.backend.utils.HashUtils;
|
||||
import jakarta.validation.Valid;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
@RestController
|
||||
public class UploadPakFileController {
|
||||
public static Logger logger = LogManager.getLogger(UploadApkController.class);
|
||||
private static final String UPLOAD_PATH = "uploadPak";
|
||||
|
||||
static {
|
||||
String projectPath = System.getProperty("user.dir");
|
||||
String dirPath = projectPath + File.separator + UPLOAD_PATH + File.separator;
|
||||
File dirFile = new File(dirPath);
|
||||
if (!dirFile.exists()) {
|
||||
logger.info("mkdir " + dirFile.getPath() + "\t" + dirFile.mkdirs());
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@PostMapping(path = "/android/upload_pak_file")
|
||||
public Result uploadPakFile(@Valid @ModelAttribute PakFileInfo pakFileInfo) throws Exception {
|
||||
String jsonUuid = pakFileInfo.json_uuid();
|
||||
if (!Boolean.TRUE.equals(stringRedisTemplate.hasKey("data:" + Collections.singletonMap("uuid", jsonUuid)))) {
|
||||
return Result.error().message("json_uuid不存在");
|
||||
}
|
||||
InputStream inputStream = pakFileInfo.file().getInputStream();
|
||||
String sha1 = HashUtils.calculateSHA1(inputStream);
|
||||
logger.info("origin_sha1 = " + pakFileInfo.sha1());
|
||||
logger.info("sha1 = " + sha1);
|
||||
if (!sha1.equalsIgnoreCase(pakFileInfo.sha1())) {
|
||||
return Result.error().message("sha1不匹配");
|
||||
}
|
||||
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/android/upload_pak_file_old")
|
||||
public Result uploadPakFile(@RequestParam("file") MultipartFile multipartFile, @RequestParam("name") String name,
|
||||
@RequestParam("path") String path, @RequestParam("sha1") String origin_sha1,
|
||||
@RequestParam("json_id") String jsonId) throws Exception {
|
||||
// if (TextUtils.isEmpty(jsonId)){
|
||||
// return Result.error().message("json_id不能为空");
|
||||
// }
|
||||
InputStream inputStream = multipartFile.getInputStream();
|
||||
String sha1 = HashUtils.calculateSHA1(inputStream);
|
||||
logger.info("origin_sha1 = " + origin_sha1);
|
||||
logger.info("sha1 = " + sha1);
|
||||
if (!sha1.equalsIgnoreCase(origin_sha1)) {
|
||||
return Result.error().message("sha1不匹配");
|
||||
}
|
||||
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.mir4updater.backend.controller.apk;
|
||||
|
||||
import com.mir4updater.backend.dto.PakInfoJsonRequest;
|
||||
import com.mir4updater.backend.result.Result;
|
||||
import com.mir4updater.backend.service.PakInfoService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/android/upload_pak_info")
|
||||
public class UploadPakInfoController {
|
||||
|
||||
@Autowired
|
||||
private PakInfoService pakInfoService;
|
||||
|
||||
@PostMapping
|
||||
public Result createData(@Valid @RequestBody PakInfoJsonRequest request) {
|
||||
String id = pakInfoService.processData(request);
|
||||
|
||||
return Result.ok().data(Collections.singletonMap("uuid", id));
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/mir4updater/backend/dto/PakFileInfo.java
Normal file
19
src/main/java/com/mir4updater/backend/dto/PakFileInfo.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.mir4updater.backend.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public record PakFileInfo(
|
||||
@NotNull(message = "文件不能为空")
|
||||
MultipartFile file,
|
||||
@NotBlank(message = "name不能为空")
|
||||
String name,
|
||||
@NotBlank(message = "path不能为空")
|
||||
String path,
|
||||
@NotBlank(message = "sha1不能为空")
|
||||
String sha1,
|
||||
@NotBlank(message = "uuid不能为空")
|
||||
String json_uuid
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mir4updater.backend.dto;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
public record PakInfoJsonRequest(
|
||||
ObjectNode json
|
||||
) {
|
||||
}
|
||||
136
src/main/java/com/mir4updater/backend/entity/ApkInfo.java
Normal file
136
src/main/java/com/mir4updater/backend/entity/ApkInfo.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.mir4updater.backend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "apk_info")
|
||||
@EqualsAndHashCode
|
||||
|
||||
public class ApkInfo implements Serializable {
|
||||
|
||||
@Embedded
|
||||
private ApkInfoPK id; // 复合主键
|
||||
|
||||
@Id
|
||||
@Column(name = "app_id", nullable = false, length = 20)
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
|
||||
int appId;
|
||||
|
||||
@Column(name = "app_name", nullable = false, length = 128)
|
||||
String appName;
|
||||
|
||||
@Column(name = "package_name", nullable = false)
|
||||
String packageName;
|
||||
|
||||
@Column(name = "version_code", nullable = false, length = 11)
|
||||
long versionCode;
|
||||
|
||||
@Column(name = "version_name", nullable = false)
|
||||
String versionName;
|
||||
|
||||
@Column(name = "sha1", nullable = false, length = 160)
|
||||
String sha1;
|
||||
|
||||
@Column(name = "file_path", nullable = false)
|
||||
String filePath;
|
||||
|
||||
@Column(name = "icon_path", nullable = false)
|
||||
String iconPath;
|
||||
|
||||
@Column(name = "size", nullable = false, length = 20)
|
||||
long size;
|
||||
|
||||
@Column(name = "is_xapk", columnDefinition = "bit(1) default 0")
|
||||
boolean xapk = false;
|
||||
|
||||
@Column(name = "xapk_version", length = 11)
|
||||
int xapk_version;
|
||||
|
||||
public ApkInfo() {
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public long getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
public void setVersionCode(long versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
public String getVersionName() {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
this.versionName = versionName;
|
||||
}
|
||||
|
||||
public String getSha1() {
|
||||
return sha1;
|
||||
}
|
||||
|
||||
public void setSha1(String sha1) {
|
||||
this.sha1 = sha1;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public String getIconPath() {
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
public void setIconPath(String iconPath) {
|
||||
this.iconPath = iconPath;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public boolean isXapk() {
|
||||
return xapk;
|
||||
}
|
||||
|
||||
public void setXapk(boolean xapk) {
|
||||
this.xapk = xapk;
|
||||
}
|
||||
|
||||
public int getXapk_version() {
|
||||
return xapk_version;
|
||||
}
|
||||
|
||||
public void setXapk_version(int xapk_version) {
|
||||
this.xapk_version = xapk_version;
|
||||
}
|
||||
}
|
||||
56
src/main/java/com/mir4updater/backend/entity/ApkInfoPK.java
Normal file
56
src/main/java/com/mir4updater/backend/entity/ApkInfoPK.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package com.mir4updater.backend.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
// 使用 @EmbeddedId
|
||||
@Embeddable
|
||||
public class ApkInfoPK implements Serializable {
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
String sha1;
|
||||
|
||||
@Column(name = "package_name", insertable = false, updatable = false)
|
||||
String packageName;
|
||||
|
||||
@Column(name = "version_code", insertable = false, updatable = false)
|
||||
String versionCode;
|
||||
|
||||
// getters, setters, equals, hashCode
|
||||
|
||||
public ApkInfoPK() {
|
||||
|
||||
}
|
||||
|
||||
public ApkInfoPK(String sha1, String packageName, String version_code) {
|
||||
this.sha1 = sha1;
|
||||
this.packageName = packageName;
|
||||
this.versionCode = version_code;
|
||||
}
|
||||
|
||||
public String getSha1() {
|
||||
return sha1;
|
||||
}
|
||||
|
||||
public void setSha1(String sha1) {
|
||||
this.sha1 = sha1;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public String getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
public void setVersionCode(String version_code) {
|
||||
this.versionCode = version_code;
|
||||
}
|
||||
}
|
||||
43
src/main/java/com/mir4updater/backend/entity/PakInfo.java
Normal file
43
src/main/java/com/mir4updater/backend/entity/PakInfo.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.mir4updater.backend.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class PakInfo implements Serializable {
|
||||
@Id
|
||||
@UuidGenerator(style = UuidGenerator.Style.TIME)
|
||||
private UUID uuid;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(nullable = false, columnDefinition = "JSON")
|
||||
private Map<String, Object> json;
|
||||
|
||||
public PakInfo() {
|
||||
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.uuid = id;
|
||||
}
|
||||
|
||||
public Map<String, Object> getJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
public void setJson(Map<String, Object> json) {
|
||||
this.json = json;
|
||||
}
|
||||
}
|
||||
57
src/main/java/com/mir4updater/backend/entity/UserInfo.java
Normal file
57
src/main/java/com/mir4updater/backend/entity/UserInfo.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.mir4updater.backend.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class UserInfo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 4389921857866822592L;
|
||||
|
||||
long user_id;
|
||||
String user_name;
|
||||
String user_phone;
|
||||
String salt;
|
||||
String regist_time;
|
||||
|
||||
public long getUser_id() {
|
||||
return user_id;
|
||||
}
|
||||
|
||||
public void setUser_id(long user_id) {
|
||||
this.user_id = user_id;
|
||||
}
|
||||
|
||||
public String getUser_name() {
|
||||
return user_name;
|
||||
}
|
||||
|
||||
public void setUser_name(String user_name) {
|
||||
this.user_name = user_name;
|
||||
}
|
||||
|
||||
public String getUser_phone() {
|
||||
return user_phone;
|
||||
}
|
||||
|
||||
public void setUser_phone(String user_phone) {
|
||||
this.user_phone = user_phone;
|
||||
}
|
||||
|
||||
public String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(String salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public String getRegist_time() {
|
||||
return regist_time;
|
||||
}
|
||||
|
||||
public void setRegist_time(String regist_time) {
|
||||
this.regist_time = regist_time;
|
||||
}
|
||||
|
||||
}
|
||||
149
src/main/java/com/mir4updater/backend/entity/XapkManifest.java
Normal file
149
src/main/java/com/mir4updater/backend/entity/XapkManifest.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package com.mir4updater.backend.entity;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class XapkManifest implements Serializable {
|
||||
|
||||
public int xapk_version;
|
||||
public String package_name;
|
||||
public String name;
|
||||
public long version_code;
|
||||
public String version_name;
|
||||
public String min_sdk_version;
|
||||
public String target_sdk_version;
|
||||
public List<String> permissions;
|
||||
public List<String> split_configs;
|
||||
public long total_size;
|
||||
public String icon;
|
||||
public List<SplitApk> split_apks;
|
||||
|
||||
public static class SplitApk {
|
||||
public String file;
|
||||
public String id;
|
||||
}
|
||||
|
||||
public XapkManifest() {
|
||||
}
|
||||
|
||||
public XapkManifest(int xapk_version, String package_name, String name, long version_code, String version_name,
|
||||
String min_sdk_version, String target_sdk_version, List<String> permissions, List<String> split_configs,
|
||||
long total_size, String icon, List<SplitApk> split_apks) {
|
||||
this.xapk_version = xapk_version;
|
||||
this.package_name = package_name;
|
||||
this.name = name;
|
||||
this.version_code = version_code;
|
||||
this.version_name = version_name;
|
||||
this.min_sdk_version = min_sdk_version;
|
||||
this.target_sdk_version = target_sdk_version;
|
||||
this.permissions = permissions;
|
||||
this.split_configs = split_configs;
|
||||
this.total_size = total_size;
|
||||
this.icon = icon;
|
||||
this.split_apks = split_apks;
|
||||
}
|
||||
|
||||
public int getXapk_version() {
|
||||
return xapk_version;
|
||||
}
|
||||
|
||||
public void setXapk_version(int xapk_version) {
|
||||
this.xapk_version = xapk_version;
|
||||
}
|
||||
|
||||
public String getPackage_name() {
|
||||
return package_name;
|
||||
}
|
||||
|
||||
public void setPackage_name(String package_name) {
|
||||
this.package_name = package_name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getVersion_code() {
|
||||
return version_code;
|
||||
}
|
||||
|
||||
public void setVersion_code(long version_code) {
|
||||
this.version_code = version_code;
|
||||
}
|
||||
|
||||
public String getVersion_name() {
|
||||
return version_name;
|
||||
}
|
||||
|
||||
public void setVersion_name(String version_name) {
|
||||
this.version_name = version_name;
|
||||
}
|
||||
|
||||
public String getMin_sdk_version() {
|
||||
return min_sdk_version;
|
||||
}
|
||||
|
||||
public void setMin_sdk_version(String min_sdk_version) {
|
||||
this.min_sdk_version = min_sdk_version;
|
||||
}
|
||||
|
||||
public String getTarget_sdk_version() {
|
||||
return target_sdk_version;
|
||||
}
|
||||
|
||||
public void setTarget_sdk_version(String target_sdk_version) {
|
||||
this.target_sdk_version = target_sdk_version;
|
||||
}
|
||||
|
||||
public List<String> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public List<String> getSplit_configs() {
|
||||
return split_configs;
|
||||
}
|
||||
|
||||
public void setSplit_configs(List<String> split_configs) {
|
||||
this.split_configs = split_configs;
|
||||
}
|
||||
|
||||
public long getTotal_size() {
|
||||
return total_size;
|
||||
}
|
||||
|
||||
public void setTotal_size(long total_size) {
|
||||
this.total_size = total_size;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public List<SplitApk> getSplit_apks() {
|
||||
return split_apks;
|
||||
}
|
||||
|
||||
public void setSplit_apks(List<SplitApk> split_apks) {
|
||||
this.split_apks = split_apks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/mir4updater/backend/redis/RedisConfig.java
Normal file
28
src/main/java/com/mir4updater/backend/redis/RedisConfig.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.mir4updater.backend.redis;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
// Key序列化为字符串
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
// Value序列化为JSON
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
|
||||
// 对于Hash结构,根据需要配置
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mir4updater.backend.repository;
|
||||
|
||||
|
||||
import com.mir4updater.backend.entity.ApkInfo;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ApkInfoRepository extends JpaRepository<ApkInfo, Long> {
|
||||
// 自定义查询方法(可选)
|
||||
List<ApkInfo> findApkInfosByPackageName(String pkg);
|
||||
List<ApkInfo> findAll();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mir4updater.backend.repository;
|
||||
|
||||
import com.mir4updater.backend.entity.PakInfo;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PakInfoRepository extends JpaRepository<PakInfo, Long> {
|
||||
}
|
||||
60
src/main/java/com/mir4updater/backend/result/JsonData.java
Normal file
60
src/main/java/com/mir4updater/backend/result/JsonData.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.mir4updater.backend.result;
|
||||
|
||||
/**
|
||||
* 接口返回工具类
|
||||
*/
|
||||
public class JsonData {
|
||||
private int code;
|
||||
private Object data;
|
||||
private String msg;
|
||||
|
||||
public JsonData() {
|
||||
}
|
||||
|
||||
public JsonData(int code, Object data) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public JsonData(int code, Object data, String msg) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public static JsonData buildSuccess(Object data) {
|
||||
return new JsonData(0, data);
|
||||
}
|
||||
|
||||
public static JsonData buildError(String msg) {
|
||||
return new JsonData(-1, "", msg);
|
||||
}
|
||||
|
||||
public static JsonData buildError(int code, String msg) {
|
||||
return new JsonData(code, "", msg);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
129
src/main/java/com/mir4updater/backend/result/Result.java
Normal file
129
src/main/java/com/mir4updater/backend/result/Result.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package com.mir4updater.backend.result;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统一返回格式类
|
||||
*
|
||||
* @author 爷爷的茶七里香
|
||||
* @date 2022/05/30
|
||||
*/
|
||||
public class Result {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 返回的消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 放置响应的数据
|
||||
*/
|
||||
private Map<String, Object> data = new HashMap<>();
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
/** 以下是定义一些常用到的格式,可以看到调用了我们创建的枚举类 */
|
||||
|
||||
public static Result ok() {
|
||||
Result r = new Result();
|
||||
r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
|
||||
r.setCode(ResultCodeEnum.SUCCESS.getCode());
|
||||
r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Result error() {
|
||||
Result r = new Result();
|
||||
r.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess());
|
||||
r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());
|
||||
r.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage());
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Result notFound() {
|
||||
Result r = new Result();
|
||||
r.setSuccess(ResultCodeEnum.NOT_FOUND.getSuccess());
|
||||
r.setCode(ResultCodeEnum.NOT_FOUND.getCode());
|
||||
r.setMessage(ResultCodeEnum.NOT_FOUND.getMessage());
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Result setResult(ResultCodeEnum resultCodeEnum) {
|
||||
Result r = new Result();
|
||||
r.setSuccess(resultCodeEnum.getSuccess());
|
||||
r.setCode(resultCodeEnum.getCode());
|
||||
r.setMessage(resultCodeEnum.getMessage());
|
||||
return r;
|
||||
}
|
||||
|
||||
public Result success(Boolean success) {
|
||||
this.setSuccess(success);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result message(String message) {
|
||||
this.setMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result code(Integer code) {
|
||||
this.setCode(code);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result data(String key, Object value) {
|
||||
this.data.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result data(Map<String, Object> map) {
|
||||
this.setData(map);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 以下是get/set方法,如果项目有集成lombok可以使用@Data注解代替 */
|
||||
|
||||
public Boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(Boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Map<String, Object> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Map<String, Object> data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.mir4updater.backend.result;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*
|
||||
* @author 爷爷的茶七里香
|
||||
* @date 2022/05/30
|
||||
*/
|
||||
public enum ResultCodeEnum {
|
||||
|
||||
SUCCESS(true, 20000, "成功"),
|
||||
|
||||
UNKNOWN_REASON(false, 20001, "未知错误"),
|
||||
NOT_FOUND(true, 20004, "没有数据");
|
||||
|
||||
|
||||
private final Boolean success;
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
ResultCodeEnum(Boolean success, Integer code, String message) {
|
||||
this.success = success;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultCodeEnum{" + "success=" + success + ", code=" + code + ", message='" + message + '\'' + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.mir4updater.backend.service;
|
||||
|
||||
import com.mir4updater.backend.entity.ApkInfo;
|
||||
import com.mir4updater.backend.repository.ApkInfoRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ApkInfoService {
|
||||
@Autowired
|
||||
private ApkInfoRepository apkInfoRepository;
|
||||
|
||||
public void addApk(ApkInfo user) {
|
||||
apkInfoRepository.save(user);
|
||||
}
|
||||
|
||||
public List<ApkInfo> getApkInfo(String pkg) {
|
||||
return apkInfoRepository.findApkInfosByPackageName(pkg);
|
||||
}
|
||||
|
||||
public List<ApkInfo> getAll() {
|
||||
return apkInfoRepository.findAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.mir4updater.backend.service;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.mir4updater.backend.dto.PakInfoJsonRequest;
|
||||
import com.mir4updater.backend.entity.PakInfo;
|
||||
import com.mir4updater.backend.repository.PakInfoRepository;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.validation.ValidationException;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class PakInfoService {
|
||||
private final PakInfoRepository pakInfoRepository;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
public PakInfoService(PakInfoRepository pakInfoRepository, RedisTemplate<String, Object> redisTemplate) {
|
||||
this.pakInfoRepository = pakInfoRepository;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public String processData(PakInfoJsonRequest request) {
|
||||
// 创建实体
|
||||
PakInfo entity = new PakInfo();
|
||||
entity.setJson(convertToMap(request.json()));
|
||||
// 保存到数据库
|
||||
pakInfoRepository.save(entity);
|
||||
// 保存到Redis,有效期1小时
|
||||
redisTemplate.opsForValue().set(
|
||||
"data:" + Collections.singletonMap("uuid", entity.getId()),
|
||||
request.json()
|
||||
);
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
private Map<String, Object> convertToMap(ObjectNode node) {
|
||||
try {
|
||||
return new ObjectMapper().convertValue(node, new TypeReference<>() {});
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ValidationException("Invalid JSON structure");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.mir4updater.backend.utils;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
//生成验证码,并将验证码存入session中,以ip为key
|
||||
public class CheckCodeUtils {
|
||||
public static BufferedImage getCheckCode(HttpServletRequest request, String ip) {
|
||||
int width = 60, height = 30;
|
||||
//创建一个图像,宽60 高30
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = image.getGraphics();
|
||||
Random random = new Random();
|
||||
g.setColor(getRandomColor(200, 250));
|
||||
g.fillRect(0, 0, width, height);
|
||||
g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
|
||||
g.setColor(getRandomColor(160, 200));
|
||||
//干扰线生成
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int x = random.nextInt(width);
|
||||
int y = random.nextInt(height);
|
||||
int xl = random.nextInt(12);
|
||||
int yl = random.nextInt(12);
|
||||
g.drawLine(x, y, x + xl, y + yl);
|
||||
}
|
||||
String strCode = "";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
String strNumber = String.valueOf(random.nextInt(10));
|
||||
strCode = strCode + strNumber;
|
||||
//设置字体颜色
|
||||
g.drawString(strNumber, 13 * i + 6, 20);
|
||||
}
|
||||
System.out.println("当前ip" + ip);
|
||||
System.out.println("当前验证码" + strCode);
|
||||
request.getSession().setAttribute(ip, strCode);
|
||||
g.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 随机获取颜色的方法
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Color getRandomColor(int fc, int bc) {
|
||||
Random random = new Random();
|
||||
Color reandomColor = null;
|
||||
if (fc > 255) {
|
||||
fc = 255;
|
||||
}
|
||||
if (bc > 255) {
|
||||
bc = 255;
|
||||
}
|
||||
int r = fc + random.nextInt(bc - fc);
|
||||
int g = fc + random.nextInt(bc - fc);
|
||||
int b = fc + random.nextInt(bc - fc);
|
||||
reandomColor = new Color(r, g, b);
|
||||
return reandomColor;
|
||||
}
|
||||
|
||||
}
|
||||
190
src/main/java/com/mir4updater/backend/utils/CreateImageCode.java
Normal file
190
src/main/java/com/mir4updater/backend/utils/CreateImageCode.java
Normal file
@@ -0,0 +1,190 @@
|
||||
package com.mir4updater.backend.utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 生成 数字+字母 验证码图片
|
||||
*/
|
||||
public class CreateImageCode {
|
||||
//图片的宽度
|
||||
private int width = 160;
|
||||
//图片的高度
|
||||
private int height = 40;
|
||||
//验证码字符个数
|
||||
private int codeCount = 4;
|
||||
//验证码干扰线数
|
||||
private int lineCount = 20;
|
||||
//验证码
|
||||
private String code = null;
|
||||
//验证码图片buffer
|
||||
private BufferedImage buffImg = null;
|
||||
//创建一个生成随机数对象
|
||||
Random random = new Random();
|
||||
|
||||
//构造函数
|
||||
public CreateImageCode() {
|
||||
creatImage();
|
||||
}
|
||||
|
||||
public CreateImageCode(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
creatImage();
|
||||
}
|
||||
|
||||
public CreateImageCode(int width, int height, int codeCount) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.codeCount = codeCount;
|
||||
creatImage();
|
||||
}
|
||||
|
||||
public CreateImageCode(int width, int height, int codeCount, int lineCount) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.codeCount = codeCount;
|
||||
this.lineCount = lineCount;
|
||||
creatImage();
|
||||
}
|
||||
|
||||
//生成图片
|
||||
private void creatImage() {
|
||||
//字体的宽度
|
||||
int fontWidth = width / codeCount;
|
||||
//字体的高度
|
||||
int fontHeight = height - 5;
|
||||
int codeY = height - 8;
|
||||
//图像buff
|
||||
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = buffImg.getGraphics();
|
||||
//设置背景色
|
||||
g.setColor(getRandColor(200, 250));
|
||||
g.fillRect(0, 0, width, height);
|
||||
//设置字体
|
||||
Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
|
||||
g.setFont(font);
|
||||
//设置干扰线
|
||||
for (int i = 0; i < lineCount; i++) {
|
||||
int xs = random.nextInt(width);
|
||||
int ys = random.nextInt(height);
|
||||
int xe = xs + random.nextInt(width);
|
||||
int ye = ys + random.nextInt(height);
|
||||
g.setColor(getRandColor(1, 255));
|
||||
g.drawLine(xs, ys, xe, ye);
|
||||
}
|
||||
//添加噪点
|
||||
float yawpRate = 0.01f;//噪声率
|
||||
int area = (int) (yawpRate * width * height);
|
||||
for (int i = 0; i < area; i++) {
|
||||
int x = random.nextInt(width);
|
||||
int y = random.nextInt(height);
|
||||
buffImg.setRGB(x, y, random.nextInt(255));
|
||||
}
|
||||
String str1 = randomStr(codeCount);//得到随机字符
|
||||
this.code = str1;
|
||||
for (int i = 0; i < codeCount; i++) {
|
||||
String strRand = str1.substring(i, i + 1);
|
||||
g.setColor(getRandColor(1, 255));
|
||||
//g.drawString(str,x,y) ---- str:为要画出来的东西;x 和 y :表示要画的东西最左则字符的基线位于此图形上下文坐标系的 (x,y)位置处
|
||||
g.drawString(strRand, i * fontWidth + 3, codeY);
|
||||
}
|
||||
}
|
||||
|
||||
//得到随机字符
|
||||
private String randomStr(int n) {
|
||||
String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567990";
|
||||
String str2 = "";
|
||||
int len = str1.length() - 1;
|
||||
double r;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r = (Math.random()) * len;
|
||||
str2 = str2 + str1.charAt((int) r);
|
||||
}
|
||||
return str2;
|
||||
}
|
||||
|
||||
//得到随机颜色
|
||||
private Color getRandColor(int fc, int bc) {
|
||||
if (fc > 255) fc = 255;
|
||||
if (bc > 255) bc = 255;
|
||||
int r = fc + random.nextInt(bc - fc);
|
||||
int g = fc + random.nextInt(bc - fc);
|
||||
int b = fc + random.nextInt(bc - fc);
|
||||
return new Color(r, g, b);
|
||||
}
|
||||
|
||||
//产生随机字体
|
||||
private Font getFont(int size) {
|
||||
Random random = new Random();
|
||||
Font font[] = new Font[5];
|
||||
font[0] = new Font("Ravie", Font.PLAIN, size);
|
||||
font[1] = new Font("Antique Olive Compact", Font.PLAIN, size);
|
||||
font[2] = new Font("Fixedsys", Font.PLAIN, size);
|
||||
font[3] = new Font("Wide Latin", Font.PLAIN, size);
|
||||
font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size);
|
||||
return font[random.nextInt(5)];
|
||||
}
|
||||
|
||||
//扭曲方法
|
||||
private void shear(Graphics g, int wl, int hl, Color color) {
|
||||
shearX(g, wl, hl, color);
|
||||
shearY(g, wl, hl, color);
|
||||
}
|
||||
|
||||
private void shearX(Graphics g, int wl, int hl, Color color) {
|
||||
int period = random.nextInt(2);
|
||||
boolean borderGap = true;
|
||||
int frames = 1;
|
||||
int phase = random.nextInt(2);
|
||||
for (int i = 0; i < hl; i++) {
|
||||
double d = (double) (period >> 1)
|
||||
* Math.sin((double) i / (double) period
|
||||
+ (6.2831853071795862D * (double) phase)
|
||||
/ (double) frames);
|
||||
g.copyArea(0, i, wl, 1, (int) d, 0);
|
||||
if (borderGap) {
|
||||
g.setColor(color);
|
||||
g.drawLine((int) d, i, 0, i);
|
||||
g.drawLine((int) d + wl, i, wl, i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void shearY(Graphics g, int w1, int h1, Color color) {
|
||||
int period = random.nextInt(40) + 10; // 50;
|
||||
boolean borderGap = true;
|
||||
int frames = 20;
|
||||
int phase = 7;
|
||||
for (int i = 0; i < w1; i++) {
|
||||
double d = (double) (period >> 1)
|
||||
* Math.sin((double) i / (double) period
|
||||
+ (6.2831853071795862D * (double) phase)
|
||||
/ (double) frames);
|
||||
g.copyArea(i, 0, 1, h1, 0, (int) d);
|
||||
if (borderGap) {
|
||||
g.setColor(color);
|
||||
g.drawLine(i, (int) d, i, 0);
|
||||
g.drawLine(i, (int) d + h1, i, h1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(OutputStream os) {
|
||||
try {
|
||||
ImageIO.write(buffImg, "png", os);
|
||||
os.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code.toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
29
src/main/java/com/mir4updater/backend/utils/FileUtils.java
Normal file
29
src/main/java/com/mir4updater/backend/utils/FileUtils.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.mir4updater.backend.utils;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FileUtils {
|
||||
public static Logger logger = LogManager.getLogger(FileUtils.class);
|
||||
|
||||
public static boolean byteToFile(byte[] bytes, File file) {
|
||||
// 创建文件输出流
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
// 将字节数组写入到文件中
|
||||
fos.write(bytes);
|
||||
logger.info("写入文件成功!" + file.getAbsoluteFile());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.info(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
79
src/main/java/com/mir4updater/backend/utils/HashUtils.java
Normal file
79
src/main/java/com/mir4updater/backend/utils/HashUtils.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package com.mir4updater.backend.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class HashUtils {
|
||||
public static String getFileMD5(File file) throws NoSuchAlgorithmException, IOException {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
byte[] dataBytes = new byte[1024];
|
||||
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(dataBytes)) != -1) {
|
||||
md.update(dataBytes, 0, bytesRead);
|
||||
}
|
||||
|
||||
byte[] mdBytes = md.digest();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte mdByte : mdBytes) {
|
||||
sb.append(Integer.toString((mdByte & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String calculateSHA1(File file) throws NoSuchAlgorithmException, IOException {
|
||||
InputStream inputStream = new FileInputStream(file);
|
||||
return calculateSHA1(inputStream);
|
||||
}
|
||||
|
||||
public static String calculateSHA1(InputStream inputStream) throws NoSuchAlgorithmException, IOException {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
byte[] hashBytes = digest.digest();
|
||||
return bytesToHex(hashBytes, true);
|
||||
}
|
||||
|
||||
public static String calculateSHA256(File file) throws NoSuchAlgorithmException, IOException {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
byte[] hashBytes = digest.digest();
|
||||
return bytesToHex(hashBytes);
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
String hex = String.format("%02x", b & 0xFF);
|
||||
/*大写*/
|
||||
// String hex = String.format("%02X", b & 0xFF);
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes, boolean upcase) {
|
||||
String format = upcase ? "%02X" : "%02x";
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
String hex = String.format(format, b & 0xFF);
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mir4updater.backend.utils;
|
||||
|
||||
public class TextUtils {
|
||||
public static boolean isEmpty(CharSequence str) {
|
||||
return str == null || str.length() == 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user