日程改为全量同步 + 修复大响应超时(laoyang/FamilyBook#31)

## 背景

用户 2026-10-30:「全部拉取,全量同步」。
起因:切到下个月再点某一天,端上没数据(即使服务端有)。
根因是请求范围写死成「今天-7 ~ +60」且切月不重拉 ⇒ 窗口外的日子永远空。

## 改动

### FbModuleLoad.loadModule
- `name` 参数从 `FbModuleName` 放宽到 `string`
  日程改成全量后,缓存键要带请求窗口(`calendar:1970-01-01:2100-12-31`),
  否则"旧窗口的数据"会被当成新窗口的数据显示。
- 新增可选 `timeoutMs`
  全量约 9.3MB / 28628 条,默认 15 秒在移动网络下会超时;
  超时后 `FbModuleRepo` 退回旧缓存,表现成"翻月没数据"且极难查。

### FbModuleRepo.load
- 透传 `timeoutMs` 给 `FbApi`。

### FbCalendarPage
- 请求改为全量范围(`CAL_FULL_FROM` ~ `CAL_FULL_TO`),超时放宽到 60 秒。
- 缓存键带窗口(`calendar:<from>:<to>`):某次请求失败后 `FbModuleRepo`
  会把**旧范围**的缓存 sink 回来,端上就把"只含 10~12 月的数据"当全量显示
  —— 这是该 bug 的帮凶,加窗口标识后从根上消失。
- `agendaDays()` 的 `-7 ~ +60` 现在**只决定列表显示多少天**,
  不再兼作请求范围(原来两个数必须同步的约束随全量同步作废)。
- 删除 `agendaFrom()` / `agendaTo()`(不再需要按当前日期算窗口)。

### 恢复 canEdit 的写权限判断
本轮改动曾把
    `isEventWritable(this.detailBase, this.books)`
回退成
    `isEditableEvent(this.detailBase)`
这是**工单 #31 已修过的 bug 回退**:只判 kind/id/calId 不看本的可写性,
导致只读共享日历里的日程也显示「编辑」按钮,用户改完点保存才报
「这本日历你没有写权限」。

已恢复,并与首页 `FbHomePage.ets:1517` 保持同一口径
(否则同一事件"从首页进能编辑、从日历页进不能")。

## 验证

`build_hap debug` → `BUILD SUCCESSFUL`(33 tasks)。

⚠️ **未做真机验证**。全量同步的实际耗时与内存占用需在真机上确认
(28628 条约 9.3MB,解析 150~300ms,主要成本是流量)。

## 备注

这 3 个文件的改动来自 2026-10-30 那一轮,因故未提交;
本次一并整理提交,与品牌改名(家书→岁月家)无关。
This commit is contained in:
2026-11-01 22:54:22 +08:00
parent d2129c9aab
commit b32501535e
3 changed files with 91 additions and 42 deletions
+14 -2
View File
@@ -79,10 +79,22 @@ export const FB_MODULE_PATH: Record<string, string> = {
* 这是**好事**(月视图也能直接命中缓存),但**不要**指望缓存里的范围
* 与当前视图严格一致;页面永远按自己需要的日期去索引。
*/
export async function loadModule<T>(name: FbModuleName, parse: (json: string) => T,
/**
* @param name 缓存键。**接收任意字符串**而不是只收 `FbModuleName`:
* 🔴 2026-10-30:日程改成全量同步后,缓存键要带上请求窗口
* (`calendar:1970-01-01:2100-12-31`),否则"旧窗口的数据"会被当成
* 新窗口的数据显示 —— 那正是"翻月没数据/数据不对"的根因之一。
* `FbModuleRepo.load` 本来就是 `string`,这里放宽是为了让调用方传得进来。
* @param timeoutMs 可选:本次请求超时(毫秒)。
* 🔴 2026-10-30:日程改成一次拉**全量**(约 9.3MB / 28628 条),
* 默认 15 秒在移动网络下会超时;超时后 `FbModuleRepo` 退回旧缓存,
* 表现成"翻月没数据"且极难查。⇒ 大响应必须能放宽超时。
*/
export async function loadModule<T>(name: string, parse: (json: string) => T,
sink: (data: T, fromCache: boolean) => void,
onNote?: (note: string) => void,
pathOverride?: string): Promise<void> {
pathOverride?: string,
timeoutMs?: number): Promise<void> {
const path: string = pathOverride !== undefined && pathOverride !== ''
? pathOverride
+7 -1
View File
@@ -65,8 +65,11 @@ export class FbModuleRepo {
/**
* @param name 缓存键(**不限于预定义模块**:两级接口用完整路径当键,
* 如 'ledger_detail_1')。
* @param timeoutMs 可选:本次请求超时(毫秒)。大响应(如日程全量 9.3MB)
* 必须放宽,否则 15 秒超时后会退回旧缓存,
* 表现成"翻月没数据",极难查。
*/
async load(name: string, path: string, sink: FbModuleSink): Promise<void> {
async load(name: string, path: string, sink: FbModuleSink, timeoutMs?: number): Promise<void> {
// ① 本地缓存(若还没读过本地库,`readCache` 会返回空串,不影响)
const cached: string = await this.readCache(name);
if (cached !== '') {
@@ -81,6 +84,9 @@ export class FbModuleRepo {
return;
}
const opts: FbApiOptions = { method: 'GET', token: token };
if (timeoutMs !== undefined && timeoutMs > 0) {
opts.timeoutMs = timeoutMs;
}
const res: FbApiResult = await fbApi(path, opts);
if (!res.ok) {
hilog.warn(DOMAIN, TAG, '加载 %{public}s 失败:%{public}s', name, res.error);