18 Commits
76 changed files with 21017 additions and 329 deletions

No files matched your search

+4 -3
View File
@@ -94,10 +94,11 @@ ZIP 会生成到 `.artifacts/app-updates`,根目录直接包含 `index.html`
`versions.props` 的 `JiaowuFrontendVersion`(传入 `--version` 时也必须一致)。使用
SuperAdmin 进入“运维与审计 → App 前端热更新”,上传 ZIP,填写目标平台、通道和
兼容的原生版本后先保存为草稿,再执行发布。当前 Android 工程的 `versionName` 为
`1.0`,因此对应更新包的“兼容原生版本”应填写 `1.0`。
`2.6.0`,因此对应更新包的“兼容原生版本”应填写 `2.6.0`。
App 启动、以及从后台回到前台超过 5 分钟后,会向 `/api/app-updates/latest` 检查版本,
在后台下载并校验服务端提供的 SHA-256;切到后台或下次启动时切换,并保留当前页面地址。
App 每次启动、网络恢复以及从后台回到前台时,都会向 `/api/app-updates/latest` 检查版本,
在后台下载并校验服务端提供的 SHA-256。下载完成后,用户可在常驻提示中确认立即重新加载;
若选择稍后,切到后台或下次启动时自动切换,并保留当前页面地址。
新资源若未能成功启动,原生更新器会自动回滚。再次发布
已归档版本即可回滚正式通道;不同原生版本、Android/iOS、测试/正式通道彼此隔离。
更新版本元数据和 ZIP 保存在数据库中,部署新服务端版本前必须先执行
+29
View File
@@ -0,0 +1,29 @@
using System.Text;
using System.Text.Json;
namespace Jiaowu.Api.Contracts;
/// <summary>Encodes endpoint-specific keyset values without exposing a query offset.</summary>
public static class CursorToken
{
private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web);
public static string Encode<T>(T value) =>
Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value, Options)))
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
public static bool TryDecode<T>(string? token, out T? value)
{
value = default;
if (string.IsNullOrWhiteSpace(token)) return true;
try
{
var normalized = token.Replace('-', '+').Replace('_', '/');
normalized = normalized.PadRight(normalized.Length + (4 - normalized.Length % 4) % 4, '=');
value = JsonSerializer.Deserialize<T>(Encoding.UTF8.GetString(Convert.FromBase64String(normalized)), Options);
return value is not null;
}
catch (JsonException) { return false; }
catch (FormatException) { return false; }
}
}
+15
View File
@@ -1,5 +1,20 @@
namespace Jiaowu.Api.Contracts;
/// <summary>
/// A forward-only keyset page. <see cref="NextCursor"/> is opaque to clients and
/// must be sent back unchanged as the <c>cursor</c> query parameter.
/// </summary>
public sealed record CursorPagedResult<T>(
IReadOnlyCollection<T> Items,
string? NextCursor,
bool HasMore,
int PageSize,
int? Total = null);
/// <summary>
/// Legacy offset-page response. New list endpoints must use
/// <see cref="CursorPagedResult{T}"/> instead.
/// </summary>
public sealed record PagedResult<T>(
IReadOnlyCollection<T> Items,
int Total,
Loaded 3 of 76 files, more files were not shown because too many files have changed in this diff. Show more