Nestjs实现简单评论发布、获取逻辑

407 阅读1分钟

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

前言

在最近负责的项目中,有针对每一个项目进行项目跟进,如下图有接触、交流、poc、立项推动、招投标、交付六项跟进,每个跟进下都有评论模块

image.png

实现

数据库表字段设计

  • base.schema.ts

image.png

  • comments.schema.ts

image.png

基本逻辑

  • 数据库表字段设计如上(这是根据此次需求设计的字段,),在获取评论数据时,需要确定是在哪一个项目跟进下获取的,需要定义出当前跟进id(followId)以便做关联,并且评论可以回复,那便需要确定当前评论有无上一级或下一级,定义parentId作为关联,如果没有上一级就是第一级,parentId设置为0,回复评论的parentId为被回复评论的id值,举个例子如下:
idfollowTypefollowIdcontentparentIduserInfocreateTimeupdateTime
1concat1评论0xxxxxxxxx
2concat1回复评论1xxxxxxxxx

接口实现及对获取的评论数据进行数组转树形结构

  • 根据followId在评论表中进行查全部数据
    • 前端获取当前followId并传递参数followId给后端进行查询
  • comments.controller.ts
// 根据跟进id查评论
  @Get('/getComments')
  async getComments(@Request() req, @Response() res) {
    const followId = req.body.followId;
    try {
      const data = await this.commentsService.getComments({
        followId
      });
      return res.success(data);
    } catch (e) {
      this.logger.error(`获取评论数据失败: ${e.message}`);
      return res.error('获取评论数据失败');
    }
  }
  • comments.service.ts
// 将数组转化为树形结构的方法
const arrayToTree = (items) => {
    const result = []; // 结果集
    const itemMap = {};

    // 先转成map存储
    for (const item of items) {
      itemMap[item.id] = { ...item };
      itemMap[item.id]._doc.children = [];

      const id = item.id;
      const parentId = item.parentId;
      const treeItem = itemMap[id]._doc;

      // 第一层
      if (parentId == 0) {
        result.push(treeItem);
      } else {
        if (!itemMap[parentId]) {
          itemMap[parentId]._doc.children = [];
        }
        itemMap[parentId]._doc.children.push(treeItem);
      }
    }

    return result;
  };
  
 // 跟进项目id查接触信息
  async getComments({ followId }) {
    const comments = await this.CommentsModel.find({
      followId
    });
    const commentsList = this.arrayToTree(comments);

    return {
      total: commentsList.length,
      record: commentsList
    };
  }
  • 在看方法时,会注意到itemMap[item.id]._doc.children = [],这个_doc是什么呢?为什么要这样做呢?接着往下看:

    • 将根据followId从数据库中获取的数据转化为树形结构时,我们在给查询结果直接添加了额外的children属性,那就导致了在返回的数据中出现了一些“多余”的字符(那“多余”字符怎么来的,稍后解释),我们可以看到,在_doc中存的才是我们需要的数据
    • 在这先了解一下Object.getOwnPropertyDescriptors(),这个方法返回一个对象,所有原来的对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。
    • Object.getOwnPropertyDescriptors(comments[0])返回的结果如下所示,代码结构和postman返回的一致,由此可见上文提到“多余”字符是这么来的,返回的都是属性描述符
  • postman返回结果: image.png

  • 使用Object.getOwnPropertyDescriptors()返回结果:

 {
  '$__': {
    value: InternalCache { activePaths: [StateMachine], skipId: true },
    writable: true,
    enumerable: true,
    configurable: true
  },
  '$isNew': {
    value: false,
    writable: true,
    enumerable: true,
    configurable: true
  },
  _doc: {
    value: {
      _id: new ObjectId("63ddf2db5b3b37578a4077ab"),
      userInfo: [Object],
      salesProject: '63c4ea6457cfeec648276086',
      followType: 'concat',
      followId: '63c4ea8257cfeec6482760c8',
      content: 'ceshipinglun',
      parentId: '0',
      createTime: 2023-02-04T05:53:31.188Z,
      updateTime: 2023-02-04T05:53:31.188Z,
      __v: 0
    },
    writable: true,
    enumerable: true,
    configurable: true
  }
}
  • find() 或者 findOne() 得到的结果,是有一层封装的,在获取这个结果的时候,会自动解构,但是在我们自己在这个返回的数据中添加属性的时候,不会自动解构。主要数据就存储在_doc

发布评论

  • 前端传递评论数据至后端,后端需要判断是否具有用户信息
// 发布评论
  @Post('/publishComment')
  async publishComment(@Request() req, @Response() res) {
    try {
      const data = req.body as Comments;
      if (!data.userInfo.user_id || !data.userInfo.user_name) {
        return res.error('无效的支持人员');
      }

      const result = await this.commentsService.publishComment(data);
      if (!result) {
        return res.error('评论不存在');
      }

      return res.success(result);
    } catch (e) {
      if (e.message.includes('duplicate key error')) {
        return res.error('评论已存在');
      }
      this.logger.error(`发布评论失败: ${e.message}`);
      return res.error(e.message || '发布评论失败');
    }
  }

删除评论

// 删除评论
  @Post('/deleteComment')
  async deleteComment(@Body('id') id, @Response() res) {
    try {
      await this.commentsService.deleteComment(id);
      return res.success();
    } catch (err) {
      this.logger.error(`删除评论数据失败: ${err.stack}`);
      return res.error(err.message || '删除评论数据失败');
    }
  }