task-161(客户端公开版本接口兼容): 冻结 /api/version 响应契约
新增 PublicVersionLookup.currentVersionResponse + Task161Test。 TDD: RED→GREEN。
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package com.nanri.aiimage.modules.softwareversion.service.support;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端公开版本查询纯逻辑(module 09):空语义、created_at DESC + id 稳定排序、
|
||||||
|
* 无 ApiResponse 包装;供 PublicVersionController/契约测试复用。
|
||||||
|
*/
|
||||||
|
public final class PublicVersionLookup {
|
||||||
|
|
||||||
|
/** web_config 版本记录投影(id 用于同时间稳定排序)。 */
|
||||||
|
public record WebVersion(Long id, String version, String fileUrl, LocalDateTime createdAt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private PublicVersionLookup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取最新版本记录:created_at DESC,同一时间按 id 倒序稳定;空/无有效回 null。 */
|
||||||
|
public static WebVersion selectLatest(List<WebVersion> rows) {
|
||||||
|
if (rows == null || rows.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<WebVersion> list = new ArrayList<>(rows);
|
||||||
|
list.sort((a, b) -> {
|
||||||
|
int time = compareTime(b.createdAt(), a.createdAt());
|
||||||
|
if (time != 0) {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
long ai = a.id() == null ? 0L : a.id();
|
||||||
|
long bi = b.id() == null ? 0L : b.id();
|
||||||
|
return Long.compare(bi, ai);
|
||||||
|
});
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int compareTime(LocalDateTime a, LocalDateTime b) {
|
||||||
|
if (a == null && b == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return a.compareTo(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当前服务版本响应:保留 version/desc/url 字段(可为空字符串),不包装 ApiResponse。 */
|
||||||
|
public static Map<String, Object> currentVersionResponse(String version, String desc, String url) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("version", version == null ? "" : version);
|
||||||
|
map.put("desc", desc == null ? "" : desc);
|
||||||
|
map.put("url", url == null ? "" : url);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 最新版本响应:无记录返回 version=null,file_url=null(客户端语义)。 */
|
||||||
|
public static Map<String, Object> latestVersionResponse(WebVersion latest) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
if (latest == null) {
|
||||||
|
map.put("version", null);
|
||||||
|
map.put("file_url", null);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
map.put("version", latest.version());
|
||||||
|
map.put("file_url", latest.fileUrl());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package com.nanri.aiimage.modules.softwareversion.service.support;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/** task-161:冻结 /api/version 响应契约(version/desc/url 保留且无 ApiResponse 包装)。 */
|
||||||
|
class Task161Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_normal_primary_path() {
|
||||||
|
Map<String, Object> resp = PublicVersionLookup.currentVersionResponse("1.0.46", "x", "https://a");
|
||||||
|
assertEquals("1.0.46", resp.get("version"));
|
||||||
|
assertEquals("x", resp.get("desc"));
|
||||||
|
assertEquals("https://a", resp.get("url"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_normal_variant_input() {
|
||||||
|
Map<String, Object> resp = PublicVersionLookup.currentVersionResponse(null, null, null);
|
||||||
|
assertEquals("", resp.get("version"));
|
||||||
|
assertEquals("", resp.get("desc"));
|
||||||
|
assertEquals("", resp.get("url"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_repeated_is_idempotent() {
|
||||||
|
assertTrue(PublicVersionLookup.currentVersionResponse("v", "d", "u")
|
||||||
|
.equals(PublicVersionLookup.currentVersionResponse("v", "d", "u")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_boundary_empty_input() {
|
||||||
|
assertTrue(PublicVersionLookup.currentVersionResponse("", "", "").values().stream().allMatch(v -> "".equals(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_boundary_single_item() {
|
||||||
|
Map<String, Object> resp = PublicVersionLookup.currentVersionResponse("1.0.0", "", "");
|
||||||
|
assertEquals(3, resp.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_boundary_limit_or_missing_field() {
|
||||||
|
assertFalse(PublicVersionLookup.currentVersionResponse("v", "d", "u").containsKey("success"), "公开接口不包装信封");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_invalid_input_rejected() {
|
||||||
|
Map<String, Object> resp = PublicVersionLookup.currentVersionResponse("1", "a", "b");
|
||||||
|
assertEquals("1", resp.get("version"), "version 不被转数字");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_task_161_freeze_public_version_dependency_failure_returns_actionable_message() {
|
||||||
|
// 保留字段即使为空;desc/url 不能丢弃
|
||||||
|
assertEquals(3, PublicVersionLookup.currentVersionResponse("v", "", "").keySet().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user