【NestJs】使用Sse 全局单向消息提醒发送和物流信息对接

此文来讲讲如何使用Sse全局单向消息提醒发送和物流信息对接。

一、Sse(Server-Send Events)

它是H5的api,用于在服务器和客户端之间实时推送数据流。Sse可以用于实现实时通知、实时聊天、实时数据更新和实时监控等功能,并且可自动链接,和Websocket不同的是,它是单向的,只允许服务端向客户端实时推送,客户端向服务端发送不了数据消息。

接下来讲不同文件里实现Sse。

1、首先我们现在在Nest的项目里创建公共文件,写入项目注册Sse的node事件,来创建和调用Sse,代码如下:

arduino 复制代码
import { EventEmitter } from 'node:events';

export class sseEvent {
  private static sseEvent = new EventEmitter();

  static getEvent () {
    return this.sseEvent
  }
}

2、在业务方法里注册并定义node事件,代码如下:

javascript 复制代码
import { sseEvent } from 'src/common/sseEvent/utils';

sseEvent.getEvent().emit('send', data);

真实业务代码图:先去调用查业务相关表的方法,当然,有些业务场景是可以拿redis的,具体情况根据业务来。

3、在控制层创建Sse接口并触发刚才已经注册好的node 事件,代码如下:

typescript 复制代码
import { Observable, interval, map } from 'rxjs';
import { sseEvent } from 'src/common/sseEvent/utils';

export interface MessageEvent {
  data: object;
}

/** Sse 查询消息提醒数量,可自定义为全局 */
@Sse('clientWeb/getMessageNum')
    async sse(): Promise<Observable<MessageEvent>> {
    return new Observable<any>((observer) => {
      /** 此处触发刚才注册好的node事件获取数据 */
      sseEvent.getEvent().on('send', (data: any) => {
        observer.next({ data: data });
      });
    });
}

真实业务代码图:

那为啥不直接写个查业务相关表的方法在上面调用呢?因为消息提醒是某一个业务场景触发的,打个比方,此时A员工填写好请假条,在create请假条的方法里定义node事件,及时触发Sse发送到审批人手里,总不能去轮询查对应的业务表来触发Sse发送吧。

4、前端代码如下:这里用的Vue3和Ts

ini 复制代码
const isNoReadNumber = ref<Number>(); // 未读或者未审批的消息
const eventSource = new EventSource(`${api}/api/public/clientWeb/getMessageNum`);
eventSource.onmessage = ({ data }) => {
  isNoReadNumber.value = JSON.parse(data)?.isNoReadNumber
}

当然我们可以获取登录用户的角色来获取对应的消息提醒,用户的信息存在redis,放在全局模块守卫,前端在headers里传过来的token去解析存在redis里对应的角色,来获取某角色的消息。前提是创建消息的时候,它得赋予某个角色/某个岗位/某个部门,可用关联表去关联对应的请假条id、角色id/岗位id/部门id。

二、物流信息对接

这里对接的第三方平台是快递鸟,可去百度查询,这里就不贴地址以防说我是打广告的哈。

1、先创建好方法,创建方法前先安装依赖,注意:md5-hex不同的依赖版本对应不同的node版本,本人node版本的是16.18.0,所以安装的是3.0.1版本

安装依赖

perl 复制代码
npm install md5-hex@3.0.1
npm install querystring

安装好后写一个全局的方法可供其它方法调用,代码如下:

typescript 复制代码
import { Injectable } from '@nestjs/common'
import querystring from 'querystring'
import { HttpService } from '@nestjs/axios'
import { lastValueFrom } from 'rxjs'
import md5Hex from 'md5-hex'
import { GetSettingService } from 'src/system/setting/get.setting.service'

@Injectable()
export class LogisticsService {
  constructor(
    private readonly getSettingService: GetSettingService,
    private readonly http: HttpService,
  ) {}
  
  // 获取系统设置,(先获取系统设置的快递配置信息)
  async getSettingInfo() {
    let settingInfo = await this.getSettingService.findOneInfo()
    return settingInfo
  }

  // 查询物流
  async sendLogistics() {
    const { logisticsUserId, logisticsKey } = await this.getSettingInfo();
    if (!logisticsUserId) return false; // 没填写用户id
    if (!logisticsKey) return false; // 没填写api的key
    const Url = 'https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx';

    //请求接口指令
    const RequestType  = '1002';
    // 组装应用级参数
    const RequestData = {
      'CustomerName': '',
      'OrderCode': '',
      'ShipperCode': 'STO', // 举个例子,申通快递
      'LogisticCode': '7732684xxxxx', // 物流单号,可动态传进来,这里举例就写死了
    };
    const DataSign = Buffer.from(md5Hex(JSON.stringify(RequestData)+logisticsKey)).toString('base64')
    const reqParams = {
      RequestType,
      EBusinessID: logisticsUserId,
      DataSign,
      RequestData: JSON.stringify(RequestData),
      DataType: 2
    }
    const headers = {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
    const { data } = await lastValueFrom(this.http.post(Url, querystring.stringify(reqParams), { headers }))
    return data;
  }
}

2、那我们写个接口来测试测试,代码如下:

Service层

typescript 复制代码
import { LogisticsService } from 'src/common/logistics/logistics.service'
// 后端接口返回封装,可自定义
import { ResultData } from '../../common/utils/result'

@Injectable()
export class SettingService {
  constructor(
      @InjectEntityManager()
      private readonly logisticsService: LogisticsService,
  ) {}
  
/** 测试查询物流 */
  async sendLogistics() {
    const info = await this.logisticsService.sendLogistics();

    if (!info) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '查询物流信息失败,请查看对应的配置信息是否填写')
    return ResultData.ok(info);
  }
  
 }

Controller层

less 复制代码
@ApiTags('系统设置模块')
@Controller('setting')
    export class SettingController {
      constructor(private readonly settingService: SettingService) {}
      
      @Get('/sendLogistics')
      @ApiOperation({ summary: '测试查询物流' })
      @ApiResult()
      async sendLogistics() {
        return await this.settingService.sendLogistics();
      }
}

来看看前端测试的结果,返回正常结果,就说明成功了。

这就是使用Sse 全局单向消息提醒发送和物流信息对接

以上代码都已经开源了,链接是:gitee.com/wx375149069... 此Nest开源项目包含以下丰富的功能,麻烦大家点个Start

相关推荐
吕彬-前端34 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱37 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
许野平44 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端