sms模拟的路由

This commit is contained in:
2026-10-27 15:51:05 +08:00
parent 5135207bf8
commit cdf0976f2e
3 changed files with 117 additions and 44 deletions
+31 -15
View File
@@ -1,36 +1,52 @@
// Message Gateway 内容发送接口 Mock:POST /ewl/msgw/v1/messages/content/send // Message Gateway 内容发送接口 Mock:POST /ewl/msgw/v1/messages/content/send
// 后端短信已从旧 SMS Hub(/SMSToken + /SMSRequest/Post)切换到该接口,认证复用 EDIP Token。 // 后端短信已从旧 SMS Hub(/SMSToken + /SMSRequest/Post)切换到该接口,认证复用 EDIP Token。
// 响应严格按真实网关信封:HTTP 200 + status/errorCode,成功时 smsid 为全小写字段、errorCode 为空字符串。 // 响应严格按真实网关信封:HTTP 200 + status/errorCode,成功时 smsid 为全小写字段、errorCode 为空字符串。
const { getForcedErrorCode, getForcedErrorMsg } = require('./smsErrorState') // 支持三类故障注入(见 smsErrorState.js):HTTP 状态码、业务错误码、收件人级失败状态、缺失 smsid。
const { getFaultInjection } = require('./smsErrorState')
function registerMessageGatewayContentSendRoute({ app }) { function registerMessageGatewayContentSendRoute({ app }) {
app.post('/ewl/msgw/v1/messages/content/send', (req, res) => { app.post('/ewl/msgw/v1/messages/content/send', (req, res) => {
const data = req.body?.data || {} const data = req.body?.data || {}
const recipients = Array.isArray(data.recipients) ? data.recipients : [] const recipients = Array.isArray(data.recipients) ? data.recipients : []
const fault = getFaultInjection()
// 错误注入:优先取运行时开关(/mock-api/sms/content-send-error),回落环境变量。 // ① HTTP 层故障:以指定状态码响应,用于验证 4xx 不重试、5xx/429 重试
// 8923 属供应商暂不可用(后端应重试),8907 属号码非法(后端不应重试) if (fault.httpStatus) {
const forcedErrorCode = getForcedErrorCode() return res.status(fault.httpStatus).json({
if (forcedErrorCode) {
return res.status(200).json({
status: false, status: false,
errorCode: forcedErrorCode, errorCode: fault.errorCode || '',
message: getForcedErrorMsg(), message: fault.errorMsg,
data: null, data: null,
}) })
} }
// ② 业务错误码:HTTP 200 + errorCode,用于验证 8901-8910 不重试、8911-8930 重试
if (fault.errorCode) {
return res.status(200).json({
status: false,
errorCode: fault.errorCode,
message: fault.errorMsg,
data: null,
})
}
// ③ 成功响应:可选注入收件人级失败状态(smsStatus=FAILED)与缺失 smsid
const recipientStatus = fault.recipientStatus || 'QUEUED'
const responseData = {
smsStatus: recipients.map((r) => ({
recipient: r?.phone || '',
status: recipientStatus,
})),
}
if (!fault.suppressSmsId) {
responseData.smsid = `mock-sms-${Date.now()}`
}
return res.status(200).json({ return res.status(200).json({
status: true, status: true,
errorCode: '', errorCode: '',
message: '', message: '',
data: { data: responseData,
smsid: `mock-sms-${Date.now()}`,
smsStatus: recipients.map((r) => ({
recipient: r?.phone || '',
status: 'QUEUED',
})),
},
}) })
}) })
} }
+64 -13
View File
@@ -1,25 +1,76 @@
// Message Gateway 内容发送 Mock 的运行时错误码开关。 // Message Gateway 内容发送 Mock 的运行时故障注入开关。
// 目的:联调时可在进程内切换 errorCode(如 8923 可重试、8907 不可重试),不必反复重启 Mock 进程。 // 目的:联调时可在进程内切换故障形态(错误码 / HTTP 状态码 / 收件人级失败 / 缺失 smsid),
// 初值取环境变量 MOCK_SMS_FORCE_ERROR_CODE,之后由 /mock-api/sms/content-send-error 覆盖。 // 不必反复重启 Mock 进程。初值取环境变量,之后由 /mock-api/sms/content-send-error 覆盖。
const state = { const state = {
forcedErrorCode: (process.env.MOCK_SMS_FORCE_ERROR_CODE || '').trim(), forcedErrorCode: (process.env.MOCK_SMS_FORCE_ERROR_CODE || '').trim(),
forcedErrorMsg: process.env.MOCK_SMS_FORCE_ERROR_MSG || 'Mock message gateway error', forcedErrorMsg: process.env.MOCK_SMS_FORCE_ERROR_MSG || 'Mock message gateway error',
// 非 0 时直接以该 HTTP 状态码响应(用于验证 4xx 不重试、5xx/429 重试)
forcedHttpStatus: Number(process.env.MOCK_SMS_FORCE_HTTP_STATUS || 0),
// 非空时把每个收件人的 status 替换成该值(用于验证 smsStatus=FAILED 的收件人级失败)
forcedRecipientStatus: (process.env.MOCK_SMS_FORCE_RECIPIENT_STATUS || '').trim(),
// 为真时成功响应里不返回 smsid(用于验证成功但无网关消息 ID 的审计标注)
suppressSmsId: String(process.env.MOCK_SMS_SUPPRESS_SMS_ID || '').toLowerCase() === 'true',
} }
function setForcedErrorCode(errorCode, errorMsg) { function normalizeHttpStatus(value) {
state.forcedErrorCode = errorCode == null ? '' : String(errorCode).trim() const status = Number(value || 0)
if (errorMsg != null && String(errorMsg).trim() !== '') { return Number.isInteger(status) && status >= 100 && status <= 599 ? status : 0
state.forcedErrorMsg = String(errorMsg).trim() }
/** 覆盖故障注入配置;未提供的字段保持不变,传 null/空串表示清除该项 */
function setFaultInjection(overrides = {}) {
if (overrides.errorCode !== undefined) {
state.forcedErrorCode = overrides.errorCode == null ? '' : String(overrides.errorCode).trim()
} }
return { errorCode: state.forcedErrorCode, errorMsg: state.forcedErrorMsg } if (overrides.errorMsg !== undefined && String(overrides.errorMsg || '').trim() !== '') {
state.forcedErrorMsg = String(overrides.errorMsg).trim()
}
if (overrides.httpStatus !== undefined) {
state.forcedHttpStatus = normalizeHttpStatus(overrides.httpStatus)
}
if (overrides.recipientStatus !== undefined) {
state.forcedRecipientStatus =
overrides.recipientStatus == null ? '' : String(overrides.recipientStatus).trim()
}
if (overrides.noSmsId !== undefined) {
state.suppressSmsId = Boolean(overrides.noSmsId)
}
return currentFaultInjection()
} }
function getForcedErrorCode() { function currentFaultInjection() {
return state.forcedErrorCode return {
errorCode: state.forcedErrorCode,
errorMsg: state.forcedErrorMsg,
httpStatus: state.forcedHttpStatus,
recipientStatus: state.forcedRecipientStatus,
noSmsId: state.suppressSmsId,
}
} }
function getForcedErrorMsg() { /** 清除全部故障注入,回到真实成功响应 */
return state.forcedErrorMsg function clearFaultInjection() {
state.forcedErrorCode = ''
state.forcedErrorMsg = 'Mock message gateway error'
state.forcedHttpStatus = 0
state.forcedRecipientStatus = ''
state.suppressSmsId = false
return currentFaultInjection()
} }
module.exports = { setForcedErrorCode, getForcedErrorCode, getForcedErrorMsg } function getFaultInjection() {
return {
errorCode: state.forcedErrorCode,
errorMsg: state.forcedErrorMsg,
httpStatus: state.forcedHttpStatus,
recipientStatus: state.forcedRecipientStatus,
suppressSmsId: state.suppressSmsId,
}
}
module.exports = {
setFaultInjection,
clearFaultInjection,
currentFaultInjection,
getFaultInjection,
}
@@ -1,6 +1,15 @@
// Message Gateway 内容发送 Mock 的错误码开关节点(仅联调用)。 // Message Gateway 内容发送 Mock 的故障注入开关节点(仅联调用)。
// 用途:进程内切换 errorCode,验后端重试语义(8911-8930 应重试,8901-8910 不应重试),避免反复重启 Mock。 // 用途:进程内切换故障形态,验证后端的成功/错误处理与重试语义,避免反复重启 Mock。
const { setForcedErrorCode, getForcedErrorCode, getForcedErrorMsg } = require('../ewl/msgw/smsErrorState') // POST {"errorCode":"8923"} 注入业务错误码(8911-8930 应重试,8901-8910 不应重试)
// POST {"httpStatus":401} 注入 HTTP 状态码(4xx 不应重试,5xx/429 应重试)
// POST {"recipientStatus":"FAILED"} 注入收件人级失败(整体 200 但 smsStatus=FAILED)
// POST {"noSmsId":true} 成功响应中不返回 smsid
// DELETE 清除全部注入
const {
setFaultInjection,
clearFaultInjection,
currentFaultInjection,
} = require('../ewl/msgw/smsErrorState')
function registerSmsContentSendErrorOverrideRoute({ app }) { function registerSmsContentSendErrorOverrideRoute({ app }) {
const route = '/mock-api/sms/content-send-error' const route = '/mock-api/sms/content-send-error'
@@ -8,31 +17,28 @@ function registerSmsContentSendErrorOverrideRoute({ app }) {
app.get(route, (req, res) => app.get(route, (req, res) =>
res.status(200).json({ res.status(200).json({
success: true, success: true,
message: 'SMS content send error override retrieved successfully.', message: 'SMS content send fault injection retrieved successfully.',
data: { errorCode: getForcedErrorCode(), errorMsg: getForcedErrorMsg() }, data: currentFaultInjection(),
}), }),
) )
app.post(route, (req, res) => { app.post(route, (req, res) => {
const body = req.body && typeof req.body === 'object' ? req.body : {} const body = req.body && typeof req.body === 'object' ? req.body : {}
const override = setForcedErrorCode(body.errorCode ?? '', body.errorMsg)
return res.status(200).json({ return res.status(200).json({
success: true, success: true,
message: 'SMS content send error override saved successfully.', message: 'SMS content send fault injection saved successfully.',
data: override, data: setFaultInjection(body),
}) })
}) })
app.delete(route, (req, res) => { app.delete(route, (req, res) =>
const cleared = setForcedErrorCode('', null) res.status(200).json({
return res.status(200).json({
success: true, success: true,
message: 'SMS content send error override cleared successfully.', message: 'SMS content send fault injection cleared successfully.',
data: cleared, data: clearFaultInjection(),
}) }),
}) )
} }
module.exports = registerSmsContentSendErrorOverrideRoute module.exports = registerSmsContentSendErrorOverrideRoute