Skip to content

Commit d72cb29

Browse files
committed
feat(note): add topic recent-update endpoint and api-client method
Returns max(modified, created) across published notes of a topic. Frontends previously inferred this from the latest note (size=1) which broke when modified was null on most notes.
1 parent a6c3c0a commit d72cb29

4 files changed

Lines changed: 51 additions & 1 deletion

File tree

apps/core/src/modules/note/note.controller.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,18 @@ export class NoteController {
722722
return result
723723
}
724724

725+
@Get('/topics/:id/recent-update')
726+
async getTopicRecentUpdate(
727+
@Param() params: MongoIdDto,
728+
@HasAdminAccess() isAuthenticated: boolean,
729+
) {
730+
const ts = await this.noteService.getTopicRecentUpdate(
731+
params.id,
732+
isAuthenticated,
733+
)
734+
return { ts }
735+
}
736+
725737
@Patch('/:id/publish')
726738
@Auth()
727739
async setPublishStatus(

apps/core/src/modules/note/note.service.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,4 +604,21 @@ export class NoteService {
604604
},
605605
)
606606
}
607+
608+
async getTopicRecentUpdate(
609+
topicId: string,
610+
isAuthenticated: boolean,
611+
): Promise<Date | null> {
612+
const objectId = new this.model.base.Types.ObjectId(topicId)
613+
const match: Record<string, unknown> = { topicId: objectId }
614+
if (!isAuthenticated) match.isPublished = true
615+
616+
const [doc] = await this.model.aggregate<{ ts: Date }>([
617+
{ $match: match },
618+
{ $project: { ts: { $ifNull: ['$modified', '$created'] } } },
619+
{ $sort: { ts: -1 } },
620+
{ $limit: 1 },
621+
])
622+
return doc?.ts ?? null
623+
}
607624
}

packages/api-client/__tests__/controllers/note.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { vi } from 'vitest'
2+
13
import { mockRequestInstance } from '~/__tests__/helpers/instance'
24
import { mockResponse } from '~/__tests__/helpers/response'
35
import { axiosAdaptor } from '~/adaptors/axios'
46
import { NoteController } from '~/controllers'
57
import { RequestError } from '~/core'
6-
import { vi } from 'vitest'
78

89
const { spyOn } = vi
910

@@ -119,4 +120,14 @@ describe('test note client', () => {
119120

120121
expect(data).toEqual({ data: [], pagination: {} })
121122
})
123+
124+
test('GET /notes/topics/:id/recent-update', async () => {
125+
mockResponse('/notes/topics/11111111/recent-update', {
126+
ts: '2025-10-19T14:57:30.803Z',
127+
})
128+
129+
const data = await client.note.getTopicRecentUpdate('11111111')
130+
131+
expect(data).toEqual({ ts: '2025-10-19T14:57:30.803Z' })
132+
})
122133
})

packages/api-client/controllers/note.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,14 @@ export class NoteController<ResponseWrapper> implements IController {
213213
params: { page, size, lang, ...sortOptions },
214214
})
215215
}
216+
217+
/**
218+
* 获取专栏的最近更新时间(取该专栏下所有可见日记 max(modified, created))
219+
* @param topicId 专栏 ID
220+
*/
221+
getTopicRecentUpdate(topicId: string) {
222+
return this.proxy.topics(topicId)['recent-update'].get<{
223+
ts: string | null
224+
}>()
225+
}
216226
}

0 commit comments

Comments
 (0)