diff --git a/apps/core/src/modules/aggregate/aggregate.controller.ts b/apps/core/src/modules/aggregate/aggregate.controller.ts index 53cfe8885..7fdff5511 100644 --- a/apps/core/src/modules/aggregate/aggregate.controller.ts +++ b/apps/core/src/modules/aggregate/aggregate.controller.ts @@ -7,7 +7,7 @@ * Coding With IU */ -import { Controller, Get, Query } from '@nestjs/common'; +import { Controller, Get, Inject, Query } from '@nestjs/common'; import { CacheKey, CacheTTL } from '@nestjs/cache-manager'; import { ApiOperation } from '@nestjs/swagger'; import { Auth } from '~/shared/common/decorator/auth.decorator'; @@ -16,12 +16,18 @@ import { IsMaster } from '~/shared/common/decorator/role.decorator'; import { CacheKeys } from '~/shared/constants/cache.constant'; import { TopQueryDto } from './aggregate.dto'; import { AggregateService } from './aggregate.service'; +import { ServicesEnum } from '~/shared/constants/services.constant'; +import { ClientProxy } from '@nestjs/microservices'; +import { transportReqToMicroservice } from '~/shared/microservice.transporter'; +import { ConfigEvents } from '~/shared/constants/event.constant'; @Controller('aggregate') @ApiName export class AggregateController { constructor( private readonly aggregateService: AggregateService, + @Inject(ServicesEnum.config) + private readonly configService: ClientProxy, ) {} @Get('/') @@ -33,10 +39,13 @@ export class AggregateController { // this.configService.getMaster(), this.aggregateService.getAllCategory(), this.aggregateService.getAllPages(), - // this.configService.get("urls"), - // this.configService.get("site"), + transportReqToMicroservice( + this.configService, + ConfigEvents.ConfigGetByMaster, + 'site', + ), ]); - const [categories, pageMeta] = tasks.map((t) => { + const [categories, pageMeta, site] = tasks.map((t) => { if (t.status === 'fulfilled') { return t.value; } else { @@ -46,6 +55,7 @@ export class AggregateController { return { categories, pageMeta, + site, }; } @@ -74,7 +84,7 @@ export class AggregateController { @Get('/stat') @ApiOperation({ summary: '获取网站统计信息' }) - @Auth() + // @Auth() async stat() { const [count] = await Promise.all([this.aggregateService.getCounts()]); return { diff --git a/apps/core/src/modules/aggregate/aggregate.module.ts b/apps/core/src/modules/aggregate/aggregate.module.ts index bb38186c2..701df37ac 100644 --- a/apps/core/src/modules/aggregate/aggregate.module.ts +++ b/apps/core/src/modules/aggregate/aggregate.module.ts @@ -23,6 +23,10 @@ import { REDIS_TRANSPORTER } from '~/shared/constants/transporter.constants'; name: ServicesEnum.config, ...REDIS_TRANSPORTER, }, + { + name: ServicesEnum.page, + ...REDIS_TRANSPORTER, + } ]), ], controllers: [AggregateController], diff --git a/apps/core/src/modules/aggregate/aggregate.service.ts b/apps/core/src/modules/aggregate/aggregate.service.ts index 0d4920350..a0d92336f 100644 --- a/apps/core/src/modules/aggregate/aggregate.service.ts +++ b/apps/core/src/modules/aggregate/aggregate.service.ts @@ -7,66 +7,47 @@ * Coding With IU */ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { ReturnModelType } from '@typegoose/typegoose'; -import { AnyParamConstructor } from '@typegoose/typegoose/lib/types'; -import { pick } from 'lodash'; -import { CategoryService } from '~/apps/page-service/src/category.service'; +import { Inject, Injectable } from '@nestjs/common'; import { CategoryModel } from '~/apps/page-service/src/model/category.model'; -import { PageService } from '~/apps/page-service/src/page-service.service'; -import { PostService } from '~/apps/page-service/src/post-service.service'; import { CacheService } from '~/libs/cache/src'; import { CacheKeys } from '~/shared/constants/cache.constant'; import { RSSProps } from './aggregate.interface'; import { ServicesEnum } from '~/shared/constants/services.constant'; import { ClientProxy } from '@nestjs/microservices'; -import { ConfigEvents } from '~/shared/constants/event.constant'; +import { + CategoryEvents, + ConfigEvents, + PageEvents, + PostEvents, +} from '~/shared/constants/event.constant'; import { transportReqToMicroservice } from '~/shared/microservice.transporter'; +import { PostModel } from '~/apps/page-service/src/model/post.model'; @Injectable() export class AggregateService { constructor( - @Inject(forwardRef(() => PostService)) - private readonly postService: PostService, - @Inject(forwardRef(() => PageService)) - private readonly pageService: PageService, - @Inject(forwardRef(() => CategoryService)) - private readonly categoryService: CategoryService, + @Inject(ServicesEnum.page) + private readonly pageService: ClientProxy, @Inject(ServicesEnum.config) private readonly configService: ClientProxy, private readonly redis: CacheService, ) {} - getAllCategory() { - return this.categoryService.getAllCategories(); + async getAllCategory() { + // return this.categoryService.getAllCategories(); + return await transportReqToMicroservice( + this.pageService, + CategoryEvents.CategoryGetAll, + {}, + ); } - getAllPages() { - return this.pageService.model - .find({}, 'title _id slug order') - .sort({ - order: -1, - modified: -1, - }) - .lean(); - } - - /** - * findTop 查询最新文章 - * @param model 模型 - * @param condition 查询条件 - * @param size 获取数量 - */ - private findTop< - U extends AnyParamConstructor, - T extends ReturnModelType, - >(model: T, condition = {}, size = 6) { - // 获取置顶文章 - return model - .find(condition) - .sort({ created: -1 }) - .limit(size) - .select('_id title name slug created text'); + async getAllPages() { + return await transportReqToMicroservice( + this.pageService, + PageEvents.PagesGetAll, + {}, + ); } /** @@ -75,24 +56,15 @@ export class AggregateService { * @param isMaster 是否主人 */ async topActivity(size = 6, isMaster = false) { - const [posts] = await Promise.all([ - this.findTop( - this.postService.model, - !isMaster ? { hide: false } : {}, + const posts = await transportReqToMicroservice( + this.pageService, + PostEvents.PostGetTopActivity, + { size, - ) - .populate('categoryId') - .lean() - .then((res) => { - return res.map((post) => { - post.category = pick(post.categoryId, ['name', 'slug']); - delete post.categoryId; - return post; - }); - }), - ]); - - return { posts }; + isMaster, + }, + ); + return posts; } /** @@ -102,14 +74,21 @@ export class AggregateService { // const { // urls: { webUrl: baseURL }, // } = await this.configService.waitForConfigReady(); + const { frontUrl: baseURL } = await transportReqToMicroservice( + this.configService, + ConfigEvents.ConfigGetByMaster, + 'site', + ); const combineTasks = await Promise.all([ - this.postService.model - .find({ - hide: false, // 只获取发布的文章 - password: { $nq: null }, // 只获取没有密码的文章 - rss: true, // 只获取公开RSS的文章 - }) - .populate('category') + transportReqToMicroservice( // FIX: Maybe here will be crash. + this.pageService, + PostEvents.PostsListGetAll, + { + hide: false, + password: false, + rss: true, + }, + ) .then((list) => { // 如果文章存在密码,则不获取 return list.filter((document) => { @@ -123,7 +102,7 @@ export class AggregateService { `/posts/${(document.category as CategoryModel).slug}/${ document.slug }`, - // baseURL + baseURL, ), published_at: document.modified ? new Date(document.modified) @@ -141,41 +120,50 @@ export class AggregateService { * getRSSFeedContent 获取RSS内容 */ async getRSSFeedContent() { - // const { - // urls: { webUrl }, - // } = await this.configService.waitForConfigReady(); - - // const baseURL = webUrl.replace(/\/$/, ""); + const { frontUrl: baseURL } = await transportReqToMicroservice( + this.configService, + ConfigEvents.ConfigGetByMaster, + 'site', + ); const [posts] = await Promise.all([ - await this.postService.model - .find({ + transportReqToMicroservice( // FIX: Maybe here will be crash. + this.pageService, + PostEvents.PostsListGetAll, + { hide: false, + password: false, rss: true, - }) - .limit(10) - .sort({ created: -1 }) - .populate('category') - .then((list) => { - // 如果文章存在密码,则不获取 - return list.filter((document) => { - return document.password === null; - }); - }), + }, + ).then((list) => { + return list.filter((document) => { + return document.password === null; + }); + }), ]); const postsRss: RSSProps['data'] = posts.map((post) => { return { - id: String(post._id), + id: String(post.id), title: post.title, text: post.text, created: post.created!, modified: post.modified || null, - // link: baseURL + this.urlService.build(post), + link: new URL( + `/posts/${(post.category as CategoryModel).slug}/${post.slug}`, + baseURL, + ), }; }); - return postsRss - .sort((a, b) => b.created!.getTime() - a.created!.getTime()) - .slice(0, 10); + return ( + postsRss + // created 传输过来会自动从 Date 转换为 String,所以需要重新转换 + .sort( + (a, b) => + new Date(b.created as any).getTime() - + new Date(a.created as any).getTime(), + ) + .slice(0, 10) + ); } /** @@ -184,11 +172,14 @@ export class AggregateService { */ async buildRssStructure(): Promise { const data = await this.getRSSFeedContent(); - const title = (await transportReqToMicroservice( - this.configService, - ConfigEvents.ConfigGetByMaster, - 'seo', - )).title || ''; + const title = + ( + await transportReqToMicroservice( + this.configService, + ConfigEvents.ConfigGetByMaster, + 'seo', + ) + ).title || ''; return { title, data, @@ -196,14 +187,19 @@ export class AggregateService { } async getCounts() { + async function countDocuments( + client: ClientProxy, + event: string, + options?: any, + ): Promise { + const list = await transportReqToMicroservice(client, event, options || {}); + return list.length; + } + const [posts, pages, categories] = await Promise.all([ - this.postService.model.countDocuments({ - hide: false, - password: { $nq: null }, - rss: true, - }), - this.pageService.model.countDocuments(), - this.categoryService.model.countDocuments(), + countDocuments(this.pageService, PostEvents.PostsListGetAll), + countDocuments(this.pageService, PageEvents.PagesGetAll), + countDocuments(this.pageService, CategoryEvents.CategoryGetAll), ]); return { diff --git a/apps/page-service/src/page-service.controller.ts b/apps/page-service/src/page-service.controller.ts index 433e104f8..d4fdeaf55 100644 --- a/apps/page-service/src/page-service.controller.ts +++ b/apps/page-service/src/page-service.controller.ts @@ -220,8 +220,15 @@ export class PageServiceController { } @MessagePattern({ cmd: PostEvents.PostsListGetAll }) - async getPostsList() { - return this.postService.model.find().sort({ created: -1 }); + async getPostsList(condition?: { + hide?: boolean; + password?: boolean; + rss?: boolean; + }) { + return this.postService.model.find({ + ...condition, + password: condition?.password ? { $ne: null } : null, + }).sort({ created: -1 }); } @MessagePattern({ cmd: PostEvents.PostGetByMaster }) @@ -275,4 +282,10 @@ export class PageServiceController { async thumbUpPost(_id: string) { throw new NotImplementedRpcExcption('Not Implemented'); } + + @MessagePattern({ cmd: PostEvents.PostGetTopActivity }) + @ApiOperation({ summary: '获取热门文章' }) + async getTopActivity(input: { size: number; isMaster: boolean }) { + return this.postService.topActivity(input.size, input.isMaster); + } } diff --git a/apps/page-service/src/post-service.service.ts b/apps/page-service/src/post-service.service.ts index 0d8996e01..6c23c3828 100644 --- a/apps/page-service/src/post-service.service.ts +++ b/apps/page-service/src/post-service.service.ts @@ -12,10 +12,12 @@ import { addYearCondition } from '~/shared/transformers/db-query.transformer'; import { CategoryService } from './category.service'; import { PostModel } from './model/post.model'; import { isDefined } from 'class-validator'; -import { omit } from 'lodash'; +import { omit, pick } from 'lodash'; import { ClientProxy } from '@nestjs/microservices'; import { ExceptionMessage } from '~/shared/constants/echo.constant'; -import { ModelType } from '@typegoose/typegoose/lib/types'; +import { + ModelType, +} from '@typegoose/typegoose/lib/types'; import { ServicesEnum } from '~/shared/constants/services.constant'; import { NotificationEvents } from '~/shared/constants/event.constant'; import { BadRequestRpcExcption } from '~/shared/exceptions/bad-request-rpc-exception'; @@ -318,6 +320,46 @@ export class PostService { }); } + /** + * findTop 查询最新文章 + * @param model 模型 + * @param condition 查询条件 + * @param size 获取数量 + */ + private findTop(condition = {}, size = 6) { + // 获取置顶文章 + return this.postModel + .find(condition) + .sort({ created: -1 }) + .limit(size) + .select('_id title name slug created text'); + } + + /** + * topActivity 查询最新文章 + * @param size 获取数量 + * @param isMaster 是否主人 + */ + async topActivity(size = 6, isMaster = false) { + const [posts] = await Promise.all([ + this.findTop( + !isMaster ? { hide: false } : {}, + size, + ) + .populate('categoryId') + .lean() + .then((res) => { + return res.map((post) => { + post.category = pick(post.categoryId, ['name', 'slug']) as any; + const { categoryId: _, ...rest } = post; + return rest; + }); + }), + ]); + + return { posts }; + } + async CreateDefaultPost(cateId: string) { await this.postModel.countDocuments({}).then(async (count) => { if (!count) { diff --git a/mog-core.code-workspace b/mog-core.code-workspace index 6a8bec29f..f5c491c1e 100644 --- a/mog-core.code-workspace +++ b/mog-core.code-workspace @@ -4,6 +4,9 @@ "path": "apps/core", "name": "core-gateway" }, + { + "path": "apps/config-service", + }, { "path": "apps/page-service" }, diff --git a/shared/constants/event.constant.ts b/shared/constants/event.constant.ts index ee3bdaa0e..906c05e62 100644 --- a/shared/constants/event.constant.ts +++ b/shared/constants/event.constant.ts @@ -30,6 +30,7 @@ export enum PostEvents { PostPatch = 'post.patch', PostDelete = 'post.delete', PostThumbUp = 'post.thumbup', + PostGetTopActivity = 'post.get.top.activity', Ping = 'post.ping', }