- GeradoresService: cada tipo passa a ser um IGenerator (NIF, NIPC, NISS, CC, passaporte, IBAN, NIB, cartões, referência MB, pessoa, telefone, código postal, email, matrícula, EAN, UUID) registado em GeneratorRegistry; remove DAL/EF - GeradoresWS: minimal APIs em /api/v1 com OpenAPI + Scalar e health check - UnitTest: testes de ida e volta, valores conhecidos, detecção e API - geradoresfe: UI construída a partir dos metadados da API, validador livre, exportação CSV/JSON/TXT, nginx com API_UPSTREAM configurável - Docker, docker-compose e Jenkinsfile actualizados Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.5 KiB
C#
72 lines
2.5 KiB
C#
using System.Numerics;
|
|
|
|
namespace GeradoresService.Core;
|
|
|
|
public static class Checksums
|
|
{
|
|
/// <summary>
|
|
/// Dígito de controlo módulo 11 com pesos decrescentes (n+1 .. 2), usado no NIF e no NIC do CC.
|
|
/// Resto 0 ou 1 dá dígito 0.
|
|
/// </summary>
|
|
public static int Mod11(ReadOnlySpan<char> digits)
|
|
{
|
|
var sum = 0;
|
|
for (var i = 0; i < digits.Length; i++)
|
|
sum += (digits[i] - '0') * (digits.Length + 1 - i);
|
|
var remainder = sum % 11;
|
|
return remainder < 2 ? 0 : 11 - remainder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Soma Luhn a partir da direita, duplicando posições alternadas.
|
|
/// Aceita letras (A=10 .. Z=35), como no número de documento do Cartão de Cidadão.
|
|
/// </summary>
|
|
public static int LuhnSum(ReadOnlySpan<char> value)
|
|
{
|
|
var sum = 0;
|
|
var doubleIt = false;
|
|
for (var i = value.Length - 1; i >= 0; i--)
|
|
{
|
|
var v = CharValue(value[i]);
|
|
if (doubleIt)
|
|
{
|
|
v *= 2;
|
|
if (v > 9) v -= 9;
|
|
}
|
|
sum += v;
|
|
doubleIt = !doubleIt;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
public static bool LuhnValid(ReadOnlySpan<char> value) => LuhnSum(value) % 10 == 0;
|
|
|
|
/// <summary>Calcula o dígito Luhn a acrescentar a <paramref name="payload"/>.</summary>
|
|
public static int LuhnCheckDigit(string payload)
|
|
{
|
|
var sum = LuhnSum(payload + "0");
|
|
return (10 - sum % 10) % 10;
|
|
}
|
|
|
|
/// <summary>ISO 7064 MOD 97-10 sobre uma string de dígitos.</summary>
|
|
public static int Mod97(string digits) => (int)(BigInteger.Parse(digits) % 97);
|
|
|
|
/// <summary>Converte letras em números (A=10 .. Z=35) para cálculo de IBAN.</summary>
|
|
public static string LettersToDigits(string value) =>
|
|
string.Concat(value.Select(c => char.IsLetter(c) ? (char.ToUpperInvariant(c) - 'A' + 10).ToString() : c.ToString()));
|
|
|
|
private static int CharValue(char c) => c switch
|
|
{
|
|
>= '0' and <= '9' => c - '0',
|
|
>= 'A' and <= 'Z' => c - 'A' + 10,
|
|
>= 'a' and <= 'z' => c - 'a' + 10,
|
|
_ => throw new ArgumentException($"Carácter inválido '{c}'."),
|
|
};
|
|
|
|
/// <summary>Remove espaços, hífens, pontos e barras.</summary>
|
|
public static string Clean(string value) =>
|
|
new(value.Where(c => !char.IsWhiteSpace(c) && c is not '-' and not '.' and not '/').ToArray());
|
|
|
|
public static bool AllDigits(string value) => value.Length > 0 && value.All(char.IsAsciiDigit);
|
|
}
|