# 小红书作品（笔记）每日统计数据导入逻辑与字段映射

## 接口信息

- **接口**：`GET https://creator.xiaohongshu.com/api/galaxy/creator/datacenter/note/base?note_id={noteId}`
- **页面**：`https://creator.xiaohongshu.com/statistics/note-detail?noteId={noteId}`
- **数据来源**：返回体中的 `data.day`（日维度趋势数据）

## 导入流程

1. **遍历账号下所有作品**：从 `works` 表查询该账号的所有小红书作品
2. **访问笔记详情页**：打开 `statistics/note-detail?noteId={noteId}`
3. **监听接口响应**：等待 `note/base` 接口响应（超时 30 秒）
4. **解析数据**：
   - 提取 `data.day` 下的各 `*_list` 数组
   - 按 `date`（毫秒时间戳）合并为「按日一条」的数据
   - 使用 `getChinaDateFromTimestamp` 将时间戳转成中国时区日期
5. **过滤时间范围**：默认只保留「作品发布后 14 天内」的数据（`publishTime + 13 天`）
6. **批量写入**：调用 `WorkDayStatisticsService.saveStatisticsForDateBatch` 写入 `work_day_statistics` 表

## 字段映射表

### 数值型字段（每日增量，累加）

| work_day_statistics 字段 | data.day 来源 | 说明 |
|--------------------------|---------------|------|
| record_date | 各 list 项的 `date`（毫秒 → 中国时区当天 0 点） | 按日期合并多条 list |
| play_count | view_list[].count | 播放(阅读)量 |
| exposure_count | imp_list[].count | 曝光量/展现量 |
| like_count | like_list[].count | 点赞量 |
| comment_count | comment_list[].count | 评论量 |
| share_count | share_list[].count | 分享量 |
| collect_count | collect_list[].count | 收藏量 |
| fans_increase | rise_fans_list[].count | 涨粉数 |

### 比率类字段（百分比字符串，使用 `coun` 字段）

| work_day_statistics 字段 | data.day 来源 | 格式 |
|--------------------------|---------------|------|
| cover_click_rate | cover_click_rate_list[].coun | "14%" 或 "0" |
| two_second_exit_rate | exit_view2s_list[].coun | "5%" 或 "0" |
| completion_rate | finish_list[].coun | "15%" 或 "0" |

**说明**：比率类字段使用 `item.coun`（不是 `count`），如果值为 0 则存 "0"，否则存 `${n}%"`。

### 时长/数值字符串字段（不加 %）

| work_day_statistics 字段 | data.day 来源 | 格式 |
|--------------------------|---------------|------|
| avg_watch_duration | view_time_list[].count_with_double 或 count | 保留两位小数的字符串，如 "12.34" |

**说明**：优先使用 `count_with_double`，否则用 `count`，保留两位小数（四舍五入）。

## 数据汇总（同步到 works 表）

除了日统计，还会将 `data` 顶层的汇总指标同步到 `works` 表的 `yesterday*` 字段：

| works 字段 | data 来源 | 说明 |
|------------|-----------|------|
| yesterday_play_count | view_count | 总播放数 |
| yesterday_like_count | like_count | 总点赞数 |
| yesterday_comment_count | comment_count | 总评论数 |
| yesterday_share_count | share_count | 总分享数 |
| yesterday_collect_count | collect_count | 总收藏数 |
| yesterday_fans_increase | rise_fans_count | 总涨粉数 |
| yesterday_exposure_count | impl_count | 总曝光数 |
| yesterday_cover_click_rate | cover_click_rate | 封面点击率（格式化为 "14%"） |
| yesterday_avg_watch_duration | view_time_avg_with_double 或 view_time_avg | 平均观看时长（保留两位小数） |
| yesterday_completion_rate | full_view_rate | 完播率（格式化为 "15%"） |
| yesterday_two_second_exit_rate | exit_view2s_rate | 2秒退出率（格式化为 "5%"） |

## 时区处理

- **接口返回的 `date`**：中国时区（Asia/Shanghai）该日 0 点的 UTC 时间戳
- **解析方式**：使用 `getChinaDateFromTimestamp(ts)` 函数，通过 `Intl.DateTimeFormat` 按中国时区解析年月日
- **存储**：`record_date` 字段存为 DATE 类型，代表中国时区的日历日

## 时间范围限制

- **默认**：只保留「作品发布后 14 天内」的数据
  - 计算方式：`publishTime` 的当天 + 13 天 = 共 14 天
  - 例如：2026-01-01 发布，则保留 2026-01-01 至 2026-01-14 的数据
- **首批补数据**：可通过 `ignorePublishTimeLimit: true` 选项跳过此限制

## 错误处理

- **登录失效**：检测到跳转到 `login` 页面时，抛出 `XhsLoginExpiredError`
  - 首次失效：尝试刷新账号 cookie
  - 刷新成功：用新 cookie 重试一次
  - 刷新失败或重试仍失效：标记账号为 `expired` 状态
- **接口超时**：30 秒内未捕获到响应则跳过该作品，继续处理下一个
- **数据缺失**：`data.day` 为空或不存在时返回空数组，不写入

## 调用方式

### 定时任务（批量同步所有账号）

```typescript
await XiaohongshuWorkNoteStatisticsImportService.runDailyImport();
```

### 单账号同步

```typescript
const svc = new XiaohongshuWorkNoteStatisticsImportService();
await svc.importAccountWorksStatistics(account);
```

### 指定作品（首批补数据）

```typescript
await svc.importAccountWorksStatistics(account, false, {
  workIdFilter: [workId1, workId2],
  ignorePublishTimeLimit: true
});
```
