当前位置: 首页 > 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…...

EasyVtuber未来发展规划:AI虚拟主播的技术趋势与创新方向

EasyVtuber未来发展规划&#xff1a;AI虚拟主播的技术趋势与创新方向 【免费下载链接】EasyVtuber Based on Talking-head-anime 3, works like Vtube Studio. 项目地址: https://gitcode.com/gh_mirrors/ea/EasyVtuber EasyVtuber是一款基于Talking-head-anime 3技术开…...

软件体验优化中的A-B测试设计

在软件体验优化中&#xff0c;A/B测试是一种科学且高效的方法&#xff0c;通过对比不同版本的设计或功能&#xff0c;帮助团队找到最优解决方案。无论是电商平台的按钮颜色&#xff0c;还是社交应用的推送策略&#xff0c;A/B测试都能以数据驱动决策&#xff0c;显著提升用户满…...

RePKG:Wallpaper Engine资源处理的终极指南

RePKG&#xff1a;Wallpaper Engine资源处理的终极指南 【免费下载链接】repkg Wallpaper engine PKG extractor/TEX to image converter 项目地址: https://gitcode.com/gh_mirrors/re/repkg RePKG是一款强大的Wallpaper Engine资源处理工具&#xff0c;能够轻松提取PK…...

实测Phi-4-mini-reasoning:让AI帮你写作业,数学逻辑题轻松应对

实测Phi-4-mini-reasoning&#xff1a;让AI帮你写作业&#xff0c;数学逻辑题轻松应对 1. 引言&#xff1a;你的智能作业助手来了 作为一名学生&#xff0c;你是否经常被数学作业和逻辑推理题困扰&#xff1f;或者作为家长&#xff0c;你是否为辅导孩子作业而头疼&#xff1f…...

收藏!小白程序员轻松入门AI Agent,解锁大模型“手脚”与“感官”的奥秘

本文深入浅出地解释了AI Agent的概念和重要性&#xff0c;将AI Agent比作“全能的超级实习生”&#xff0c;拥有LLM的大脑进行决策&#xff0c;同时具备执行任务的能力。文章详细介绍了AI Agent的组成部分&#xff0c;包括大脑&#xff08;LLM&#xff09;、规划、记忆和工具&a…...

【从零开始学Java | 第三十二篇】方法引用(Method Reference)

目录 前言 一、什么是方法引用&#xff1f; 1.引例 2.方法引用的语法 二、方法引用的分类 1.引用静态方法 2.引用成员方法 ①其他类&#xff1a;其他类对象::方法名 3.引用构造方法 4.使用类名引用成员方法 5.引用数组的构造方法 总结 前言 在 Java 8 引入 Lambda 表…...

郭老师-如何判断一个人有没有领导力

如何判断一个人有没有领导力 ——从魅力到思想力的四重修炼“真正的领导力&#xff0c; 不在于个人魅力&#xff0c; 而在于—— 带领团队做出成绩&#xff0c; 赢得信任&#xff0c; 并拥有清晰的战略思想。”&#x1f33f; 领导力的核心&#xff0c; 是绩效导向&#xff0c; …...

嵌入式开发实战:为Android设备交叉编译mmc-utils工具集

1. 为什么需要交叉编译mmc-utils 在嵌入式开发中&#xff0c;我们经常需要与eMMC存储设备打交道。mmc-utils就是这样一套专门用于管理eMMC存储设备的实用工具集&#xff0c;它提供了读取extcsd、修改分区配置、设置写保护等强大功能。但问题来了——Android设备通常没有预装这些…...

零成本玩转谷歌Gemini模型:从入门到实战的完整指南

1. 为什么选择谷歌Gemini模型&#xff1f; 最近大模型领域真是热闹非凡&#xff0c;各家厂商都在不断推陈出新。作为一名长期关注AI发展的技术爱好者&#xff0c;我实测过多款主流大模型&#xff0c;包括GPT-4o、Claude 3.5 Sonnet等。但不得不说&#xff0c;谷歌最新推出的Gem…...

收藏!小白程序员必看:如何低成本精准选型大模型,避免花冤枉钱?

选择大模型的关键在于匹配自身需求&#xff0c;需结合成本预算、技术能力、业务场景、合规要求四大维度进行筛选。文章提出先明确核心需求&#xff0c;拒绝参数崇拜&#xff0c;再根据个人开发者、小团队、垂直领域用户等不同类型给出具体选型建议。同时&#xff0c;文章还提醒…...