fix: 登录兼容联通19位
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
# Change: Retry 19-digit ICCID login with a Luhn check digit
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Some IoT cards are entered or scanned as a 19-digit ICCID prefix. The asset-verification endpoint requires the 20-digit ICCID, so the first verification cannot return an asset token even though the identifier can be deterministically completed.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- When a login identifier is exactly 19 numeric characters and starts with `89`, call `/api/c/v1/auth/verify-asset` with the entered value first.
|
||||||
|
- If that first request fails or does not return `asset_token`, compute the twentieth digit with the ISO/IEC 7812 Luhn (mod-10) check-digit algorithm and retry verification once using the completed ICCID.
|
||||||
|
- Keep the first failure entirely silent to the customer. Only the retry failure is handled by the existing login error flow.
|
||||||
|
- Persist and use the successful 20-digit ICCID for the authenticated session without mutating the text in the input field during the silent retry.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- Affected capability: `iccid-login-check-digit-retry` (new)
|
||||||
|
- Affected code: `pages/login/login.vue`
|
||||||
|
- Affected API: `POST /api/c/v1/auth/verify-asset`
|
||||||
|
- No backend API, request payload shape, or shared error handling is changed.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Silent 19-digit ICCID verification retry
|
||||||
|
|
||||||
|
When an asset login identifier matches `^89\\d{17}$`, the H5 client SHALL first verify the entered 19-digit value. If that attempt fails or does not return a non-empty `asset_token`, it SHALL calculate the ISO/IEC 7812 Luhn mod-10 check digit, append it as the twentieth digit, and verify the completed ICCID exactly once.
|
||||||
|
|
||||||
|
#### Scenario: Completed ICCID succeeds
|
||||||
|
|
||||||
|
- **WHEN** a customer enters a 19-digit numeric identifier beginning with `89`
|
||||||
|
- **AND** the initial verification does not provide an asset token
|
||||||
|
- **AND** verification of the Luhn-completed 20-digit ICCID returns an asset token
|
||||||
|
- **THEN** the customer continues through login without seeing an error from the first verification
|
||||||
|
- **AND** the authenticated session uses the 20-digit ICCID
|
||||||
|
|
||||||
|
#### Scenario: Initial verification succeeds
|
||||||
|
|
||||||
|
- **WHEN** a customer enters a 19-digit numeric identifier beginning with `89`
|
||||||
|
- **AND** initial verification returns an asset token
|
||||||
|
- **THEN** the client SHALL not calculate or submit a second identifier
|
||||||
|
|
||||||
|
#### Scenario: Completed ICCID fails
|
||||||
|
|
||||||
|
- **WHEN** the Luhn-completed retry does not return an asset token
|
||||||
|
- **THEN** the client SHALL invoke the existing login failure presentation once using the retry failure
|
||||||
|
|
||||||
|
#### Scenario: Identifier is not a 19-digit ICCID prefix
|
||||||
|
|
||||||
|
- **WHEN** an identifier does not match `^89\\d{17}$`
|
||||||
|
- **AND** verification fails or does not return an asset token
|
||||||
|
- **THEN** the client SHALL not issue a retry
|
||||||
|
- **AND** SHALL retain the existing login failure presentation
|
||||||
10
openspec/changes/add-iccid-login-check-digit-retry/tasks.md
Normal file
10
openspec/changes/add-iccid-login-check-digit-retry/tasks.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
## 1. Login fallback
|
||||||
|
|
||||||
|
- [x] 1.1 Add a pure ISO/IEC 7812 Luhn mod-10 check-digit helper for a 19-digit ICCID prefix.
|
||||||
|
- [x] 1.2 Retry asset verification once and silently only when the initial identifier matches `^89\\d{17}$` and the first verification fails or lacks `asset_token`.
|
||||||
|
- [x] 1.3 Persist the completed ICCID only after the retry succeeds; retain the existing visible error path for all final failures and non-matching identifiers.
|
||||||
|
|
||||||
|
## 2. Verification
|
||||||
|
|
||||||
|
- [x] 2.1 Verify known Luhn vectors plus first-attempt success, fallback success, fallback failure, and non-ICCID failure behavior.
|
||||||
|
- [x] 2.2 Run the H5 production build.
|
||||||
@@ -258,6 +258,50 @@
|
|||||||
return (params.get('identifier') || '').trim();
|
return (params.get('identifier') || '').trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isNineteenDigitIccidPrefix = (value) => /^89\d{17}$/.test(value);
|
||||||
|
|
||||||
|
const appendIccidLuhnCheckDigit = (prefix) => {
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
|
for (let index = prefix.length - 1, offset = 0; index >= 0; index--, offset++) {
|
||||||
|
let digit = Number(prefix[index]);
|
||||||
|
if (offset % 2 === 0) {
|
||||||
|
digit *= 2;
|
||||||
|
if (digit > 9) digit -= 9;
|
||||||
|
}
|
||||||
|
sum += digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${prefix}${(10 - (sum % 10)) % 10}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyAssetToken = async (assetIdentifier) => {
|
||||||
|
const verifyData = await authApi.verifyAsset(assetIdentifier);
|
||||||
|
if (!verifyData?.asset_token) {
|
||||||
|
throw { msg: '资产校验未返回有效登录凭证' };
|
||||||
|
}
|
||||||
|
return verifyData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyAssetWithIccidRetry = async (enteredIdentifier) => {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
verifyData: await verifyAssetToken(enteredIdentifier),
|
||||||
|
verifiedIdentifier: enteredIdentifier
|
||||||
|
};
|
||||||
|
} catch (firstError) {
|
||||||
|
if (!isNineteenDigitIccidPrefix(enteredIdentifier)) {
|
||||||
|
throw firstError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const completedIccid = appendIccidLuhnCheckDigit(enteredIdentifier);
|
||||||
|
return {
|
||||||
|
verifyData: await verifyAssetToken(completedIccid),
|
||||||
|
verifiedIdentifier: completedIccid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const doLogin = async () => {
|
const doLogin = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
// 丢弃上一轮未完成授权留下的临时凭证,避免校验失败时继续复用。
|
// 丢弃上一轮未完成授权留下的临时凭证,避免校验失败时继续复用。
|
||||||
@@ -265,13 +309,11 @@
|
|||||||
sessionStorage.removeItem('assetToken');
|
sessionStorage.removeItem('assetToken');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const verifyData = await authApi.verifyAsset(identifier.value);
|
const enteredIdentifier = identifier.value;
|
||||||
if (!verifyData?.asset_token) {
|
const { verifyData, verifiedIdentifier } = await verifyAssetWithIccidRetry(enteredIdentifier);
|
||||||
throw { msg: '资产校验未返回有效登录凭证' };
|
|
||||||
}
|
|
||||||
|
|
||||||
userStore.setAssetToken(verifyData.asset_token);
|
userStore.setAssetToken(verifyData.asset_token);
|
||||||
userStore.setIdentifier(identifier.value);
|
userStore.setIdentifier(verifiedIdentifier);
|
||||||
|
|
||||||
await redirectToWxAuth(verifyData.asset_token);
|
await redirectToWxAuth(verifyData.asset_token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user