fix(madao): 适配国家中文标签和线路归一化

- 优先显示 MaDao 返回的 label_zh,不在 FlowPilot 内维护国家翻译表
- 兼容旧 provider_value 保存值,刷新直连国家后自动反选 ISO value
- 将 any / Any operator 归一为空线路,避免重复选项和错误下发
- 保留 MaDao 隐藏复用控件缺失时的默认开启状态
This commit is contained in:
Isulew
2026-05-29 21:59:20 +08:00
parent e441c5e7b8
commit c64ff5152c
4 changed files with 209 additions and 12 deletions
+11 -1
View File
@@ -42,6 +42,15 @@
return normalizeText(value).toLowerCase().replace(/[^a-z0-9_-]+/g, '');
}
function normalizeOperator(value = '') {
const rawValue = normalizeText(value);
const compactValue = rawValue.toLowerCase().replace(/[^a-z0-9]+/g, '');
if (!rawValue || compactValue === 'any' || compactValue === 'anyoperator') {
return '';
}
return rawValue.toLowerCase().replace(/[^a-z0-9_-]+/g, '');
}
function normalizeCountry(value = '') {
const trimmed = normalizeText(value);
if (!trimmed) {
@@ -255,7 +264,7 @@
service: normalizeText(options?.service || state?.madaoServiceName, DEFAULT_SERVICE),
};
const country = normalizeCountry(options?.country || state?.madaoCountry);
const operator = normalizeProviderId(options?.operator || state?.madaoOperator);
const operator = normalizeOperator(options?.operator || state?.madaoOperator);
const minPrice = normalizePrice(options?.minPrice ?? state?.madaoMinPrice);
const maxPrice = normalizePrice(options?.maxPrice ?? state?.madaoMaxPrice);
@@ -708,6 +717,7 @@
normalizeCountry,
normalizeCountryKey,
normalizeCountryLabel,
normalizeOperator,
resolveActivationCountry,
pollActivation,
pollActivationCode,
+90 -7
View File
@@ -5705,7 +5705,12 @@ function normalizeMaDaoProviderIdValue(value = '') {
}
function normalizeMaDaoOperatorValue(value = '') {
return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '');
const rawValue = String(value || '').trim();
const compactValue = rawValue.toLowerCase().replace(/[^a-z0-9]+/g, '');
if (!rawValue || compactValue === 'any' || compactValue === 'anyoperator') {
return '';
}
return rawValue.toLowerCase().replace(/[^a-z0-9_-]+/g, '');
}
function normalizeMaDaoCountry(value = '') {
@@ -5723,6 +5728,22 @@ function normalizeMaDaoCountry(value = '') {
return lowered.replace(/[^a-z0-9_-]+/g, '');
}
function formatMaDaoCountryDisplayLabel(value = '', label = '', labelZh = '') {
const country = normalizeMaDaoCountry(value);
const sourceLabelZh = String(labelZh || '').trim();
const sourceLabel = String(label || '').trim();
if (!country) {
return sourceLabelZh || sourceLabel;
}
if (country === 'local') {
return sourceLabelZh || '本地';
}
if (country === 'any') {
return sourceLabelZh || '任意国家';
}
return sourceLabelZh || sourceLabel || country;
}
function normalizeMaDaoPriceValue(value = '') {
const rawValue = String(value ?? '').trim();
if (!rawValue) {
@@ -5860,6 +5881,7 @@ function normalizeMaDaoOptionListItems(items = [], selectedValue = '', normalize
normalizedItems.push({
value,
label: String(item?.label || item?.name || item?.display_name || item?.displayName || value).trim() || value,
labelZh: String(item?.label_zh || item?.labelZh || item?.display_name_zh || item?.displayNameZh || '').trim(),
hint: String(item?.hint || item?.description || item?.provider_value || item?.providerValue || '').trim(),
enabled: item?.enabled !== false,
});
@@ -5875,8 +5897,54 @@ function normalizeMaDaoOptionListItems(items = [], selectedValue = '', normalize
return normalizedItems.filter((item) => item.enabled !== false);
}
function resolveMaDaoOptionSelectedValue(items = [], selectedValue = '', normalizeValue = normalizeMaDaoIdentifierValue) {
const normalizedSelected = normalizeValue(selectedValue);
if (!normalizedSelected) {
return '';
}
const sourceItems = Array.isArray(items) ? items : [];
for (const item of sourceItems) {
const value = normalizeValue(
item?.value
|| item?.id
|| item?.provider
|| item?.provider_value
|| item?.providerValue
|| item?.country
|| item?.operator
|| ''
);
if (!value) {
continue;
}
const aliases = [
item?.value,
item?.id,
item?.provider,
item?.provider_value,
item?.providerValue,
item?.country,
item?.operator,
item?.label,
item?.label_zh,
item?.labelZh,
item?.name,
item?.display_name,
item?.displayName,
];
if (aliases.some((alias) => normalizeValue(alias || '') === normalizedSelected)) {
return value;
}
}
return normalizedSelected;
}
function setMaDaoProviderSelectOptions(selectedValue = latestState?.madaoProviderId || '') {
const normalizedSelected = normalizeMaDaoProviderIdValue(selectedValue);
const normalizedSelected = resolveMaDaoOptionSelectedValue(
maDaoProviderOptions,
selectedValue,
normalizeMaDaoProviderIdValue
);
const options = normalizeMaDaoOptionListItems(
maDaoProviderOptions,
normalizedSelected,
@@ -5890,13 +5958,20 @@ function setMaDaoProviderSelectOptions(selectedValue = latestState?.madaoProvide
}
function setMaDaoCountrySelectOptions(selectedValue = latestState?.madaoCountry || '') {
const normalizedSelected = normalizeMaDaoCountry(selectedValue);
const normalizedSelected = resolveMaDaoOptionSelectedValue(
maDaoCountryOptions,
selectedValue,
normalizeMaDaoCountry
);
const options = normalizeMaDaoOptionListItems(
maDaoCountryOptions,
normalizedSelected,
normalizeMaDaoCountry,
'已保存的国家'
);
).map((item) => ({
...item,
label: formatMaDaoCountryDisplayLabel(item.value, item.label, item.labelZh),
}));
setSelectOptions(selectMaDaoCountry, options, {
placeholder: '请先选择服务商',
value: normalizedSelected,
@@ -5904,7 +5979,11 @@ function setMaDaoCountrySelectOptions(selectedValue = latestState?.madaoCountry
}
function setMaDaoOperatorSelectOptions(selectedValue = latestState?.madaoOperator || '') {
const normalizedSelected = normalizeMaDaoOperatorValue(selectedValue);
const normalizedSelected = resolveMaDaoOptionSelectedValue(
maDaoOperatorOptions,
selectedValue,
normalizeMaDaoOperatorValue
);
const options = normalizeMaDaoOptionListItems(
maDaoOperatorOptions,
normalizedSelected,
@@ -17653,8 +17732,12 @@ function buildPhoneSmsProviderStatePatch(provider = getSelectedPhoneSmsProvider(
madaoOperator: shouldReadMaDaoDirectControls
? normalizeMaDaoOperatorValue(selectMaDaoOperator?.value || '')
: normalizeMaDaoOperatorValue(latestState?.madaoOperator || ''),
madaoAutoPickCountry: Boolean(inputMaDaoAutoPickCountry?.checked),
madaoReusePhone: Boolean(inputMaDaoReusePhone?.checked),
madaoAutoPickCountry: typeof inputMaDaoAutoPickCountry !== 'undefined' && inputMaDaoAutoPickCountry
? Boolean(inputMaDaoAutoPickCountry.checked)
: (latestState?.madaoAutoPickCountry !== undefined ? Boolean(latestState.madaoAutoPickCountry) : true),
madaoReusePhone: typeof inputMaDaoReusePhone !== 'undefined' && inputMaDaoReusePhone
? Boolean(inputMaDaoReusePhone.checked)
: (latestState?.madaoReusePhone !== undefined ? Boolean(latestState.madaoReusePhone) : true),
madaoMinPrice: normalizeMaDaoPriceValue(inputMaDaoMinPrice?.value || ''),
madaoMaxPrice: normalizeMaDaoPriceValue(inputMaDaoMaxPrice?.value || ''),
};
+31
View File
@@ -104,6 +104,37 @@ test('MaDao routing acquire sends routing plan only', async () => {
assert.equal(activation.madaoRoutingItemId, 'route-1');
});
test('MaDao direct acquire treats any operator as default route', async () => {
const requests = [];
const provider = api.createProvider({
fetchImpl: async (_url, options = {}) => {
requests.push({ body: JSON.parse(options.body) });
return createJsonResponse({
ticket_id: 'ticket-any',
phone_number: '+66111111111',
country: 'TH',
provider: 'fivesim',
});
},
});
await provider.acquireActivation({
madaoMode: 'direct',
madaoProviderId: 'fivesim',
madaoCountry: 'TH',
madaoOperator: 'Any operator',
});
assert.equal(requests.length, 1);
assert.deepEqual(requests[0].body, {
provider: 'fivesim',
service: 'openai',
auto_pick_country: true,
reuse_phone: true,
country: 'TH',
});
});
test('MaDao poll extracts codes from nested messages and reports pending status', async () => {
const statusEvents = [];
const provider = api.createProvider({
@@ -315,8 +315,8 @@ test('MaDao direct selects load provider country and operator options from daemo
return JSON.stringify({
provider: 'stored-provider',
items: [
{ value: 'GB', label: 'United Kingdom' },
{ value: 'TH', label: 'Thailand' },
{ value: 'GB', label: 'United Kingdom', label_zh: '英国', provider_value: 'england' },
{ value: 'TH', label: 'Thailand', label_zh: '泰国', provider_value: '52' },
],
});
},
@@ -331,6 +331,7 @@ test('MaDao direct selects load provider country and operator options from daemo
return JSON.stringify({
provider: 'stored-provider',
items: [
{ value: 'any', label: 'Any operator' },
{ value: 'operator-a', label: 'Operator A' },
{ value: 'operator-b', label: 'Operator B' },
],
@@ -347,7 +348,7 @@ let latestState = {
madaoBaseUrl: 'http://madao.local/api/acquire',
madaoHttpSecret: 'madao-secret',
madaoProviderId: 'stored-provider',
madaoCountry: 'GB',
madaoCountry: 'england',
madaoOperator: 'operator-a',
};
let maDaoProviderOptions = [];
@@ -358,7 +359,7 @@ let toastText = '';
const inputMaDaoBaseUrl = { value: 'http://madao.local/api/acquire' };
const inputMaDaoHttpSecret = { value: 'madao-secret' };
const selectMaDaoProviderId = { value: 'stored-provider', options: [], replaceChildren(...children) { this.options = children; } };
const selectMaDaoCountry = { value: 'GB', options: [], replaceChildren(...children) { this.options = children; } };
const selectMaDaoCountry = { value: 'england', options: [], replaceChildren(...children) { this.options = children; } };
const selectMaDaoOperator = { value: 'operator-a', options: [], replaceChildren(...children) { this.options = children; } };
function updateHeroSmsPlatformDisplay() {
displayText = [selectMaDaoProviderId.value, selectMaDaoCountry.value, selectMaDaoOperator.value].filter(Boolean).join('/');
@@ -371,9 +372,11 @@ ${extractFunction('normalizeMaDaoIdentifierValue')}
${extractFunction('normalizeMaDaoProviderIdValue')}
${extractFunction('normalizeMaDaoOperatorValue')}
${extractFunction('normalizeMaDaoCountry')}
${extractFunction('formatMaDaoCountryDisplayLabel')}
${extractFunction('createSelectOptionElement')}
${extractFunction('setSelectOptions')}
${extractFunction('normalizeMaDaoOptionListItems')}
${extractFunction('resolveMaDaoOptionSelectedValue')}
${extractFunction('setMaDaoProviderSelectOptions')}
${extractFunction('setMaDaoCountrySelectOptions')}
${extractFunction('setMaDaoOperatorSelectOptions')}
@@ -408,7 +411,9 @@ return {
assert.equal(api.selectMaDaoOperator.value, 'operator-a');
assert.deepStrictEqual(api.providerOptions.map((option) => option.value), ['', 'stored-provider']);
assert.deepStrictEqual(api.countryOptions.map((option) => option.value), ['', 'GB', 'TH']);
assert.deepStrictEqual(api.countryOptions.map((option) => option.label), ['请先选择服务商', '英国', '泰国']);
assert.deepStrictEqual(api.operatorOptions.map((option) => option.value), ['', 'operator-a', 'operator-b']);
assert.deepStrictEqual(api.operatorOptions.map((option) => option.label), ['任意线路', 'Operator A', 'Operator B']);
assert.equal(api.displayText, 'stored-provider/GB/operator-a');
assert.equal(api.toastText, '已刷新 MaDao 服务商。');
assert.deepStrictEqual(requests.map((request) => request.pathname), [
@@ -1503,6 +1508,7 @@ ${extractFunction('normalizeMaDaoIdentifierValue')}
${extractFunction('normalizeMaDaoProviderIdValue')}
${extractFunction('normalizeMaDaoOperatorValue')}
${extractFunction('normalizeMaDaoCountry')}
${extractFunction('formatMaDaoCountryDisplayLabel')}
${extractFunction('normalizeMaDaoPriceValue')}
${extractFunction('normalizeFiveSimCountryCode')}
${extractFunction('normalizeFiveSimCountryOrderValue')}
@@ -1755,9 +1761,11 @@ ${extractFunction('normalizeMaDaoProviderIdValue')}
${extractFunction('normalizeMaDaoOperatorValue')}
${extractFunction('normalizeMaDaoCountry')}
${extractFunction('normalizeMaDaoPriceValue')}
${extractFunction('formatMaDaoCountryDisplayLabel')}
${extractFunction('createSelectOptionElement')}
${extractFunction('setSelectOptions')}
${extractFunction('normalizeMaDaoOptionListItems')}
${extractFunction('resolveMaDaoOptionSelectedValue')}
${extractFunction('buildMaDaoRoutingPlanOptions')}
${extractFunction('setMaDaoRoutingPlanSelectOptions')}
${extractFunction('setMaDaoProviderSelectOptions')}
@@ -1842,6 +1850,71 @@ return {
assert.equal(api.savedPayload.fiveSimMinPrice, '0.88');
});
test('buildPhoneSmsProviderStatePatch preserves MaDao hidden reuse defaults when controls are absent', () => {
const api = new Function(`
let latestState = {
phoneSmsProvider: 'madao',
madaoBaseUrl: 'http://127.0.0.1:7822',
madaoHttpSecret: 'secret-old',
madaoMode: 'direct',
madaoRoutingPlanId: 'plan-old',
madaoProviderId: 'provider-old',
madaoCountry: 'TH',
madaoOperator: 'any',
madaoAutoPickCountry: true,
madaoReusePhone: true,
madaoMinPrice: '0.01',
madaoMaxPrice: '0.09',
};
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
const PHONE_SMS_PROVIDER_HERO = PHONE_SMS_PROVIDER_HERO_SMS;
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
const PHONE_SMS_PROVIDER_NEXSMS = 'nexsms';
const PHONE_SMS_PROVIDER_MADAO = 'madao';
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'vietnam';
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '越南 (Vietnam)';
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
const DEFAULT_FIVE_SIM_PRODUCT = 'openai';
const DEFAULT_NEX_SMS_SERVICE_CODE = 'ot';
const DEFAULT_MADAO_BASE_URL = 'http://127.0.0.1:7822';
const MADAO_MODE_ROUTING_PLAN = 'routing_plan';
const MADAO_MODE_DIRECT = 'direct';
const DEFAULT_MADAO_MODE = MADAO_MODE_ROUTING_PLAN;
const selectMaDaoMode = { value: 'direct' };
const selectMaDaoRoutingPlanId = { value: 'plan-live' };
const selectMaDaoProviderId = { value: 'provider-live' };
const selectMaDaoCountry = { value: 'GB' };
const selectMaDaoOperator = { value: 'Any operator' };
const inputMaDaoBaseUrl = { value: 'http://127.0.0.1:7822/api/acquire' };
const inputMaDaoHttpSecret = { value: 'secret-live' };
const inputMaDaoMinPrice = { value: '0.02' };
const inputMaDaoMaxPrice = { value: '0.20' };
${extractFunction('normalizePhoneSmsProvider')}
${extractFunction('normalizeMaDaoBaseUrlValue')}
${extractFunction('normalizeMaDaoModeValue')}
${extractFunction('normalizeMaDaoIdentifierValue')}
${extractFunction('normalizeMaDaoRoutingPlanIdValue')}
${extractFunction('normalizeMaDaoProviderIdValue')}
${extractFunction('normalizeMaDaoOperatorValue')}
${extractFunction('normalizeMaDaoCountry')}
${extractFunction('normalizeMaDaoPriceValue')}
${extractFunction('normalizePhoneSmsMinPriceValue')}
${extractFunction('normalizePhoneSmsMaxPriceValue')}
${extractFunction('buildPhoneSmsProviderStatePatch')}
return { buildPhoneSmsProviderStatePatch };
`)();
const patch = api.buildPhoneSmsProviderStatePatch('madao');
assert.equal(patch.madaoAutoPickCountry, true);
assert.equal(patch.madaoReusePhone, true);
assert.equal(patch.madaoOperator, '');
assert.equal(patch.madaoProviderId, 'provider-live');
assert.equal(patch.madaoCountry, 'GB');
});
test('HeroSMS operator helpers normalize keyed operator payloads', () => {
const api = new Function(`
const DEFAULT_HERO_SMS_OPERATOR = 'any';