From 8aa074e88fe49f3cf4e59a60c42abfd890688551 Mon Sep 17 00:00:00 2001 From: itao Date: Mon, 29 Jan 2024 00:51:20 +0800 Subject: [PATCH] feat: app version force update --- .../src/main/java/enums/AppPlatformEnum.java | 26 +++ .../main/java/enums/AppUpdateTypeEnum.java | 36 ---- .../user/controller/UpdateController.java | 64 ++----- .../java/com/luoo/user/dao/AppUpdateDao.java | 37 +++- .../luoo/user/dto/response/AppUpdateDTO.java | 8 +- .../java/com/luoo/user/pojo/AppUpdate.java | 27 ++- .../luoo/user/service/AppUpdateService.java | 169 +++--------------- luoo_user/src/main/resources/sql/domain.sql | 5 +- 8 files changed, 120 insertions(+), 252 deletions(-) create mode 100644 luoo_common/src/main/java/enums/AppPlatformEnum.java delete mode 100644 luoo_common/src/main/java/enums/AppUpdateTypeEnum.java diff --git a/luoo_common/src/main/java/enums/AppPlatformEnum.java b/luoo_common/src/main/java/enums/AppPlatformEnum.java new file mode 100644 index 0000000..41d34f7 --- /dev/null +++ b/luoo_common/src/main/java/enums/AppPlatformEnum.java @@ -0,0 +1,26 @@ +package enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; + +import static java.util.stream.Collectors.toMap; + +@Getter +@RequiredArgsConstructor +public enum AppPlatformEnum { + IOS("苹果"), + ANDROID("安卓"); + + private final String description; + + private static final Map STATIC_MAP = Arrays.stream(values()) + .collect(toMap(AppPlatformEnum::name, Function.identity())); + + public static AppPlatformEnum getByCode(String code) { + return STATIC_MAP.get(code.toUpperCase()); + } +} diff --git a/luoo_common/src/main/java/enums/AppUpdateTypeEnum.java b/luoo_common/src/main/java/enums/AppUpdateTypeEnum.java deleted file mode 100644 index 26c881b..0000000 --- a/luoo_common/src/main/java/enums/AppUpdateTypeEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package enums; - -public enum AppUpdateTypeEnum { - ALL(0, ".apk", "全更新"), WGT(1, ".wgt", "局部热更新"); - - private Integer type; - private String suffix; - private String description; - - AppUpdateTypeEnum(int type, String suffix, String description) { - this.type = type; - this.suffix = suffix; - this.description = description; - } - - public Integer getType() { - return type; - } - - public String getSuffix() { - return suffix; - } - - public String getDescription() { - return description; - } - - public static AppUpdateTypeEnum getByType(Integer type) { - for (AppUpdateTypeEnum at : AppUpdateTypeEnum.values()) { - if (at.getType().equals(type)) { - return at; - } - } - return null; - } -} diff --git a/luoo_user/src/main/java/com/luoo/user/controller/UpdateController.java b/luoo_user/src/main/java/com/luoo/user/controller/UpdateController.java index 04b85e5..b8328dc 100644 --- a/luoo_user/src/main/java/com/luoo/user/controller/UpdateController.java +++ b/luoo_user/src/main/java/com/luoo/user/controller/UpdateController.java @@ -1,35 +1,23 @@ package com.luoo.user.controller; +import annotation.GlobalInterceptor; +import annotation.VerifyParam; +import api.Result; +import com.luoo.user.dto.response.AppUpdateDTO; +import com.luoo.user.pojo.AppUpdate; +import com.luoo.user.service.AppUpdateService; +import io.swagger.annotations.Api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; -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 com.luoo.user.dto.response.AppUpdateDTO; -import com.luoo.user.pojo.AppUpdate; -import com.luoo.user.service.AppUpdateService; - -import annotation.GlobalInterceptor; -import annotation.VerifyParam; -import api.Result; -import enums.AppUpdateTypeEnum; -import enums.RequestFrequencyTypeEnum; -import io.swagger.annotations.Api; import util.StringTools; -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; import java.util.Arrays; @Api(tags = "UpdateController") @RestController @@ -42,49 +30,19 @@ public class UpdateController { @PostMapping("/checkVersion") @GlobalInterceptor - public Result checkVersion(@RequestParam("appVersion") String appVersion, - @VerifyParam(required = true) @RequestParam("deviceId") String deviceId) { + public Result checkVersion(@VerifyParam(required = true) @RequestParam("platform") String platform, + @VerifyParam(required = true) @RequestParam("deviceId") String deviceId, + @RequestParam("appVersion") String appVersion) { if (StringTools.isEmpty(appVersion)) { return Result.success(); } - AppUpdate appUpdate = appUpdateService.getLatestUpdate(appVersion, deviceId); + AppUpdate appUpdate = appUpdateService.getLatestUpdate(platform, appVersion, deviceId); if (appUpdate == null) { return Result.success(); } AppUpdateDTO appUpdateDTO = new AppUpdateDTO(); BeanUtils.copyProperties(appUpdate, appUpdateDTO); - AppUpdateTypeEnum typeEnum = AppUpdateTypeEnum.getByType(appUpdate.getUpdateType()); - appUpdateDTO.setSize(getFileSize(appUpdate.getId() , typeEnum.getSuffix())); appUpdateDTO.setUpdateList(Arrays.asList(appUpdate.getUpdateDescArray())); return Result.success(appUpdateDTO); } - - private Long getFileSize(String id, String suffix) { - return 0L; - } - - @GetMapping("/download/{id}") - @GlobalInterceptor(frequencyType = RequestFrequencyTypeEnum.DAY, requestFrequencyThreshold = 10) - public void download(HttpServletResponse response, @VerifyParam(required = true) @PathVariable Integer id) { - - /* - * OutputStream out = null; FileInputStream in = null; try { AppUpdate appUpdate - * = appUpdateService.getAppUpdateById(id); AppUpdateTypeEnum typeEnum = - * AppUpdateTypeEnum.getByType(appUpdate.getUpdateType()); String fileName = - * appConfig.getAppName() + "." + appUpdate.getVersion() + typeEnum.getSuffix(); - * File file = new File(appConfig.getProjectFolder() + - * Constants.APP_UPDATE_FOLDER + appUpdate.getId() + typeEnum.getSuffix()); if - * (!file.exists()) { return; } - * response.setContentType("application/x-msdownload; charset=UTF-8"); - * response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName - * + "\""); response.setContentLengthLong(file.length()); - * - * in = new FileInputStream(file); byte[] byteData = new byte[1024]; out = - * response.getOutputStream(); int len = 0; while ((len = in.read(byteData)) != - * -1) { out.write(byteData, 0, len); } out.flush(); } catch (Exception e) { - * logger.error("读取文件异常", e); } finally { if (out != null) { try { out.close(); - * } catch (IOException e) { logger.error("IO异常", e); } } if (in != null) { try - * { in.close(); } catch (IOException e) { logger.error("IO异常", e); } } } - */ - } } diff --git a/luoo_user/src/main/java/com/luoo/user/dao/AppUpdateDao.java b/luoo_user/src/main/java/com/luoo/user/dao/AppUpdateDao.java index d86778c..8c7e146 100644 --- a/luoo_user/src/main/java/com/luoo/user/dao/AppUpdateDao.java +++ b/luoo_user/src/main/java/com/luoo/user/dao/AppUpdateDao.java @@ -7,6 +7,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Predicate; @@ -15,24 +16,33 @@ import java.util.List; public interface AppUpdateDao extends JpaRepository, JpaSpecificationExecutor { - default AppUpdate selectLatestUpdate(String appVersion, String deviceId){ - Specification appUpdateSpecification = createAppUpdateSpecification(appVersion, deviceId); - Sort createTimeDescSort = Sort.by(Sort.Direction.DESC, "createTime"); - Page appUpdatePage = this.findAll(appUpdateSpecification, PageRequest.of(0, 1, createTimeDescSort)); + /** + * 查询当前版本之后的最新版本 + * @param platform 平台 + * @param currentVersion 当前版本 + * @param deviceId 设备ID(灰度发布) + * @return 最新版本 + */ + default AppUpdate selectLatestUpdate(String platform, String currentVersion, String deviceId){ + Specification appUpdateSpecification = createAppUpdateSpecification(platform, currentVersion, deviceId); + Sort createTimeDescSort = Sort.by(Sort.Direction.DESC, "version"); + Page appUpdatePage = this.findAll(appUpdateSpecification, + PageRequest.of(0, 1, createTimeDescSort)); if(appUpdatePage.getContent().isEmpty()){ return null; } return appUpdatePage.getContent().get(0); } - default Specification createAppUpdateSpecification(String appVersion, String deviceId) { + default Specification createAppUpdateSpecification(String platform, String currentVersion, String deviceId) { return (root, criteriaQuery, criteriaBuilder) -> { List predicateList = new ArrayList<>(); - predicateList.add(criteriaBuilder.greaterThan(root.get("version"), appVersion)); + predicateList.add(criteriaBuilder.equal(root.get("platform"), platform)); + predicateList.add(criteriaBuilder.greaterThan(root.get("version"), currentVersion)); Predicate allPublishPredicate = criteriaBuilder.equal(root.get("status"), 2); Predicate grayPublishPredicate = criteriaBuilder.equal(root.get("status"), 1); - Expression grayDeviceExpress = criteriaBuilder - .function("find_in_set", Integer.class, criteriaBuilder.literal(deviceId), root.get("grayscaleDevice")); + Expression grayDeviceExpress = criteriaBuilder.function("find_in_set", + Integer.class, criteriaBuilder.literal(deviceId), root.get("grayscaleDevice")); Predicate grayDevicePredicate = criteriaBuilder.gt(grayDeviceExpress, 0); Predicate grayScalePredicate = criteriaBuilder.and(grayPublishPredicate, grayDevicePredicate); predicateList.add(criteriaBuilder.or(allPublishPredicate, grayScalePredicate)); @@ -40,4 +50,15 @@ public interface AppUpdateDao extends JpaRepository, JpaSpeci return criteriaBuilder.and(predicateList.toArray(predicates)); }; } + + /** + * 查询两个版本之间是否有强制更新的版本 + * @param platform 平台 + * @param fromVersion 起始版本 + * @param toVersion 结束版本 + * @return 强制更新的版本(任何一个) + */ + @Query(value = "select * from tb_app_update where status != 0 and force_update = 1 and platform = :platform " + + " and version > :fromVersion and version < :toVersion limit 1", nativeQuery = true) + AppUpdate getAndCheckForceUpdate(String platform, String fromVersion, String toVersion); } diff --git a/luoo_user/src/main/java/com/luoo/user/dto/response/AppUpdateDTO.java b/luoo_user/src/main/java/com/luoo/user/dto/response/AppUpdateDTO.java index f90832d..b87a83f 100644 --- a/luoo_user/src/main/java/com/luoo/user/dto/response/AppUpdateDTO.java +++ b/luoo_user/src/main/java/com/luoo/user/dto/response/AppUpdateDTO.java @@ -24,6 +24,10 @@ public class AppUpdateDTO{ */ @ApiModelProperty(value = "更新描述") private List updateList; - @ApiModelProperty(value = "大小") - private Long size; + + /** + * 强制更新 + */ + @ApiModelProperty(value = "强制更新") + private Boolean forceUpdate; } diff --git a/luoo_user/src/main/java/com/luoo/user/pojo/AppUpdate.java b/luoo_user/src/main/java/com/luoo/user/pojo/AppUpdate.java index 946d028..785ac19 100644 --- a/luoo_user/src/main/java/com/luoo/user/pojo/AppUpdate.java +++ b/luoo_user/src/main/java/com/luoo/user/pojo/AppUpdate.java @@ -24,6 +24,11 @@ public class AppUpdate implements Serializable { @Id private String id; + /** + * 平台(ios|android) + */ + private String platform; + /** * 版本号 */ @@ -35,9 +40,9 @@ public class AppUpdate implements Serializable { private String updateDesc; /** - * 更新类型0:全更新 1:局部热更新 + * 强制更新 */ - private Integer updateType; + private Boolean forceUpdate; /** * 创建时间 @@ -94,12 +99,20 @@ public class AppUpdate implements Serializable { return this.updateDesc; } - public void setUpdateType(Integer updateType) { - this.updateType = updateType; + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public Boolean getForceUpdate() { + return forceUpdate; } - public Integer getUpdateType() { - return this.updateType; + public void setForceUpdate(Boolean forceUpdate) { + this.forceUpdate = forceUpdate; } public void setCreateTime(Date createTime) { @@ -128,6 +141,6 @@ public class AppUpdate implements Serializable { @Override public String toString() { - return "自增ID:" + (id == null ? "空" : id) + ",版本号:" + (version == null ? "空" : version) + ",更新描述:" + (updateDesc == null ? "空" : updateDesc) + ",更新类型0:全更新 1:局部热更新:" + (updateType == null ? "空" : updateType) + ",创建时间:" + (createTime == null ? "空" : DateUtil.format(createTime, DateTimePatternEnum.YYYY_MM_DD_HH_MM_SS.getPattern())) + ",0:未发布 1:灰度发布 2:全网发布:" + (status == null ? "空" : status) + ",灰度设备ID:" + (grayscaleDevice == null ? "空" : grayscaleDevice); + return "自增ID:" + (id == null ? "空" : id) + ",版本号:" + (version == null ? "空" : version) + ",更新描述:" + (updateDesc == null ? "空" : updateDesc) + ",强制更新:" + (forceUpdate == null ? "空" : forceUpdate) + ",创建时间:" + (createTime == null ? "空" : DateUtil.format(createTime, DateTimePatternEnum.YYYY_MM_DD_HH_MM_SS.getPattern())) + ",0:未发布 1:灰度发布 2:全网发布:" + (status == null ? "空" : status) + ",灰度设备ID:" + (grayscaleDevice == null ? "空" : grayscaleDevice); } } diff --git a/luoo_user/src/main/java/com/luoo/user/service/AppUpdateService.java b/luoo_user/src/main/java/com/luoo/user/service/AppUpdateService.java index 26f27a6..de6b0c8 100644 --- a/luoo_user/src/main/java/com/luoo/user/service/AppUpdateService.java +++ b/luoo_user/src/main/java/com/luoo/user/service/AppUpdateService.java @@ -1,156 +1,37 @@ package com.luoo.user.service; -import java.io.File; -import java.io.IOException; -import java.util.Date; -import java.util.List; - -import javax.annotation.Resource; - -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 com.luoo.user.dao.AppUpdateDao; import com.luoo.user.pojo.AppUpdate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import java.util.Objects; + +@Slf4j @Service +@RequiredArgsConstructor public class AppUpdateService { - - @Autowired - private AppUpdateDao appUpdateDao; + private final AppUpdateDao appUpdateDao; /** - * 根据条件查询列表 + * 获取最新版本 + * @param platform 平台 + * @param currentVersion 当前版本 + * @param deviceId 设备ID + * @return 最新版本 */ - /* - * @Override public List findListByParam(AppUpdateQuery param) { - * return this.appUpdateDao.selectList(param); } - * - *//** - * 根据条件查询列表 - */ - /* - * @Override public Integer findCountByParam(AppUpdateQuery param) { return - * this.appUpdateDao.selectCount(param); } - * - *//** - * 分页查询方法 - */ - /* - * @Override public PaginationResultVO findListByPage(AppUpdateQuery - * param) { int count = this.findCountByParam(param); int pageSize = - * param.getPageSize() == null ? PageSize.SIZE15.getSize() : - * param.getPageSize(); - * - * SimplePage page = new SimplePage(param.getPageNo(), count, pageSize); - * param.setSimplePage(page); List list = - * this.findListByParam(param); PaginationResultVO result = new - * PaginationResultVO(count, page.getPageSize(), page.getPageNo(), - * page.getPageTotal(), list); return result; } - * - *//** - * 新增 - */ - /* - * @Override public Integer add(AppUpdate bean) { return - * this.appUpdateDao.insert(bean); } - * - *//** - * 批量新增 - */ - /* - * @Override public Integer addBatch(List listBean) { if (listBean == - * null || listBean.isEmpty()) { return 0; } return - * this.appUpdateDao.insertBatch(listBean); } - * - *//** - * 批量新增或者修改 - */ - /* - * @Override public Integer addOrUpdateBatch(List listBean) { if - * (listBean == null || listBean.isEmpty()) { return 0; } return - * this.appUpdateDao.insertOrUpdateBatch(listBean); } - * - *//** - * 多条件更新 - */ - /* - * @Override public Integer updateByParam(AppUpdate bean, AppUpdateQuery param) - * { StringTools.checkParam(param); return this.appUpdateDao.updateByParam(bean, - * param); } - * - *//** - * 多条件删除 - */ - /* - * @Override public Integer deleteByParam(AppUpdateQuery param) { - * StringTools.checkParam(param); return this.appUpdateDao.deleteByParam(param); - * } - * - *//** - * 根据Id获取对象 - */ - /* - * @Override public AppUpdate getAppUpdateById(Integer id) { return - * this.appUpdateDao.selectById(id); } - * - *//** - * 根据Id修改 - */ - - /* - * @Override public Integer updateAppUpdateById(AppUpdate bean, Integer id) { - * return this.appUpdateDao.updateById(bean, id); } - * - *//** - * 根据Id删除 - *//* - * @Override public Integer deleteAppUpdateById(Integer id) { return - * this.appUpdateDao.deleteById(id); } - * - * @Override - * - * @Transactional(rollbackFor = Exception.class) public void - * saveUpdate(AppUpdate appUpdate, MultipartFile file) { AppUpdateQuery - * updateQuery = new AppUpdateQuery(); updateQuery.setOrderBy("id desc"); - * updateQuery.setSimplePage(new SimplePage(0, 1)); List - * appUpdateList = appUpdateDao.selectList(updateQuery); if - * (!appUpdateList.isEmpty()) { AppUpdate latest = appUpdateList.get(0); Long - * dbVerion = Long.parseLong(latest.getVersion().replace(".", "")); Long - * currentVersion = Long.parseLong(appUpdate.getVersion().replace(".", "")); if - * (appUpdate.getId() == null && currentVersion <= dbVerion) { throw new - * BusinessException("当前版本必须大于历史版本"); } if (appUpdate.getId() != null && - * currentVersion <= dbVerion && - * !appUpdate.getVersion().equals(latest.getVersion())) { throw new - * BusinessException("当前版本必须大于历史版本"); } } if (appUpdate.getId() == null) { - * appUpdate.setCreateTime(new Date()); - * appUpdate.setStatus(AppUpdateSatusEnum.INIT.getStatus()); - * appUpdateDao.insert(appUpdate); } else { appUpdate.setStatus(null); - * appUpdate.setGrayscaleDevice(null); appUpdateDao.updateById(appUpdate, - * appUpdate.getId()); } if (file != null) { File folder = new - * File(appConfig.getProjectFolder() + Constants.APP_UPDATE_FOLDER); if - * (!folder.exists()) { folder.mkdirs(); } AppUpdateTypeEnum typeEnum = - * AppUpdateTypeEnum.getByType(appUpdate.getUpdateType()); try { - * file.transferTo(new File(folder.getAbsolutePath() + "/" + appUpdate.getId() + - * typeEnum.getSuffix())); } catch (IOException e) { throw new - * BusinessException("更新App失败"); } } } - * - * @Override public void postUpdate(Integer id, Integer status, String - * grayscaleDevice) { AppUpdateSatusEnum satusEnum = - * AppUpdateSatusEnum.getByStatus(status); if (status == null) { throw new - * BusinessException(ResponseCodeEnum.CODE_600); } if - * (AppUpdateSatusEnum.GRAYSCALE == satusEnum && - * StringTools.isEmpty(grayscaleDevice)) { throw new - * BusinessException(ResponseCodeEnum.CODE_600); } if - * (AppUpdateSatusEnum.GRAYSCALE != satusEnum) { grayscaleDevice = ""; } - * AppUpdate update = new AppUpdate(); update.setStatus(status); - * update.setGrayscaleDevice(grayscaleDevice); appUpdateDao.updateById(update, - * id); } - */ - - public AppUpdate getLatestUpdate(String appVersion, String deviceId) { - return appUpdateDao.selectLatestUpdate(appVersion, deviceId); + public AppUpdate getLatestUpdate(String platform, String currentVersion, String deviceId) { + AppUpdate latestAppUpdate = appUpdateDao.selectLatestUpdate(platform, currentVersion, deviceId); + // 是否强制更新 + if(Objects.nonNull(latestAppUpdate) && !latestAppUpdate.getForceUpdate()) { + String latestVersion = latestAppUpdate.getVersion(); + log.debug("platform={}, deviceId={}, currentVersion={}, latestVersion={}", platform, deviceId, currentVersion, latestVersion); + AppUpdate forceUpdateAppUpdate = appUpdateDao.getAndCheckForceUpdate(platform, currentVersion, latestVersion); + if(Objects.nonNull(forceUpdateAppUpdate)){ + latestAppUpdate.setForceUpdate(true); + } + } + return latestAppUpdate; } } diff --git a/luoo_user/src/main/resources/sql/domain.sql b/luoo_user/src/main/resources/sql/domain.sql index 4943f3e..4f35ed5 100644 --- a/luoo_user/src/main/resources/sql/domain.sql +++ b/luoo_user/src/main/resources/sql/domain.sql @@ -2,9 +2,10 @@ drop table if exists indie_user.tb_app_update; create table if not exists indie_user.tb_app_update ( id varchar(100) not null comment 'ID' primary key, - version varchar(10) default '' not null comment '版本号', + platform varchar(10) not null comment '平台(ios|android)', + version varchar(10) default '' not null comment '版本号(版本号的格式和长度保持一致)', update_desc varchar(500) default '' not null comment '更新描述', - update_type tinyint(1) default 0 not null comment '更新类型(0=全更新, 1=局部热更新)', + update_force tinyint(1) default 0 not null comment '强制更新表示(0=否, 1=是)', status tinyint(1) default 1 not null comment '状态(0=未发布, 1=灰度发布, 2=全网发布)', grayscale_device varchar(4000) default '' not null comment '灰度设备ID集合(英文逗号相隔)', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间'