add:pda新版本发布在线更新
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package org.nl.wms.system_manage.controller.appversion;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import org.nl.common.base.ResponseData;
|
||||
import org.nl.common.domain.query.PageQuery;
|
||||
import org.nl.wms.system_manage.service.appversion.IAppVersionService;
|
||||
import org.nl.wms.system_manage.service.appversion.dao.AppVersion;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/appVersion")
|
||||
public class AppVersionController {
|
||||
|
||||
@Autowired
|
||||
private IAppVersionService appVersionService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Object> query(@RequestParam(required = false, name = "app_name") String appName, PageQuery page) {
|
||||
return ResponseData.build(appVersionService.pageQuery(appName, page), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/publish")
|
||||
public ResponseEntity<Object> publish(@RequestParam("app_name") String appName,
|
||||
@RequestParam("version_name") String versionName,
|
||||
@RequestParam("file") MultipartFile file) {
|
||||
return ResponseData.build(appVersionService.publish(appName, versionName, file), HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@GetMapping("/check")
|
||||
public ResponseEntity<Object> check(@RequestParam("app_name") String appName,
|
||||
@RequestParam(required = false, name = "version_name") String versionName) {
|
||||
return ResponseData.build(appVersionService.checkLatest(appName, versionName), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@GetMapping("/download/{appVersionId}")
|
||||
public void download(@PathVariable String appVersionId, HttpServletResponse response) throws IOException {
|
||||
AppVersion version = appVersionService.getDownloadVersion(appVersionId);
|
||||
File apk = new File(version.getApk_path());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setContentLengthLong(apk.length());
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" +
|
||||
URLEncoder.encode(version.getApk_name(), StandardCharsets.UTF_8.name()).replace("+", "%20"));
|
||||
try (FileInputStream input = new FileInputStream(apk)) {
|
||||
StreamUtils.copy(input, response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.nl.wms.system_manage.service.appversion;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.nl.common.domain.query.PageQuery;
|
||||
import org.nl.wms.system_manage.service.appversion.dao.AppVersion;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface IAppVersionService extends IService<AppVersion> {
|
||||
|
||||
IPage<AppVersion> pageQuery(String appName, PageQuery page);
|
||||
|
||||
AppVersion publish(String appName, String versionName, MultipartFile apk);
|
||||
|
||||
Map<String, Object> checkLatest(String appName, String versionName);
|
||||
|
||||
AppVersion getDownloadVersion(String appVersionId);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.nl.wms.system_manage.service.appversion.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@TableName("sys_app_version")
|
||||
public class AppVersion implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId("app_version_id")
|
||||
private String app_version_id;
|
||||
|
||||
private String app_name;
|
||||
|
||||
private String version_name;
|
||||
|
||||
private String apk_name;
|
||||
|
||||
@JsonIgnore
|
||||
private String apk_path;
|
||||
|
||||
private Long apk_size;
|
||||
|
||||
private String create_id;
|
||||
|
||||
private String create_name;
|
||||
|
||||
private String create_time;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.nl.wms.system_manage.service.appversion.dao.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.nl.wms.system_manage.service.appversion.dao.AppVersion;
|
||||
|
||||
@Mapper
|
||||
public interface AppVersionMapper extends BaseMapper<AppVersion> {
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package org.nl.wms.system_manage.service.appversion.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.nl.common.domain.query.PageQuery;
|
||||
import org.nl.common.exception.BadRequestException;
|
||||
import org.nl.common.utils.SecurityUtils;
|
||||
import org.nl.config.FileProperties;
|
||||
import org.nl.wms.system_manage.service.appversion.IAppVersionService;
|
||||
import org.nl.wms.system_manage.service.appversion.dao.AppVersion;
|
||||
import org.nl.wms.system_manage.service.appversion.dao.mapper.AppVersionMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AppVersionServiceImpl extends ServiceImpl<AppVersionMapper, AppVersion> implements IAppVersionService {
|
||||
|
||||
@Autowired
|
||||
private FileProperties fileProperties;
|
||||
|
||||
@Override
|
||||
public IPage<AppVersion> pageQuery(String appName, PageQuery page) {
|
||||
return page(page.build(AppVersion.class), new LambdaQueryWrapper<AppVersion>()
|
||||
.like(StrUtil.isNotBlank(appName), AppVersion::getApp_name, appName)
|
||||
.orderByDesc(AppVersion::getCreate_time));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AppVersion publish(String appName, String versionName, MultipartFile apk) {
|
||||
if (StrUtil.isBlank(appName) || StrUtil.isBlank(versionName)) {
|
||||
throw new BadRequestException("应用名称和版本号不能为空");
|
||||
}
|
||||
if (apk == null || apk.isEmpty() || !"apk".equalsIgnoreCase(getExtension(apk.getOriginalFilename()))) {
|
||||
throw new BadRequestException("只能上传 APK 文件");
|
||||
}
|
||||
if (apk.getSize() > fileProperties.getMaxSize() * 1024 * 1024) {
|
||||
throw new BadRequestException("APK 文件超过允许大小");
|
||||
}
|
||||
if (count(new LambdaQueryWrapper<AppVersion>()
|
||||
.eq(AppVersion::getApp_name, appName)
|
||||
.eq(AppVersion::getVersion_name, versionName)) > 0) {
|
||||
throw new BadRequestException("该应用版本已发布");
|
||||
}
|
||||
|
||||
String apkName = safeFilePart(appName) + "-" + safeFilePart(versionName) + ".apk";
|
||||
File directory = new File(fileProperties.getPath().getPath(), "apk");
|
||||
File destination = new File(directory, apkName);
|
||||
if (destination.exists()) {
|
||||
throw new BadRequestException("同名 APK 文件已存在");
|
||||
}
|
||||
if (!directory.exists() && !directory.mkdirs()) {
|
||||
throw new BadRequestException("APK 存储目录创建失败");
|
||||
}
|
||||
|
||||
try {
|
||||
apk.transferTo(destination);
|
||||
AppVersion appVersion = new AppVersion();
|
||||
appVersion.setApp_version_id(IdUtil.getSnowflake(1, 1).nextIdStr());
|
||||
appVersion.setApp_name(appName.trim());
|
||||
appVersion.setVersion_name(versionName.trim());
|
||||
appVersion.setApk_name(apkName);
|
||||
appVersion.setApk_path(destination.getCanonicalPath());
|
||||
appVersion.setApk_size(apk.getSize());
|
||||
appVersion.setCreate_id(SecurityUtils.getCurrentUserId());
|
||||
appVersion.setCreate_name(SecurityUtils.getCurrentNickName());
|
||||
appVersion.setCreate_time(DateUtil.now());
|
||||
save(appVersion);
|
||||
return appVersion;
|
||||
} catch (Exception e) {
|
||||
if (destination.exists()) {
|
||||
destination.delete();
|
||||
}
|
||||
if (e instanceof BadRequestException) {
|
||||
throw (BadRequestException) e;
|
||||
}
|
||||
throw new BadRequestException("APK 保存失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> checkLatest(String appName, String versionName) {
|
||||
if (StrUtil.isBlank(appName)) {
|
||||
throw new BadRequestException("应用名称不能为空");
|
||||
}
|
||||
List<AppVersion> versions = list(new LambdaQueryWrapper<AppVersion>()
|
||||
.eq(AppVersion::getApp_name, appName.trim()));
|
||||
AppVersion latest = null;
|
||||
for (AppVersion version : versions) {
|
||||
if (latest == null || compareVersion(version.getVersion_name(), latest.getVersion_name()) > 0) {
|
||||
latest = version;
|
||||
}
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("has_update", latest != null && compareVersion(latest.getVersion_name(), versionName) > 0);
|
||||
if (latest != null) {
|
||||
Map<String, Object> latestInfo = new HashMap<>();
|
||||
latestInfo.put("app_version_id", latest.getApp_version_id());
|
||||
latestInfo.put("app_name", latest.getApp_name());
|
||||
latestInfo.put("version_name", latest.getVersion_name());
|
||||
latestInfo.put("apk_name", latest.getApk_name());
|
||||
latestInfo.put("apk_size", latest.getApk_size());
|
||||
latestInfo.put("create_time", latest.getCreate_time());
|
||||
result.put("latest", latestInfo);
|
||||
} else {
|
||||
result.put("latest", null);
|
||||
}
|
||||
result.put("download_url", latest == null ? null : "/api/appVersion/download/" + latest.getApp_version_id());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppVersion getDownloadVersion(String appVersionId) {
|
||||
AppVersion version = getById(appVersionId);
|
||||
if (version == null || !new File(version.getApk_path()).isFile()) {
|
||||
throw new BadRequestException("APK 文件不存在");
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private String getExtension(String name) {
|
||||
if (name == null || name.lastIndexOf('.') < 0) {
|
||||
return "";
|
||||
}
|
||||
return name.substring(name.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
private String safeFilePart(String value) {
|
||||
String safeValue = value.trim().replaceAll("[\\\\/:*?\"<>|\\s]+", "_");
|
||||
if (safeValue.isEmpty() || ".".equals(safeValue) || "..".equals(safeValue)) {
|
||||
throw new BadRequestException("应用名称或版本号不能用于文件名");
|
||||
}
|
||||
return safeValue;
|
||||
}
|
||||
|
||||
private int compareVersion(String first, String second) {
|
||||
String[] firstParts = normalizeVersion(first).split("\\.");
|
||||
String[] secondParts = normalizeVersion(second).split("\\.");
|
||||
int length = Math.max(firstParts.length, secondParts.length);
|
||||
for (int index = 0; index < length; index++) {
|
||||
int firstPart = index < firstParts.length ? parseVersionPart(firstParts[index]) : 0;
|
||||
int secondPart = index < secondParts.length ? parseVersionPart(secondParts[index]) : 0;
|
||||
if (firstPart != secondPart) {
|
||||
return firstPart > secondPart ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String normalizeVersion(String version) {
|
||||
return version == null ? "0" : version.trim().replaceFirst("^[vV]", "");
|
||||
}
|
||||
|
||||
private int parseVersionPart(String value) {
|
||||
try {
|
||||
return Integer.parseInt(value.replaceAll("\\D.*", ""));
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user