当前位置: 首页 > news >正文

TypeScript实战篇 - TS实战: 服务层开发 - 完整的聊天服务

目录

huatian-svc/src/main.ts

huatian-svc/src/context/ChatContext.ts

huatian-svc/src/ChatSession.ts

huatian-svc/src/service/ChatIDService.ts

huatian-svc/src/dao/DB.ts

huatian-svc/src/dao/Dao.ts

huatian-svc/src/dao/create_db.ts


huatian-svc/src/main.ts

import express, {NextFunction,Request,Response
} from "express";
// 安装cookie-parser: npm add cookie-parser
import cookieParser from "cookie-parser";
import { AccountContext } from "./context/AccountContext";
import { Token } from "./dao/Token";
import { ChatContext } from "./context/ChatContext";
import { Message } from "@huatian/model";
// 创建一个服务
const app = express()
// cookie功能,整理好cookie,req.cookies
app.use(cookieParser())
// 登录后的数据类
type LoggedInRequest = Request & { uid: number }
async function sendStdResponse<T>(res: Response, f: T)
async function sendStdResponse(res: Response, f: Promise<any>)
async function sendStdResponse(res: Response, f: () => Promise<any>)
async function sendStdResponse(res: Response, f: any) {try {let data = typeof f === 'function' ? f() : fif (data instanceof Promise) {data = await data}res.send({success: true,data})} catch (ex: any) {console.error(ex)res.status(500).send({success: false,message: ex.toString()})}
}
// token
async function token(req: Request & { uid: number },// req 是带着uid的res: Response,next: NextFunction
) {// tokenHash~=sessionidconst tokenHash = req.cookies["x-token"] as stringconst token = Token.getInstance()const tokenObject = token.getToken(tokenHash)if (tokenObject === null) {res.status(401).send({success: false})return}req.uid = tokenObject.uidnext()
}
app.get('/foo', token, (req: Request & { uid: number }, res) => {res.send(req.uid + '-ok')
})
// 登录接口,json传参【express.json()解析json】
app.post('/token', express.json(), async (req, res) => {const { uname, pwd } = req.bodyconst account = AccountContext.getInstance()const user = await account.veritfy(uname, pwd)console.log(uname, pwd, user.getId())const token = Token.getInstance()// 刷新tokenconst tokenObject = token.refreshToken(user.getId())res.cookie("x-token", tokenObject.token)sendStdResponse(res, "ok")
})
// ==================聊天接口相关==============================
// 名词命名接口
app.post("/message", token, express.json(), async (req: LoggedInRequest, res) => {const uid = req.uid// 先拿到场景const chatContext = ChatContext.getInstance()// 返回,最后发送的信息sendStdResponse(res, async () => {return await chatContext.send(uid, req.body as Message)})
})
// 请求聊天内容
app.get('/message', token, async (req: LoggedInRequest, res) => {const uid = req.uid// 最后一条id,不传就读所有const lastId = parseInt(req.query.last_id as string) || 0console.log({ uid, lastId })const chatContext = ChatContext.getInstance()sendStdResponse(res, () => {return chatContext.read(uid, lastId)})
})
// =================================================
// 监听一个6001端口号的服务
app.listen(6001, () => {console.log('listen at 6001')
})

huatian-svc/src/context/ChatContext.ts

// 聊天场景
import { Message } from "@huatian/model"
import { UserRepository } from "../repo/UserRepository"
import { ChatIDService } from "../service/ChatIDService"
// 服务当中的聊天场景【处理网络,处理存储,不处理模型】
export class ChatContext {// 单例private static inst = new ChatContext()private repo = UserRepository.getInstance()// 获取单例public static getInstance() {return ChatContext.inst}// 发送public async send(uid: number, msg: Message) {// Message需要一个服务 生成idconst sentMsg = { ...msg }const toReceiveMsg = { ...msg }sentMsg.id = await ChatIDService.getInstance().getId()toReceiveMsg.id = await ChatIDService.getInstance().getId()msg.from = uid // 覆盖一下fromconst from = this.repo.getUser(msg.from)const to = this.repo.getUser(msg.to)const session = from.chat().createChatSession(to)// 用session的聊天方法session.chat(sentMsg, toReceiveMsg)// 告诉客户端最新发送的消息是哪条return sentMsg.id}public read(uid: number, lastId: number) {// 用户仓库中拿出用户const user = this.repo.getUser(uid)// 用lastId拿所有未读 消息return user.chat().unReadMessage(lastId)}
}

huatian-svc/src/ChatSession.ts

import { Message } from "./Message";
import { User } from "./User";
// 聊天会话模型
export class ChatSession {private from: Userprivate to: Userpublic constructor(from: User, to: User) {this.from = fromthis.to = to}// 此处修改为收发个一条消息public chat(sentMsg: Message, roReceiveMsg: Message) { // 会话中可以聊天this.from.chat().send(sentMsg) // 用户发送一条消息this.to.chat().receive(roReceiveMsg) // 用户接收一条消息}
}

huatian-svc/src/service/ChatIDService.ts

// 生成消息id的服务
import { ChatIDSetDao } from "../dao/Dao"
import { DB } from "../dao/DB"
// Message生成id的服务
const STEP = 100000
export class ChatIDService {// 初始化时间端的单例写法private static inst: ChatIDService = new ChatIDService()private id_base: number = 0private id_start: number = 0public static getInstance(): ChatIDService {return ChatIDService.inst}/*** 每次拿到的是一个集合的ID* 比如0~99999*/private async requestIdSet() {if (this.id_base >= this.id_start &&this.id_base < this.id_start + STEP) {return}const sequelize = DB.getSequelize()const transaction = await sequelize.transaction()try {// 0---->100000// 0----->100000// 上锁之后要等前一条完成,才能继续执行这一条// 拿表最后一条数据const lastRecord = await ChatIDSetDao.findOne({order: [["id", "desc"]], // 根据id倒序,就是最后一条记录lock: transaction.LOCK.UPDATE // 锁住,不能同时访问})const startNumber = lastRecord? lastRecord.getDataValue("start") + 100000: 0await ChatIDSetDao.create({app: "test", // 临时start: startNumber})// 恢复一下值this.id_start = startNumberthis.id_base = startNumber} catch (ex) {console.error(ex)transaction.rollback()// 回滚}}public async getId() {await this.requestIdSet()return this.id_base++}
}

huatian-svc/src/dao/DB.ts

// 表,负责连接的
// node端比较好用的数据工具
import path from "path";
import { Sequelize } from "sequelize";
export class DB {static sequelize: Sequelize// 初始化时间较长的单例写法static getSequelize() {if (!DB.sequelize) {DB.sequelize = new Sequelize({dialect: "sqlite", // 数据库类型,上线后替换成mysqlstorage: path.resolve(__dirname, "mydb.db")})}return DB.sequelize}
}

huatian-svc/src/dao/Dao.ts

// 创建一张表
import { Model, Optional, DataTypes } from "sequelize";
import { DB } from "./DB";
// 聊天集合的一个分类
interface ChatIDSetAttributes {id: number,app: string,// app,集群,应用名称start: number
}
export class ChatIDSetDao extends Model<ChatIDSetAttributes,Optional<ChatIDSetAttributes, "id">
>{ }
ChatIDSetDao.init({id: {type: DataTypes.BIGINT, // INTEGER;数量很多的话用,BIGINT,几十亿数据 autoIncrement: true,// 自增字段primaryKey: true// 主键},app: {type: DataTypes.STRING(20),allowNull: false // 不允许null},start: {type: DataTypes.BIGINT,allowNull: false // 不允许null}}, {sequelize: DB.getSequelize(),tableName: "id_set" // 表的名称
}
)

huatian-svc/src/dao/create_db.ts

// 初始化表的脚本
import { ChatIDSetDao } from "./Dao";
//sync自动创建表,force: true存在也创建
ChatIDSetDao.sync({ force: true })

相关文章:

TypeScript实战篇 - TS实战: 服务层开发 - 完整的聊天服务

目录 huatian-svc/src/main.ts huatian-svc/src/context/ChatContext.ts huatian-svc/src/ChatSession.ts huatian-svc/src/service/ChatIDService.ts huatian-svc/src/dao/DB.ts huatian-svc/src/dao/Dao.ts huatian-svc/src/dao/create_db.ts huatian-svc/src/main.ts…...

【雕爷学编程】MicroPython动手做(32)——物联网之MQTT

MQTT &#xff08;Message Queuing Telemetry Transport&#xff09;消息队列遥测传输协议&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的"轻量级"通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff0c;由IBM在1999年发布。M…...

操作系统专栏4-网络专题from小林coding

网络专题 文件传输mmapwritesend file大文件传输过程 文件传输 传统的文件传输过程 在这个过程中发生了4次用户态与内核态之间的切换,4次数据拷贝分别是 read系统调用陷入内核,read完成返回write调用陷入内核,write返回 4次数据拷贝分别是 磁盘->内核缓冲区->用户缓冲…...

《C和指针》(6)指针

1、内存和地址 计算机的内存是由数以亿万计的位&#xff08;bit&#xff09;组成&#xff0c;每一个位可以容纳值0、1值。由于一个位所能表示的值的范围太有限&#xff0c;所以单独的位用处不大。通常许多为合成一组作为一个单位&#xff0c;这样就可以存储范围较大的值。下图…...

零基础强化学习入门分享

&#xff08;一&#xff09;前言&#xff1a;强化学习入门顺序。 以前主要学习硬件PCB单片机等知识&#xff0c;后来接触的项目也大多与电气相关&#xff0c;从一窍不通到稍微找到点门道&#xff0c;中间走过不少弯路&#xff0c;误打误撞中&#xff0c;也留下了一些经验。 我的…...

QT快捷键

--------------------------------------------------- --------------------------------------------------- QT断点调试 Ctrl B 编译程序 F5 调试运行程序 F10 单步调试 F11 进入函数调试 --------------------------------------------------- -----------------------…...

LabVIEW 开发在不确定路况下自动速度辅助系统

LabVIEW 开发在不确定路况下自动速度辅助系统 智能驾驶辅助系统是汽车行业最先进的升级和尖端技术&#xff0c;智能交通系统依靠智能驾驶辅助系统在公共交通部门工作。该智能驾驶辅助系统技术包括自适应巡航控制&#xff0c;防抱死制动系统&#xff0c;安全气囊展开&#xff0…...

《面试1v1》ElasticSearch 和 Lucene

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…...

P5727 【深基5.例3】冰雹猜想

【深基5.例3】冰雹猜想 题目描述 给出一个正整数 n n n&#xff0c;然后对这个数字一直进行下面的操作&#xff1a;如果这个数字是奇数&#xff0c;那么将其乘 3 3 3 再加 1 1 1&#xff0c;否则除以 2 2 2。经过若干次循环后&#xff0c;最终都会回到 1 1 1。经过验证很…...

ConcurrentHashMap1.7 源码浅析

分析过HashMap的1.7的版本的结构&#xff0c;但是HashMap是线程不安全的&#xff0c;多线程触发扩容还会发生死循环问题&#xff0c;那么ConcurrentHashMap 就是解决这个问题的&#xff0c;这是一个线程安全的Map&#xff0c;那么对应的内部实现是怎么样的&#xff0c;简单分析…...

跨境电商时代的安全护航

随着跨境电商业务的蓬勃发展&#xff0c;网络安全问题日益突出。为了保障个人信息的安全和商业竞争的公平性&#xff0c;防关联浏览器和多开浏览器的需求日益增长。本文将为您介绍隐擎fox指纹浏览器&#xff0c;探讨其在跨境电商时代的重要作用&#xff0c;以及如何通过该浏览器…...

JavaScript Es6 _1 笔记

JavaScript Es6 _1 笔记 学习作用域、变量提升、闭包等语言特征&#xff0c;加深对 JavaScript 的理解&#xff0c;掌握变量赋值、函数声明的简洁语法&#xff0c;降低代码的冗余度。 理解作用域对程序执行的影响能够分析程序执行的作用域范围理解闭包本质&#xff0c;利用闭包…...

结构体和 Json 相互转换(序列化反序列化)

关于 JSON 数据 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也 易于机器解析和生成。RESTfull Api 接口中返回的数据都是 json 数据。 Json 的基本格式如下&#xff1a; { "a": "Hello", "b": "…...

【力扣刷题 | 第二十四天】

目录 前言&#xff1a; 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 总结 前言&#xff1a; 今晚我们爆刷动态规划类型的题目。 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这…...

PyTorch使用(一)(常用库)

1.各大模型库 hub&#xff1a;简单来说就是专门为PyTorch集成的算法模型库 网站&#xff1a;GitHub - pytorch/hub: Submission to https://pytorch.org/hub/ Model Zoo&#xff1a;这个平台上提供预训练模型&#xff0c;在每个模型上&#xff0c;会标注出这个模型在GitHub的标…...

React ~ React Router 6

React Router 6 VS React Router 5.x 内置组件的变化; 移除<Switch /> , 新增<Routes />语法的变化; component { About } 变为 element { <About /> }新增多个hook官方明确推荐函数式组件了! 一级路由(变化) 安装路由 npm i react-router-dom (默认是最…...

【LeetCode每日一题】——304.二维区域和检索-矩阵不可变

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 中等 三【题目编号】 304.二维区域和检索-矩阵不可变 四【题目描述】 …...

硬件串口通信协议学习(UART、IIC、SPI、CAN)

0.前言 学习资料&#xff1a;江协科技的个人空间-江协科技个人主页-哔哩哔哩视频 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发 全双工&#xff1a;通信…...

第一章-JavaScript基础进阶part2:事件

文章目录 概念一、注册事件&#xff08;绑定事件&#xff09;1.1 addEventListener事件监听 二、删除事件&#xff08;解绑&#xff09;三、DOM事件流四、事件对象event4.1 e.target与this与e.currentTarget的区别4.2 事件对象的常见属性 五、阻止事件默认行为及冒泡六、事件委…...

如何优雅的使用后端接口

优雅的后端接口 一个后端接口大致分为四个部分&#xff1a;接口地址(url)、接口请求方式(get、post等)、请求数据(request)、响 应数据(response)。 一、URL & Method Rest 设计风格 》 Restful API 简单理解&#xff1a; URI 是用来唯一标志一个互联网资源&#xff1b;Me…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...