# 数据分页查询接口文档(假设根据createTime查询)
## 接口基本信息
- **接口名称**:按创建时间分页查询数据(支持相同时间连续分页)
- **接口路径**:`/api/data/queryByCreateTime`
- **请求方法**:GET
- **接口描述**:根据起始时间、上次最后一条数据的唯一标识,分页查询按`createTime`(创建时间)升序+`id`(业务唯一标识)升序排序的数据;首次查询可传递`lastId=0`,后续每次查询传递上次返回数据列表最后一条的`id`。
## 请求参数
| 参数名 | 类型 | 是否必填 | 示例值 | 描述 |
|---------------|--------|----------|----------------------|----------------------------------------------------------------------|
| startTime | number | 是 | 1620000000000 | 查询起始时间(13位时间戳,毫秒级);首次查询传`0`(从最早数据开始);后续查询传上次返回数据列表最后一条的`createTime`。 |
| lastId | string | 是 | "id_50" 或 "0" | 上次查询返回数据列表最后一条的`id`;**首次查询传递`0`,非首次查询传递实际`id`**(若未传入则默认按`0`处理)。 |
| pageSize | number | 是 | 50 | 每次查询的最大条数;取值范围1-1000(超出范围按默认100处理)。 |
## 响应参数
| 字段名 | 类型 | 示例值 | 描述 |
|-----------------|-------------|----------------------|----------------------------------------------------------------------|
| code | number | 200 | 状态码 |
| message | string | "查询成功" | 响应提示信息。 |
| data | object | - | 响应数据主体。 |
| data.list | array | - | 本次查询的数据列表(按`createTime`升序、`id`升序排序);若无数据则返回空数组。 |
| data.list[].id | string | "id_123" | 业务唯一标识(主键,用于相同`createTime`时的补充排序)。 |
| data.list[].createTime | number | 1620000000000 | 数据创建时间(13位时间戳)。 |
| data.list[].xxx | any | - | 其他业务字段(根据实际数据结构补充)。 |
## 错误响应示例
```json
{
"code": 400,
"message": "参数错误:pageSize超出范围(1-1000)",
"data": null
}
```
## 接口调用示例
### 场景:100条数据的`createTime`均为`T=1620000000000`,`id`为`id_1`至`id_100`,`pageSize=50`
#### 首次查询(从最早数据开始)
- **请求参数**:`startTime=0&lastId=0&pageSize=50`(首次查询`lastId`传`0`)
- **响应示例**:
```json
{
"code": 200,
"message": "查询成功",
"data": {
"list": [
{"idId": "id_1", "createTime": 1620000000000, "name": "数据1"},
// ... 中间48条数据
{"idId": "id_50", "createTime": 1620000000000, "name": "数据50"}
]
}
}
```
#### 二次查询(相同`createTime`内继续分页)
- **请求参数**:`startTime=1620000000000&lastId=id_50&pageSize=50`(非首次查询,`lastId`传上次最后一条的`id`)
- **响应示例**:
```json
{
"code": 200,
"message": "查询成功",
"data": {
"list": [
{"idId": "id_51", "createTime": 1620000000000, "name": "数据51"},
// ... 中间48条数据
{"idId": "id_100", "createTime": 1620000000000, "name": "数据100"}
]
}
}
```
#### 三次查询(无更多数据)
- **请求参数**:`startTime=1620000000000&lastId=id_100&pageSize=50`
- **响应示例**:
```json
{
"code": 200,
"message": "查询成功",
"data": {
"list": []
}
}
```
## 接口实现关键步骤
### 1. 排序规则(核心)
为解决相同`createTime`的数据分页连续性问题,底层查询使用**复合排序**:
```sql
ORDER BY createTime ASC, id ASC
```
- `createTime ASC`:确保整体按创建时间升序排列;
- `idId ASC`:当`createTime`相同时,按业务唯一标识`id`升序排列(`id`全局唯一且有序),保证相同时间的数据有固定顺序。
### 2. 查询条件(分页逻辑)
根据请求参数`startTime`和`lastId`构建查询条件(兼容首次/非首次查询,避免重复返回):
- **统一查询条件**:
满足“`createTime > startTime`” 或 “`createTime = startTime 且 id > lastId`”,既包含比`startTime`更新的数据,也包含相同`startTime`中未查询过的后续数据(通过`id`过滤)。
```sql
WHERE (createTime > #{startTime})
OR (createTime = #{startTime} AND idId > #{lastId})
ORDER BY createTime ASC, idId ASC
LIMIT #{pageSize}
```
如果希望延迟查询,增加 skipCurrentSeconds 参数,如 设置为15秒,
long endTime = 当前时间 - 15秒
改造如下
```sql
WHERE
((createTime > #{startTime})
OR (createTime = #{startTime} AND idId > #{lastId}))
and (createTime < #{endTime})
ORDER BY createTime ASC, idId ASC
LIMIT #{pageSize}
```
### 3. 调用方判断“是否有更多数据”
调用方通过本次返回的`data.list`长度判断:
- 若`list`长度等于`pageSize`:可能还有未查询的数据,可继续分页(下次查询用`list`最后一条的`createTime`作为`startTime`,`id`作为`lastId`);
- 若`list`长度小于`pageSize`或为空:表示已查询完所有符合条件的数据,无需继续查询。
## 备注说明
1. **`lastId`的使用**:首次查询必须传递`lastId=0`,非首次查询必须传递上次返回`list`最后一条的`id`(若未传入,后端默认按`0`处理,可能导致重复数据);
2. **`id`的特性**:`id`需为全局唯一且有序的字段(如自增主键、业务生成的有序ID等),确保相同`createTime`时的排序唯一性;
3. **无状态依赖**:服务端不存储调用方的历史查询信息,所有分页逻辑仅依赖当前请求的`startTime`和`lastId`,避免状态不一致问题;
4. **性能优化**:建议在`(createTime, id)`上建立联合索引,提升复合条件查询和排序的效率。