在项目列表展示场景中,按项目关联的最近未来事件升序、再按最近过去事件降序排列是提升用户体验的常见需求,这种排序方式能让用户优先关注即将发生的项目事件,同时快速回溯最近的过往项目动态。

核心排序逻辑拆解
要实现目标排序效果,需要先明确两个核心排序维度:
- 第一维度:区分项目关联的是未来事件还是过去事件,未来事件对应的项目整体排在过去事件对应的项目之前。
- 第二维度:未来事件的项目按照最近未来事件的时间升序排列,也就是时间越近的未来事件排越前;过去事件的项目按照最近过去事件的时间降序排列,也就是时间越近的过去事件排越前。
数据预处理步骤
在正式排序前,需要先对每个项目关联的事件做预处理,提取两个关键字段:
- 最近未来事件时间:如果项目存在未来事件(事件时间晚于当前时间),取时间最小的那个未来事件的时间;如果不存在未来事件,该字段记为null。
- 最近过去事件时间:如果项目存在过去事件(事件时间早于或等于当前时间),取时间最大的那个过去事件的时间;如果不存在过去事件,该字段记为null。
具体实现代码示例
JavaScript实现示例
假设当前项目列表数据结构如下,每个项目包含id、name以及关联的事件列表events,每个事件包含event_time字段:
// 模拟项目数据
const projectList = [
{
id: 1,
name: '项目A',
events: [
{ event_time: '2024-05-20 10:00:00' }, // 未来事件
{ event_time: '2024-03-10 14:00:00' } // 过去事件
]
},
{
id: 2,
name: '项目B',
events: [
{ event_time: '2024-06-01 09:00:00' }, // 未来事件
{ event_time: '2024-04-05 16:00:00' } // 过去事件
]
},
{
id: 3,
name: '项目C',
events: [
{ event_time: '2024-02-15 11:00:00' } // 仅过去事件
]
}
];
// 获取当前时间的时间戳
const now = new Date().getTime();
// 预处理每个项目的最近未来事件时间和最近过去事件时间
const processedProjects = projectList.map(project => {
let nearestFutureTime = null;
let nearestPastTime = null;
project.events.forEach(event => {
const eventTime = new Date(event.event_time).getTime();
if (eventTime > now) {
// 更新最近未来事件时间,取最小的未来时间
if (nearestFutureTime === null || eventTime < nearestFutureTime) {
nearestFutureTime = eventTime;
}
} else {
// 更新最近过去事件时间,取最大的过去时间
if (nearestPastTime === null || eventTime > nearestPastTime) {
nearestPastTime = eventTime;
}
}
});
return {
...project,
nearestFutureTime,
nearestPastTime
};
});
// 执行排序
processedProjects.sort((a, b) => {
// 第一优先级:有未来事件的项目排在有过去事件的项目前面
const aHasFuture = a.nearestFutureTime !== null;
const bHasFuture = b.nearestFutureTime !== null;
if (aHasFuture && !bHasFuture) return -1;
if (!aHasFuture && bHasFuture) return 1;
// 都有未来事件,按最近未来事件升序排列
if (aHasFuture && bHasFuture) {
return a.nearestFutureTime - b.nearestFutureTime;
}
// 都有过去事件,按最近过去事件降序排列
return b.nearestPastTime - a.nearestPastTime;
});
console.log('排序后的项目列表:', processedProjects);
SQL实现示例
如果数据存储在关系型数据库中,项目表和事件表分开存储,也可以通过SQL直接实现排序逻辑,假设表结构如下:
- projects表:id, name
- events表:id, project_id, event_time
-- 先预处理每个项目的最近未来事件时间和最近过去事件时间
WITH project_event_info AS (
SELECT
p.id AS project_id,
p.name AS project_name,
-- 最近未来事件时间,取最小的未来事件时间
MIN(CASE WHEN e.event_time > NOW() THEN e.event_time END) AS nearest_future_time,
-- 最近过去事件时间,取最大的过去事件时间
MAX(CASE WHEN e.event_time <= NOW() THEN e.event_time END) AS nearest_past_time
FROM projects p
LEFT JOIN events e ON p.id = e.project_id
GROUP BY p.id, p.name
)
-- 执行排序
SELECT project_id, project_name, nearest_future_time, nearest_past_time
FROM project_event_info
ORDER BY
-- 有未来事件的项目排前面
CASE WHEN nearest_future_time IS NOT NULL THEN 0 ELSE 1 END,
-- 未来事件按升序排列
nearest_future_time ASC,
-- 过去事件按降序排列
nearest_past_time DESC;
注意事项
在实际落地时需要注意几个细节:
- 时间字段的时区问题,需要统一所有时间的时区,避免排序出现偏差。
- 如果项目既没有未来事件也没有过去事件,可以根据业务需求将其排在最后,上述示例中这类项目会排在仅过去事件的项目之后。
- 如果项目有多个未来事件或过去事件,一定要取对应的最近时间作为排序依据,而不是随机取一个事件时间。