此文来讲讲如何使用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