feat: 初始化短信网关核心

This commit is contained in:
2026-06-24 22:24:43 +08:00
commit db3c32a871
12 changed files with 4060 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
import EventEmitter from 'events';
import logger from './logger.js';
class ConcatManager extends EventEmitter {
constructor() {
super();
this.buffer = [];
this.maxSlots = 5;
this.maxParts = 10;
this.timeoutMs = 30000; // 30秒超时
}
/**
* 查找或创建长短信槽位
*/
findOrCreateSlot(refNumber, sender, totalParts) {
// 先查找是否已存在
let slot = this.buffer.find(s =>
s.inUse && s.refNumber === refNumber && s.sender === sender
);
if (slot) {
return slot;
}
// 查找空闲槽位
slot = this.buffer.find(s => !s.inUse);
if (slot) {
this.initSlot(slot, refNumber, sender, totalParts);
return slot;
}
// 没有空闲槽位,查找最老的槽位覆盖
if (this.buffer.length < this.maxSlots) {
slot = {
inUse: false,
parts: []
};
this.buffer.push(slot);
} else {
slot = this.buffer.reduce((oldest, current) =>
current.firstPartTime < oldest.firstPartTime ? current : oldest
);
logger.warn('长短信缓存已满,覆盖最老的槽位');
}
this.initSlot(slot, refNumber, sender, totalParts);
return slot;
}
/**
* 初始化槽位
*/
initSlot(slot, refNumber, sender, totalParts) {
slot.inUse = true;
slot.refNumber = refNumber;
slot.sender = sender;
slot.totalParts = totalParts;
slot.receivedParts = 0;
slot.firstPartTime = Date.now();
slot.timestamp = null;
slot.parts = new Array(totalParts).fill(null);
}
/**
* 添加短信分段
*/
addPart(refNumber, sender, partNumber, totalParts, text, timestamp) {
logger.info(`收到长短信分段 ${partNumber}/${totalParts}, 参考号: ${refNumber}`);
const slot = this.findOrCreateSlot(refNumber, sender, totalParts);
const partIndex = partNumber - 1; // partNumber从1开始,数组从0开始
if (partIndex >= 0 && partIndex < this.maxParts) {
if (!slot.parts[partIndex]) {
slot.parts[partIndex] = text;
slot.receivedParts++;
// 保存第一个收到的分段的时间戳
if (slot.receivedParts === 1) {
slot.timestamp = timestamp;
}
logger.info(`已缓存分段 ${partNumber},当前已收到 ${slot.receivedParts}/${totalParts}`);
// 检查是否已收齐
if (slot.receivedParts >= totalParts) {
logger.info('✅ 长短信已收齐,开始合并转发');
this.assembleAndEmit(slot);
}
} else {
logger.warn(`⚠️ 分段 ${partNumber} 已存在,跳过`);
}
}
}
/**
* 合并并发射完整短信
*/
assembleAndEmit(slot) {
let fullText = '';
for (let i = 0; i < slot.totalParts; i++) {
if (slot.parts[i]) {
fullText += slot.parts[i];
} else {
fullText += `[缺失分段${i + 1}]`;
}
}
this.emit('complete', {
sender: slot.sender,
text: fullText,
timestamp: slot.timestamp
});
// 清空槽位
this.clearSlot(slot);
}
/**
* 清空槽位
*/
clearSlot(slot) {
slot.inUse = false;
slot.parts = [];
slot.receivedParts = 0;
}
/**
* 检查超时
*/
checkTimeout() {
const now = Date.now();
this.buffer.forEach(slot => {
if (slot.inUse && (now - slot.firstPartTime) >= this.timeoutMs) {
logger.warn(`⏰ 长短信超时,强制转发不完整消息`);
logger.warn(` 参考号: ${slot.refNumber}, 已收到: ${slot.receivedParts}/${slot.totalParts}`);
this.assembleAndEmit(slot);
}
});
}
/**
* 启动超时检查定时器
*/
startTimeoutChecker() {
this.timeoutChecker = setInterval(() => {
this.checkTimeout();
}, 5000); // 每5秒检查一次
logger.info('长短信超时检查器已启动');
}
/**
* 停止超时检查定时器
*/
stopTimeoutChecker() {
if (this.timeoutChecker) {
clearInterval(this.timeoutChecker);
this.timeoutChecker = null;
logger.info('长短信超时检查器已停止');
}
}
}
export default ConcatManager;