feat: 按 SIM ICCID 隔离短信
This commit is contained in:
+1
-1
@@ -166,7 +166,7 @@
|
||||
<div class="inbox-panel">
|
||||
<div class="inbox-meta">
|
||||
<span id="messageCount">最近 0 条</span>
|
||||
<span class="inbox-meta__hint">新短信会自动出现在这里</span>
|
||||
<span class="inbox-meta__hint" id="messageScope">新短信会自动出现在这里</span>
|
||||
</div>
|
||||
<div class="message-list" id="messageList" aria-live="polite">
|
||||
<p class="empty-state empty-state--light">暂无收到的短信。</p>
|
||||
|
||||
@@ -759,6 +759,26 @@ h3 {
|
||||
letter-spacing: -0.2px;
|
||||
}
|
||||
|
||||
.message-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.message-sim {
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-pill);
|
||||
background: var(--surface-soft);
|
||||
color: var(--muted);
|
||||
padding: 3px 9px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.message-item__header time {
|
||||
color: var(--muted);
|
||||
font-family: var(--font-sans);
|
||||
|
||||
+20
-4
@@ -51,6 +51,7 @@ const elements = {
|
||||
smsStatus: document.querySelector('#smsStatus'),
|
||||
messagesButton: document.querySelector('#messagesButton'),
|
||||
messageCount: document.querySelector('#messageCount'),
|
||||
messageScope: document.querySelector('#messageScope'),
|
||||
messageList: document.querySelector('#messageList'),
|
||||
atForm: document.querySelector('#atForm'),
|
||||
atOutput: document.querySelector('#atOutput'),
|
||||
@@ -311,9 +312,16 @@ function formatDateTime(value) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderMessages(messages) {
|
||||
function renderMessages(messages, meta = {}) {
|
||||
elements.messageList.replaceChildren();
|
||||
setText(elements.messageCount, `最近 ${messages.length} 条`);
|
||||
const currentSimLabel = meta.currentSim?.simLabel || '当前SIM未知';
|
||||
const countLabel = meta.partitionBySim ? `${currentSimLabel} 最近 ${messages.length} 条` : `全部SIM 最近 ${messages.length} 条`;
|
||||
const scopeLabel = meta.partitionBySim
|
||||
? (meta.currentSimKnown ? '仅显示当前SIM收到的短信' : '未识别当前SIM,收件箱已隐藏')
|
||||
: 'SIM隔离未启用';
|
||||
|
||||
setText(elements.messageCount, countLabel);
|
||||
setText(elements.messageScope, scopeLabel);
|
||||
|
||||
if (!messages.length) {
|
||||
const empty = document.createElement('p');
|
||||
@@ -345,8 +353,16 @@ function renderMessages(messages) {
|
||||
text.className = 'message-text';
|
||||
text.textContent = message.text || '';
|
||||
|
||||
const metaRow = document.createElement('div');
|
||||
metaRow.className = 'message-meta';
|
||||
|
||||
const sim = document.createElement('span');
|
||||
sim.className = 'message-sim';
|
||||
sim.textContent = message.simLabel || '未知SIM';
|
||||
|
||||
metaRow.append(sim);
|
||||
header.append(sender, status, time);
|
||||
item.append(header, text);
|
||||
item.append(header, metaRow, text);
|
||||
fragment.append(item);
|
||||
});
|
||||
|
||||
@@ -664,7 +680,7 @@ async function refreshLogs() {
|
||||
|
||||
async function refreshMessages() {
|
||||
const messages = await requestJSON('/api/sms/received?limit=50');
|
||||
renderMessages(messages.data || []);
|
||||
renderMessages(messages.data || [], messages.meta || {});
|
||||
}
|
||||
|
||||
async function toggleMobileData() {
|
||||
|
||||
+14
-4
@@ -414,12 +414,18 @@ class APIServer {
|
||||
});
|
||||
|
||||
// 查询收到的短信
|
||||
this.app.get('/api/sms/received', (req, res) => {
|
||||
this.app.get('/api/sms/received', async (req, res) => {
|
||||
try {
|
||||
const messages = this.smsProcessor.getReceivedMessages(req.query.limit);
|
||||
const currentSimIdentity = await this.smsProcessor.getCurrentSimIdentity();
|
||||
const result = this.smsProcessor.listReceivedMessages(req.query.limit, {
|
||||
currentSimIdentity,
|
||||
scope: req.query.scope
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: messages
|
||||
data: result.messages,
|
||||
meta: result.meta
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error('查询收到短信失败:', err);
|
||||
@@ -450,12 +456,16 @@ class APIServer {
|
||||
// 查询模组信息
|
||||
this.app.get('/api/modem/info', async (req, res) => {
|
||||
try {
|
||||
const iccid = await this.modem.getICCID();
|
||||
const iccid = await this.modem.getICCID({ refresh: true });
|
||||
const simIdentity = this.smsProcessor.setCurrentSimFromICCID(iccid);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
model: this.modem.modelInfo,
|
||||
iccid,
|
||||
simId: simIdentity?.simId || null,
|
||||
simLabel: simIdentity?.simLabel || '未知SIM',
|
||||
ready: this.modem.ready
|
||||
}
|
||||
});
|
||||
|
||||
+15
-8
@@ -13,10 +13,12 @@ class ConcatManager extends EventEmitter {
|
||||
/**
|
||||
* 查找或创建长短信槽位
|
||||
*/
|
||||
findOrCreateSlot(refNumber, sender, totalParts) {
|
||||
findOrCreateSlot(refNumber, sender, totalParts, simIdentity = null) {
|
||||
const simId = simIdentity?.simId || '';
|
||||
|
||||
// 先查找是否已存在
|
||||
let slot = this.buffer.find(s =>
|
||||
s.inUse && s.refNumber === refNumber && s.sender === sender
|
||||
s.inUse && s.refNumber === refNumber && s.sender === sender && s.simId === simId
|
||||
);
|
||||
|
||||
if (slot) {
|
||||
@@ -26,7 +28,7 @@ class ConcatManager extends EventEmitter {
|
||||
// 查找空闲槽位
|
||||
slot = this.buffer.find(s => !s.inUse);
|
||||
if (slot) {
|
||||
this.initSlot(slot, refNumber, sender, totalParts);
|
||||
this.initSlot(slot, refNumber, sender, totalParts, simIdentity);
|
||||
return slot;
|
||||
}
|
||||
|
||||
@@ -44,17 +46,19 @@ class ConcatManager extends EventEmitter {
|
||||
logger.warn('长短信缓存已满,覆盖最老的槽位');
|
||||
}
|
||||
|
||||
this.initSlot(slot, refNumber, sender, totalParts);
|
||||
this.initSlot(slot, refNumber, sender, totalParts, simIdentity);
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化槽位
|
||||
*/
|
||||
initSlot(slot, refNumber, sender, totalParts) {
|
||||
initSlot(slot, refNumber, sender, totalParts, simIdentity = null) {
|
||||
slot.inUse = true;
|
||||
slot.refNumber = refNumber;
|
||||
slot.sender = sender;
|
||||
slot.simId = simIdentity?.simId || '';
|
||||
slot.simIdentity = simIdentity;
|
||||
slot.totalParts = totalParts;
|
||||
slot.receivedParts = 0;
|
||||
slot.firstPartTime = Date.now();
|
||||
@@ -65,10 +69,10 @@ class ConcatManager extends EventEmitter {
|
||||
/**
|
||||
* 添加短信分段
|
||||
*/
|
||||
addPart(refNumber, sender, partNumber, totalParts, text, timestamp) {
|
||||
addPart(refNumber, sender, partNumber, totalParts, text, timestamp, options = {}) {
|
||||
logger.info(`收到长短信分段 ${partNumber}/${totalParts}, 参考号: ${refNumber}`);
|
||||
|
||||
const slot = this.findOrCreateSlot(refNumber, sender, totalParts);
|
||||
const slot = this.findOrCreateSlot(refNumber, sender, totalParts, options.simIdentity);
|
||||
const partIndex = partNumber - 1; // partNumber从1开始,数组从0开始
|
||||
|
||||
if (partIndex >= 0 && partIndex < this.maxParts) {
|
||||
@@ -110,7 +114,8 @@ class ConcatManager extends EventEmitter {
|
||||
this.emit('complete', {
|
||||
sender: slot.sender,
|
||||
text: fullText,
|
||||
timestamp: slot.timestamp
|
||||
timestamp: slot.timestamp,
|
||||
simIdentity: slot.simIdentity
|
||||
});
|
||||
|
||||
// 清空槽位
|
||||
@@ -124,6 +129,8 @@ class ConcatManager extends EventEmitter {
|
||||
slot.inUse = false;
|
||||
slot.parts = [];
|
||||
slot.receivedParts = 0;
|
||||
slot.simId = '';
|
||||
slot.simIdentity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-1
@@ -60,7 +60,9 @@ async function main() {
|
||||
// 监听长短信合并完成事件
|
||||
concatManager.on('complete', async (sms) => {
|
||||
logger.info('收到长短信合并完成事件');
|
||||
await smsProcessor.processSmsContent(sms.sender, sms.text, sms.timestamp);
|
||||
await smsProcessor.processSmsContent(sms.sender, sms.text, sms.timestamp, {
|
||||
simIdentity: sms.simIdentity
|
||||
});
|
||||
});
|
||||
|
||||
// 监听模组短信事件
|
||||
|
||||
+31
-2
@@ -21,6 +21,8 @@ class ModemManager extends EventEmitter {
|
||||
model: '未知',
|
||||
version: '未知'
|
||||
};
|
||||
this.iccid = null;
|
||||
this.iccidCheckedAt = null;
|
||||
this.mobileData = {
|
||||
cid: this.mobileDataConfig.cid,
|
||||
desiredEnabled: false,
|
||||
@@ -996,6 +998,7 @@ class ModemManager extends EventEmitter {
|
||||
|
||||
// 3. 按ML307A文档先确认SIM卡和协议栈状态
|
||||
await this.waitSIMReady();
|
||||
await this.getICCID({ refresh: true });
|
||||
await this.ensureFullFunctionality();
|
||||
|
||||
// 4. 启动保护:先断开应用层拨号,再等待短信所需的网络注册。
|
||||
@@ -1308,20 +1311,46 @@ class ModemManager extends EventEmitter {
|
||||
/**
|
||||
* 查询ICCID
|
||||
*/
|
||||
async getICCID() {
|
||||
async getICCID(options = {}) {
|
||||
if (!options.refresh && this.iccid) {
|
||||
return this.iccid;
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await this.sendATCommand('AT+CCID', 2000);
|
||||
const match = resp.match(/\+CCID:\s*(\d+)/);
|
||||
if (match) {
|
||||
return match[1];
|
||||
const previousICCID = this.iccid;
|
||||
this.iccid = match[1];
|
||||
this.iccidCheckedAt = new Date().toISOString();
|
||||
if (previousICCID !== this.iccid) {
|
||||
logger.info(`✓ 当前SIM ICCID: ${this.formatICCID(this.iccid)}`);
|
||||
}
|
||||
return this.iccid;
|
||||
}
|
||||
|
||||
this.iccid = null;
|
||||
this.iccidCheckedAt = new Date().toISOString();
|
||||
return null;
|
||||
} catch (err) {
|
||||
if (options.refresh) {
|
||||
this.iccid = null;
|
||||
this.iccidCheckedAt = new Date().toISOString();
|
||||
}
|
||||
logger.error('查询ICCID失败:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
formatICCID(iccid) {
|
||||
const value = String(iccid || '');
|
||||
if (!value) {
|
||||
return '未知';
|
||||
}
|
||||
|
||||
return `...${value.slice(-6)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭串口
|
||||
*/
|
||||
|
||||
+221
-8
@@ -1,4 +1,5 @@
|
||||
import { parse } from 'node-pdu';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
@@ -17,6 +18,12 @@ class SMSProcessor {
|
||||
this.receivedMessageSeq = 0;
|
||||
this.maxReceivedMessages = this.resolveMaxReceivedMessages();
|
||||
this.receivedMessagesFile = this.resolveReceivedMessagesFile();
|
||||
this.partitionMessagesBySim = this.resolvePartitionMessagesBySim();
|
||||
this.allowAllSimMessages = this.resolveAllowAllSimMessages();
|
||||
this.currentSimIdentity = null;
|
||||
this.currentSimCheckedAt = 0;
|
||||
this.currentSimLookup = null;
|
||||
this.simIdentityTtlMs = 5 * 1000;
|
||||
this.loadReceivedMessages();
|
||||
}
|
||||
|
||||
@@ -40,10 +47,12 @@ class SMSProcessor {
|
||||
const text = parsed.data?.getText?.() || '';
|
||||
const part = parsed.data?.parts?.[0];
|
||||
const header = part?.header;
|
||||
const simIdentity = await this.getCurrentSimIdentity();
|
||||
|
||||
logger.info('✓ PDU解析成功');
|
||||
logger.info(`发送者: ${sender}`);
|
||||
logger.info(`时间戳: ${timestamp}`);
|
||||
logger.info(`收件SIM: ${simIdentity?.simLabel || '未知SIM'}`);
|
||||
logger.info(`内容: ${text}`);
|
||||
|
||||
// 检查是否为长短信
|
||||
@@ -62,11 +71,12 @@ class SMSProcessor {
|
||||
partNumber,
|
||||
totalParts,
|
||||
text,
|
||||
timestamp
|
||||
timestamp,
|
||||
{ simIdentity }
|
||||
);
|
||||
} else {
|
||||
// 普通短信,直接处理
|
||||
await this.processSmsContent(sender, text, timestamp);
|
||||
await this.processSmsContent(sender, text, timestamp, { simIdentity });
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('处理PDU失败:', err);
|
||||
@@ -76,14 +86,19 @@ class SMSProcessor {
|
||||
/**
|
||||
* 处理短信内容并转发
|
||||
*/
|
||||
async processSmsContent(sender, text, timestamp) {
|
||||
async processSmsContent(sender, text, timestamp, options = {}) {
|
||||
const simIdentity = options.simIdentity !== undefined
|
||||
? this.normalizeSimIdentity(options.simIdentity)
|
||||
: await this.getCurrentSimIdentity();
|
||||
|
||||
logger.info('=== 处理短信内容 ===');
|
||||
logger.info(`发送者: ${sender}`);
|
||||
logger.info(`时间戳: ${timestamp}`);
|
||||
logger.info(`收件SIM: ${simIdentity?.simLabel || '未知SIM'}`);
|
||||
logger.info(`内容: ${text}`);
|
||||
logger.info('====================');
|
||||
|
||||
const messageRecord = this.addReceivedMessage(sender, text, timestamp);
|
||||
const messageRecord = this.addReceivedMessage(sender, text, timestamp, simIdentity);
|
||||
|
||||
// 推送到所有通道
|
||||
await this.pushManager.pushToAll(sender, text, timestamp);
|
||||
@@ -102,13 +117,16 @@ class SMSProcessor {
|
||||
/**
|
||||
* 记录收到的短信,供Web管理端展示。
|
||||
*/
|
||||
addReceivedMessage(sender, text, timestamp) {
|
||||
addReceivedMessage(sender, text, timestamp, simIdentity = null) {
|
||||
const normalizedSimIdentity = this.normalizeSimIdentity(simIdentity);
|
||||
const message = {
|
||||
id: `${Date.now()}-${++this.receivedMessageSeq}`,
|
||||
sender,
|
||||
text,
|
||||
timestamp,
|
||||
receivedAt: new Date().toISOString(),
|
||||
simId: normalizedSimIdentity?.simId || '',
|
||||
simLabel: normalizedSimIdentity?.simLabel || '未知SIM',
|
||||
status: 'received',
|
||||
statusText: '已接收'
|
||||
};
|
||||
@@ -119,7 +137,7 @@ class SMSProcessor {
|
||||
}
|
||||
|
||||
this.persistReceivedMessages();
|
||||
logger.info(`短信已进入Web收件箱: ${sender}`);
|
||||
logger.info(`短信已进入Web收件箱: ${sender} (${message.simLabel})`);
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -138,8 +156,53 @@ class SMSProcessor {
|
||||
* 获取最近收到的短信。
|
||||
*/
|
||||
getReceivedMessages(limit = 50) {
|
||||
const safeLimit = Math.min(Math.max(Number(limit) || 50, 1), this.maxReceivedMessages);
|
||||
return this.receivedMessages.slice(0, safeLimit);
|
||||
return this.receivedMessages.slice(0, this.resolveReceivedMessagesLimit(limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取按SIM作用域过滤后的短信。
|
||||
*/
|
||||
listReceivedMessages(limit = 50, options = {}) {
|
||||
const safeLimit = this.resolveReceivedMessagesLimit(limit);
|
||||
const requestedScope = String(options.scope || 'current').trim().toLowerCase();
|
||||
const currentSim = this.normalizeSimIdentity(options.currentSimIdentity);
|
||||
const includeAllSims = requestedScope === 'all' && this.allowAllSimMessages;
|
||||
|
||||
let effectiveScope = includeAllSims ? 'all' : 'current';
|
||||
let messages = this.receivedMessages;
|
||||
|
||||
if (this.partitionMessagesBySim && !includeAllSims) {
|
||||
if (currentSim?.simId) {
|
||||
messages = this.receivedMessages.filter(message => message.simId === currentSim.simId);
|
||||
} else {
|
||||
messages = [];
|
||||
effectiveScope = 'unavailable';
|
||||
}
|
||||
} else if (!this.partitionMessagesBySim) {
|
||||
effectiveScope = 'all';
|
||||
}
|
||||
|
||||
return {
|
||||
messages: messages.slice(0, safeLimit),
|
||||
meta: {
|
||||
limit: safeLimit,
|
||||
total: messages.length,
|
||||
scope: effectiveScope,
|
||||
requestedScope,
|
||||
partitionBySim: this.partitionMessagesBySim,
|
||||
allSimMessagesAllowed: this.allowAllSimMessages,
|
||||
allSimMessagesDenied: this.partitionMessagesBySim && requestedScope === 'all' && !this.allowAllSimMessages,
|
||||
currentSim,
|
||||
currentSimKnown: Boolean(currentSim?.simId)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制收件箱查询条数。
|
||||
*/
|
||||
resolveReceivedMessagesLimit(limit = 50) {
|
||||
return Math.min(Math.max(Number(limit) || 50, 1), this.maxReceivedMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,6 +217,20 @@ class SMSProcessor {
|
||||
return Math.min(Math.floor(configured), 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否按当前SIM隔离Web收件箱。默认开启,避免换卡后读取其他SIM历史。
|
||||
*/
|
||||
resolvePartitionMessagesBySim() {
|
||||
return this.config.inbox?.partitionBySim !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许显式查询全部SIM历史。默认关闭。
|
||||
*/
|
||||
resolveAllowAllSimMessages() {
|
||||
return this.config.inbox?.allowAllSimMessages === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析收件箱持久化文件路径。
|
||||
*/
|
||||
@@ -232,17 +309,153 @@ class SMSProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
const simIdentity = this.normalizeStoredSimIdentity(item);
|
||||
|
||||
return {
|
||||
id,
|
||||
sender: String(item.sender || '未知号码'),
|
||||
text: String(item.text || ''),
|
||||
timestamp: String(item.timestamp || item.receivedAt || new Date().toISOString()),
|
||||
receivedAt: String(item.receivedAt || new Date().toISOString()),
|
||||
simId: simIdentity?.simId || '',
|
||||
simLabel: simIdentity?.simLabel || '未知SIM',
|
||||
status: String(item.status || 'received'),
|
||||
statusText: String(item.statusText || '已接收')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从历史记录中恢复SIM身份。旧记录没有SIM字段,会归到未知SIM。
|
||||
*/
|
||||
normalizeStoredSimIdentity(item) {
|
||||
if (item.simId || item.simLabel) {
|
||||
return this.normalizeSimIdentity({
|
||||
simId: item.simId,
|
||||
simLabel: item.simLabel
|
||||
});
|
||||
}
|
||||
|
||||
if (item.sim && typeof item.sim === 'object') {
|
||||
return this.normalizeSimIdentity(item.sim);
|
||||
}
|
||||
|
||||
if (item.simIccid || item.iccid) {
|
||||
return this.createSimIdentityFromICCID(item.simIccid || item.iccid);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前SIM身份,用于收到短信时打标和Web收件箱过滤。
|
||||
*/
|
||||
async getCurrentSimIdentity(options = {}) {
|
||||
const refresh = options.refresh === true;
|
||||
const now = Date.now();
|
||||
|
||||
if (!refresh && this.currentSimCheckedAt && now - this.currentSimCheckedAt < this.simIdentityTtlMs) {
|
||||
return this.currentSimIdentity;
|
||||
}
|
||||
|
||||
if (this.currentSimLookup) {
|
||||
return this.currentSimLookup;
|
||||
}
|
||||
|
||||
if (!this.modem?.getICCID) {
|
||||
this.currentSimIdentity = null;
|
||||
this.currentSimCheckedAt = now;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.currentSimLookup = (async () => {
|
||||
try {
|
||||
const iccid = await this.modem.getICCID({ refresh: true });
|
||||
return this.setCurrentSimFromICCID(iccid);
|
||||
} catch (err) {
|
||||
logger.warn(`查询当前SIM标识失败: ${err.message}`);
|
||||
this.currentSimIdentity = null;
|
||||
this.currentSimCheckedAt = Date.now();
|
||||
return null;
|
||||
} finally {
|
||||
this.currentSimLookup = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return this.currentSimLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用ICCID更新当前SIM身份缓存。
|
||||
*/
|
||||
setCurrentSimFromICCID(iccid) {
|
||||
return this.setCurrentSimIdentity(this.createSimIdentityFromICCID(iccid));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前SIM身份缓存。
|
||||
*/
|
||||
setCurrentSimIdentity(identity) {
|
||||
this.currentSimIdentity = this.normalizeSimIdentity(identity);
|
||||
this.currentSimCheckedAt = Date.now();
|
||||
return this.currentSimIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ICCID生成稳定但不暴露完整卡号的SIM标识。
|
||||
*/
|
||||
createSimIdentityFromICCID(iccid) {
|
||||
const normalized = this.normalizeICCID(iccid);
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
simId: crypto.createHash('sha256').update(`iccid:${normalized}`).digest('hex').slice(0, 24),
|
||||
simLabel: this.formatSimLabel(normalized)
|
||||
};
|
||||
}
|
||||
|
||||
normalizeICCID(iccid) {
|
||||
return String(iccid || '').replace(/\D/g, '');
|
||||
}
|
||||
|
||||
formatSimLabel(iccid) {
|
||||
const value = String(iccid || '');
|
||||
if (!value) {
|
||||
return '未知SIM';
|
||||
}
|
||||
|
||||
return `ICCID ...${value.slice(-6)}`;
|
||||
}
|
||||
|
||||
normalizeSimIdentity(identity) {
|
||||
if (!identity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof identity === 'string') {
|
||||
return this.createSimIdentityFromICCID(identity);
|
||||
}
|
||||
|
||||
if (typeof identity !== 'object' || Array.isArray(identity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (identity.iccid || identity.simIccid) {
|
||||
return this.createSimIdentityFromICCID(identity.iccid || identity.simIccid);
|
||||
}
|
||||
|
||||
const simId = String(identity.simId || identity.id || '').trim();
|
||||
if (!simId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
simId,
|
||||
simLabel: String(identity.simLabel || identity.label || '未知SIM')
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default SMSProcessor;
|
||||
|
||||
Reference in New Issue
Block a user