2.11 所有博客

62 阅读2分钟

获取所有博客

posts.ejs

修改views/posts.ejs:

<%- include('header') %>
<% posts.forEach(function (post) { %>
<%- include('components/post-content', { post: post }) %>
<% }) %>
<%- include('footer') %>

posts.service.ts

修改src/posts/posts.service.ts,增加一个findAll方法:

async findAll(options: PopulateOptions = {}) {
    const posts = await this.model.findAll();
    if (options.isWithUserInfo) {
      await Promise.all(
        posts.map(async (post) => {
          const user = await this.userService.getUserById(post.userId);
          post.author = user;
        }),
      );
    }
    // 增加浏览次数
    if (options.isIncrementPv) {
      posts.forEach((post) => {
        this.model.findByIdAndUpdate(post.id, {
          pv: post.pv + 1,
        }).catch(this.logger.error);
      });
    }
    posts.forEach((post) => {
      this.format(post);
    });
    return posts;
  }

注意,这里我在for循环中调用getUserById,这在真实开发中是不允许的。

因为这是一个调用数据库的操作,我们的程序与数据库的交互与I/O一样,是很昂贵的。我们应该尽可能减少交互次数。也就是说,如果是调用数据库,这里应该是先得到userId的数组,再调用userService的接口,一次性拿到所有数据,然后进行组装。

顺便也提一句,如果读取本地磁盘的文件,通常的做法是先探测下(stat)这个文件存不存在,但当这个文件存在时就浪费了一次I/O,所以更优的方法是直接调用read方法,再处理失败信息。

所以,代码可以这样优化,在src/mod.ts中增加一个findByIds方法:

async findByIds(ids: string[]): Promise<U[]> {
    const arr = await Promise.all(ids.map((id) => this.findById(id)));
    return arr.filter(Boolean) as U[];
 }

在src/user/user.service.ts中使用:

  async getUsersByIds(ids: string[]) {
    return this.userModel.findByIds(ids);
  }

这段代码修改为:

const users = await this.userService.getUsersByIds(
  posts.map((post) => post.userId),
);
posts.forEach((post) => {
  post.author = users.find((user) => user.id === post.userId);
}); 

整体修改为:

 async findAll(options: PopulateOptions = {}) {
    const posts = await this.model.findAll();
    await this.formatPosts(posts, options);
    return posts;
  }

  private async formatPosts(
    posts: ModelWithId<Post>[],
    options: PopulateOptions = {},
  ) {
    if (options.isWithUserInfo) {
      const users = await this.userService.getUsersByIds(
        posts.map((post) => post.userId),
      );
      posts.forEach((post) => {
        post.author = users.find((user) => user.id === post.userId);
      });
    }
    // 增加浏览次数
    if (options.isIncrementPv) {
      posts.forEach((post) => {
        this.model.findByIdAndUpdate(post.id, {
          pv: post.pv + 1,
        }).catch(this.logger.error);
      });
    }
    posts.forEach((post) => {
      this.format(post);
    });
  }

posts.controller.ts

修改src/posts/posts.controller.ts的getAll:

@Get("/")
async getAll(@Render() render: Render) {
  const posts = await this.postsService.findAll({
    isWithUserInfo: true,
  });
  return await render("posts", {
    posts,
  });
}

验证

注册几个用户,发表不同的文章。 访问http://localhost:8000/posts, 看到所有博客数据:

image.png

每一篇标题也能点击进去,看到博客详情。

点击用户头像,页面会进行跳转:http://localhost:8000/posts?userId=gxIKUVw8E84Bxtr1QYWxC。这时应该只有该作者的博客。所以我们下来应该做下处理。

获取作者的博客

posts.service.ts

修改posts.service.ts,新增一个findByUserId方法:

 async findByUserId(userId: string, options: PopulateOptions = {}) {
    const posts = await this.model.findMany({
      userId,
    });
    await this.formatPosts(posts, options);
    return posts;
  }

修改posts.controller.ts,修改getAll方法:


  async getAll(@Query("userId") userId: string, @Render() render: Render) {
    if (userId) {
      const posts = await this.postsService.findByUserId(userId, {
        isWithUserInfo: true,
      });
      return render("posts", {
        posts,
      });
    } else {
      const posts = await this.postsService.findAll({
        isWithUserInfo: true,
      });
      return render("posts", {
        posts,
      });
    }
  }

验证

点击不同用户的头像,可以看到不同的页面,比如冯巩的:

image.png

比如天仙的:

image.png

作业

你会发现,新创建的博客在下面,这个不符合阅读习惯。试着按创建时间倒序排列。