Skip to content

Commit 658ae5d

Browse files
author
heavyrian2012
committed
添加版本检查升级功能
1 parent bf92188 commit 658ae5d

9 files changed

Lines changed: 636 additions & 1 deletion

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
2. PC端扫码登录的功能.
2727
3. 群公告的获取和更新功能.
2828
4. 客户端上传日志功能.
29+
5. 客户端版本更新检查功能,支持Android/iOS/鸿蒙三端.
2930
> 本工程为Demo工程,实际使用时需要把对应功能移植到您的应用服务中。如果需要直接使用,请按照后面的说明解决掉性能瓶颈问题。
3031
3132
#### 编译
@@ -42,9 +43,14 @@ mvn clean package
4243
应用使用的是腾讯云短信功能,需要申请到```appid/appkey/templateId```这三个参数,并配置到```tencent_sms.properties```中去。用户也可以自行更换为自己喜欢的短信提供商。在没有短信供应商的情况下,为了测试可以使用```superCode```,设置好后,客户端可以直接使用```superCode```进行登陆。上线时一定要注意删掉```superCode```
4344

4445
#### 修改配置
45-
本演示服务有4个配置文件在工程的```config```目录下,分别是```application.properties```, ```im.properties```, ```aliyun_sms.properties``````tencent_sms.properties```。请正确配置放到jar包所在的目录下的```config```目录下。
46+
本演示服务有5个配置文件在工程的```config```目录下,分别是```application.properties```, ```im.properties```, ```aliyun_sms.properties```, ```tencent_sms.properties``````version.properties```。请正确配置放到jar包所在的目录下的```config```目录下。
4647
> ```application.properties```配置中的```sms.verdor```决定是使用那个短信服务商,1为腾讯短信,2为阿里云短信
4748
49+
#### 版本更新配置
50+
`config/version.properties` 中配置 Android/iOS/鸿蒙三端的版本更新信息,服务每30秒自动检测文件修改并重新加载。客户端启动后自动调用 `/version/check` 接口检测版本,支持普通更新和强制更新两种策略。
51+
52+
详细配置说明、字段释义和常见问题请参考 **[version_update.md](./version_update.md)**
53+
4854
#### 运行
4955
```target```目录找到```app-XXXX.jar```,把jar包和放置配置文件的```config```目录放到一起,然后执行下面命令:
5056
```

config/version.properties

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# 客户端版本更新配置
2+
# 服务每30秒检查此文件是否有修改,如有修改则自动重新加载
3+
4+
# Android 版本配置
5+
version.android.latestVersion=1.3.0
6+
version.android.buildNumber=130
7+
version.android.minVersion=1.0.0
8+
version.android.forceUpdate=false
9+
version.android.title=发现新版本
10+
version.android.message=1. 优化性能\n2. 修复已知问题
11+
## Example: https://download.example.com/xxchat-latest.apk
12+
version.android.downloadUrl=
13+
14+
# iOS 版本配置
15+
version.ios.latestVersion=1.3.0
16+
version.ios.buildNumber=130
17+
version.ios.minVersion=1.0.0
18+
version.ios.forceUpdate=false
19+
version.ios.title=发现新版本
20+
version.ios.message=1. 优化性能\n2. 修复已知问题
21+
## Example: itms-apps://itunes.apple.com/us/app/XXCHAT/id1234567?ls=1&mt=8
22+
version.ios.redirectUrl=
23+
24+
# 鸿蒙 HarmonyOS 版本配置
25+
version.harmony.latestVersion=1.3.0
26+
version.harmony.buildNumber=130
27+
version.harmony.minVersion=1.0.0
28+
version.harmony.forceUpdate=false
29+
version.harmony.title=发现新版本
30+
version.harmony.message=1. 优化性能\n2. 修复已知问题
31+
## Example: store://appgallery.huawei.com/app/detail?id=com.example.XXChat
32+
version.harmony.harmonyUrl=

src/main/java/cn/wildfirechat/app/AppController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,12 @@ public Object getFavoriteItems(@RequestBody LoadFavoriteRequest request) {
259259
public Object getGroupMembersForPortrait(@RequestBody GroupIdPojo groupIdPojo) {
260260
return mService.getGroupMembersForPortrait(groupIdPojo.groupId);
261261
}
262+
263+
@CrossOrigin
264+
@GetMapping(value = "/version/check", produces = "application/json;charset=UTF-8")
265+
public Object checkVersion(@RequestParam("platform") int platform,
266+
@RequestParam("currentVersion") String currentVersion,
267+
@RequestParam(value = "buildNumber", required = false, defaultValue = "0") int buildNumber) {
268+
return mService.checkVersion(platform, currentVersion, buildNumber);
269+
}
262270
}

src/main/java/cn/wildfirechat/app/Service.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ public interface Service {
4949
RestResult removeFavoriteItems(long id);
5050
RestResult getFavoriteItems(long id, int count);
5151
RestResult getGroupMembersForPortrait(String groupId);
52+
53+
RestResult checkVersion(int platform, String currentVersion, int buildNumber);
5254
}

src/main/java/cn/wildfirechat/app/ServiceImpl.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,4 +1789,77 @@ public RestResult getGroupMembersForPortrait(String groupId) {
17891789
return RestResult.error(ERROR_SERVER_ERROR);
17901790
}
17911791
}
1792+
1793+
@Autowired
1794+
private VersionConfigService versionConfigService;
1795+
1796+
@Override
1797+
public RestResult checkVersion(int platform, String currentVersion, int buildNumber) {
1798+
String platformKey;
1799+
if (platform == 1 || platform == 8) {
1800+
platformKey = "ios";
1801+
} else if (platform == 2 || platform == 9) {
1802+
platformKey = "android";
1803+
} else if (platform == 10) {
1804+
platformKey = "harmony";
1805+
} else {
1806+
return RestResult.error(ERROR_INVALID_PARAMETER);
1807+
}
1808+
1809+
VersionCheckResponse config = versionConfigService.getVersionInfo(platformKey);
1810+
if (config == null) {
1811+
return RestResult.error(ERROR_SERVER_CONFIG_ERROR);
1812+
}
1813+
1814+
VersionCheckResponse result = new VersionCheckResponse();
1815+
result.setLatestVersion(config.getLatestVersion());
1816+
result.setBuildNumber(config.getBuildNumber());
1817+
result.setMinVersion(config.getMinVersion());
1818+
result.setTitle(config.getTitle());
1819+
result.setMessage(config.getMessage());
1820+
result.setDownloadUrl(config.getDownloadUrl());
1821+
result.setRedirectUrl(config.getRedirectUrl());
1822+
result.setHarmonyUrl(config.getHarmonyUrl());
1823+
1824+
boolean needUpdate;
1825+
if (buildNumber > 0 && config.getBuildNumber() > 0) {
1826+
needUpdate = buildNumber < config.getBuildNumber();
1827+
} else {
1828+
needUpdate = compareVersion(currentVersion, config.getLatestVersion()) < 0;
1829+
}
1830+
1831+
boolean forceUpdate = config.isForceUpdate();
1832+
if (!forceUpdate && !currentVersion.isEmpty() && !config.getMinVersion().isEmpty()) {
1833+
forceUpdate = compareVersion(currentVersion, config.getMinVersion()) < 0;
1834+
}
1835+
1836+
result.setNeedUpdate(needUpdate);
1837+
result.setForceUpdate(forceUpdate);
1838+
1839+
return RestResult.ok(result);
1840+
}
1841+
1842+
private int compareVersion(String v1, String v2) {
1843+
if (v1 == null || v1.isEmpty()) return -1;
1844+
if (v2 == null || v2.isEmpty()) return 1;
1845+
String[] parts1 = v1.split("\\.");
1846+
String[] parts2 = v2.split("\\.");
1847+
int len = Math.max(parts1.length, parts2.length);
1848+
for (int i = 0; i < len; i++) {
1849+
int n1 = i < parts1.length ? parseInt(parts1[i]) : 0;
1850+
int n2 = i < parts2.length ? parseInt(parts2[i]) : 0;
1851+
if (n1 != n2) {
1852+
return n1 < n2 ? -1 : 1;
1853+
}
1854+
}
1855+
return 0;
1856+
}
1857+
1858+
private int parseInt(String s) {
1859+
try {
1860+
return Integer.parseInt(s);
1861+
} catch (NumberFormatException e) {
1862+
return 0;
1863+
}
1864+
}
17921865
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package cn.wildfirechat.app;
2+
3+
import cn.wildfirechat.app.pojo.VersionCheckResponse;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.scheduling.annotation.Scheduled;
8+
import org.springframework.stereotype.Service;
9+
10+
import javax.annotation.PostConstruct;
11+
import java.io.File;
12+
import java.io.FileInputStream;
13+
import java.io.IOException;
14+
import java.io.InputStreamReader;
15+
import java.nio.charset.StandardCharsets;
16+
import java.util.Properties;
17+
import java.util.concurrent.ConcurrentHashMap;
18+
19+
@Service
20+
public class VersionConfigService {
21+
private static final Logger LOG = LoggerFactory.getLogger(VersionConfigService.class);
22+
23+
@Value("${version.config.path:config/version.properties}")
24+
private String configPath;
25+
26+
private final ConcurrentHashMap<String, VersionCheckResponse> versionMap = new ConcurrentHashMap<>();
27+
private long lastModified = 0;
28+
29+
@PostConstruct
30+
public void init() {
31+
loadConfig();
32+
}
33+
34+
@Scheduled(fixedRate = 30 * 1000)
35+
public void reloadIfModified() {
36+
File file = new File(configPath);
37+
if (!file.exists()) {
38+
LOG.warn("Version config file not found: {}", configPath);
39+
return;
40+
}
41+
if (file.lastModified() > lastModified) {
42+
LOG.info("Version config file modified, reloading...");
43+
loadConfig();
44+
}
45+
}
46+
47+
private synchronized void loadConfig() {
48+
File file = new File(configPath);
49+
if (!file.exists()) {
50+
LOG.warn("Version config file not found: {}", configPath);
51+
return;
52+
}
53+
54+
Properties props = new Properties();
55+
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
56+
props.load(reader);
57+
lastModified = file.lastModified();
58+
} catch (IOException e) {
59+
LOG.error("Failed to load version config", e);
60+
return;
61+
}
62+
63+
// Android
64+
versionMap.put("android", buildVersionInfo(props, "android"));
65+
// iOS
66+
versionMap.put("ios", buildVersionInfo(props, "ios"));
67+
// Harmony
68+
versionMap.put("harmony", buildVersionInfo(props, "harmony"));
69+
70+
LOG.info("Version config loaded successfully");
71+
}
72+
73+
private VersionCheckResponse buildVersionInfo(Properties props, String platform) {
74+
VersionCheckResponse info = new VersionCheckResponse();
75+
info.setLatestVersion(getProp(props, "version." + platform + ".latestVersion", ""));
76+
info.setBuildNumber(parseInt(getProp(props, "version." + platform + ".buildNumber", "0")));
77+
info.setMinVersion(getProp(props, "version." + platform + ".minVersion", ""));
78+
info.setForceUpdate(Boolean.parseBoolean(getProp(props, "version." + platform + ".forceUpdate", "false")));
79+
info.setTitle(getProp(props, "version." + platform + ".title", ""));
80+
info.setMessage(getProp(props, "version." + platform + ".message", ""));
81+
info.setDownloadUrl(getProp(props, "version." + platform + ".downloadUrl", ""));
82+
info.setRedirectUrl(getProp(props, "version." + platform + ".redirectUrl", ""));
83+
info.setHarmonyUrl(getProp(props, "version." + platform + ".harmonyUrl", ""));
84+
return info;
85+
}
86+
87+
private String getProp(Properties props, String key, String defaultValue) {
88+
String value = props.getProperty(key);
89+
return value != null ? value : defaultValue;
90+
}
91+
92+
private int parseInt(String value) {
93+
try {
94+
return Integer.parseInt(value);
95+
} catch (NumberFormatException e) {
96+
return 0;
97+
}
98+
}
99+
100+
public VersionCheckResponse getVersionInfo(String platform) {
101+
return versionMap.get(platform);
102+
}
103+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package cn.wildfirechat.app.pojo;
2+
3+
/**
4+
* 版本检查响应
5+
*/
6+
public class VersionCheckResponse {
7+
private boolean needUpdate;
8+
private boolean forceUpdate;
9+
private String latestVersion;
10+
private int buildNumber;
11+
private String title;
12+
private String message;
13+
private String downloadUrl;
14+
private String redirectUrl;
15+
private String harmonyUrl;
16+
private String minVersion;
17+
18+
public boolean isNeedUpdate() {
19+
return needUpdate;
20+
}
21+
22+
public void setNeedUpdate(boolean needUpdate) {
23+
this.needUpdate = needUpdate;
24+
}
25+
26+
public boolean isForceUpdate() {
27+
return forceUpdate;
28+
}
29+
30+
public void setForceUpdate(boolean forceUpdate) {
31+
this.forceUpdate = forceUpdate;
32+
}
33+
34+
public String getLatestVersion() {
35+
return latestVersion;
36+
}
37+
38+
public void setLatestVersion(String latestVersion) {
39+
this.latestVersion = latestVersion;
40+
}
41+
42+
public int getBuildNumber() {
43+
return buildNumber;
44+
}
45+
46+
public void setBuildNumber(int buildNumber) {
47+
this.buildNumber = buildNumber;
48+
}
49+
50+
public String getTitle() {
51+
return title;
52+
}
53+
54+
public void setTitle(String title) {
55+
this.title = title;
56+
}
57+
58+
public String getMessage() {
59+
return message;
60+
}
61+
62+
public void setMessage(String message) {
63+
this.message = message;
64+
}
65+
66+
public String getDownloadUrl() {
67+
return downloadUrl;
68+
}
69+
70+
public void setDownloadUrl(String downloadUrl) {
71+
this.downloadUrl = downloadUrl;
72+
}
73+
74+
public String getRedirectUrl() {
75+
return redirectUrl;
76+
}
77+
78+
public void setRedirectUrl(String redirectUrl) {
79+
this.redirectUrl = redirectUrl;
80+
}
81+
82+
public String getHarmonyUrl() {
83+
return harmonyUrl;
84+
}
85+
86+
public void setHarmonyUrl(String harmonyUrl) {
87+
this.harmonyUrl = harmonyUrl;
88+
}
89+
90+
public String getMinVersion() {
91+
return minVersion;
92+
}
93+
94+
public void setMinVersion(String minVersion) {
95+
this.minVersion = minVersion;
96+
}
97+
}

src/main/java/cn/wildfirechat/app/shiro/ShiroConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
7878

7979
filterChainDefinitionMap.put("/amr2mp3", "anon");
8080
filterChainDefinitionMap.put("/avatar/**", "anon");
81+
filterChainDefinitionMap.put("/version/check", "anon");
8182

8283
//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
8384
filterChainDefinitionMap.put("/**", "login");

0 commit comments

Comments
 (0)