Merge branch '240124-app-update'

main
itao 12 months ago
commit d0e0a1351a

@ -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<String, AppPlatformEnum> STATIC_MAP = Arrays.stream(values())
.collect(toMap(AppPlatformEnum::name, Function.identity()));
public static AppPlatformEnum getByCode(String code) {
return STATIC_MAP.get(code.toUpperCase());
}
}

@ -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;
}
}

@ -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); } } }
*/
}
}

@ -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<AppUpdate, String>, JpaSpecificationExecutor<AppUpdate> {
default AppUpdate selectLatestUpdate(String appVersion, String deviceId){
Specification<AppUpdate> appUpdateSpecification = createAppUpdateSpecification(appVersion, deviceId);
Sort createTimeDescSort = Sort.by(Sort.Direction.DESC, "createTime");
Page<AppUpdate> 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<AppUpdate> appUpdateSpecification = createAppUpdateSpecification(platform, currentVersion, deviceId);
Sort createTimeDescSort = Sort.by(Sort.Direction.DESC, "version");
Page<AppUpdate> appUpdatePage = this.findAll(appUpdateSpecification,
PageRequest.of(0, 1, createTimeDescSort));
if(appUpdatePage.getContent().isEmpty()){
return null;
}
return appUpdatePage.getContent().get(0);
}
default Specification<AppUpdate> createAppUpdateSpecification(String appVersion, String deviceId) {
default Specification<AppUpdate> createAppUpdateSpecification(String platform, String currentVersion, String deviceId) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> 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<Integer> grayDeviceExpress = criteriaBuilder
.function("find_in_set", Integer.class, criteriaBuilder.literal(deviceId), root.get("grayscaleDevice"));
Expression<Integer> 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<AppUpdate, String>, 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);
}

@ -24,6 +24,10 @@ public class AppUpdateDTO{
*/
@ApiModelProperty(value = "更新描述")
private List<String> updateList;
@ApiModelProperty(value = "大小")
private Long size;
/**
*
*/
@ApiModelProperty(value = "强制更新")
private Boolean forceUpdate;
}

@ -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);
}
}

@ -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<AppUpdate> findListByParam(AppUpdateQuery param) {
* return this.appUpdateDao.selectList(param); }
*
*//**
*
*/
/*
* @Override public Integer findCountByParam(AppUpdateQuery param) { return
* this.appUpdateDao.selectCount(param); }
*
*//**
*
*/
/*
* @Override public PaginationResultVO<AppUpdate> 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<AppUpdate> list =
* this.findListByParam(param); PaginationResultVO<AppUpdate> 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<AppUpdate> listBean) { if (listBean ==
* null || listBean.isEmpty()) { return 0; } return
* this.appUpdateDao.insertBatch(listBean); }
*
*//**
*
*/
/*
* @Override public Integer addOrUpdateBatch(List<AppUpdate> 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<AppUpdate>
* 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;
}
}

@ -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 '创建时间'

Loading…
Cancel
Save