You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
9.2 KiB
213 lines
9.2 KiB
using System.Text; |
|
using System.Text.RegularExpressions; |
|
using System.ComponentModel.DataAnnotations; |
|
using YbTest.Models.Insurance; |
|
using YbTest.Services.InsuranceBusinessServices; |
|
using System.Collections.Concurrent; |
|
using System.Security.Cryptography; |
|
using System.Text.Json; |
|
|
|
namespace YbTest.Services |
|
{ |
|
public class MedicalInsuranceService |
|
{ |
|
private readonly ConcurrentDictionary<string, IInsuranceBusinessService> _serviceCache = new(); |
|
|
|
public void ValidateRequest(InsuranceRequest request) |
|
{ |
|
if (string.IsNullOrEmpty(request.MsgId)) |
|
throw new ValidationException("报文ID不能为空"); |
|
|
|
if (string.IsNullOrEmpty(request.MdtrtareaAdmvs)) |
|
throw new ValidationException("就医地医保区划不能为空"); |
|
|
|
if (string.IsNullOrEmpty(request.RecerSysCode)) |
|
throw new ValidationException("接收方系统代码不能为空"); |
|
|
|
if (string.IsNullOrEmpty(request.Infver)) |
|
throw new ValidationException("接口版本号不能为空"); |
|
if (string.IsNullOrEmpty(request.OpterType.ToString())) |
|
throw new ValidationException("经办人类别不能为空"); |
|
if (string.IsNullOrEmpty(request.Opter)) |
|
throw new ValidationException("经办人不能为空"); |
|
|
|
if (string.IsNullOrEmpty(request.OpterName)) |
|
throw new ValidationException("经办人姓名不能为空"); |
|
|
|
// 假设 request.InfTime 现在是 DateTime 类型,将其转换为字符串进行判断 |
|
if (string.IsNullOrEmpty(request.InfTime.ToString("yyyyMMddHHmmss"))) |
|
throw new ValidationException("交易时间不能为空"); |
|
if (string.IsNullOrEmpty(request.FixmedinsCode)) |
|
throw new ValidationException("定点医药机构编号不能为空"); |
|
|
|
if (string.IsNullOrEmpty(request.FixmedinsName)) |
|
throw new ValidationException("定点医药机构名称不能为空"); |
|
} |
|
|
|
public IInsuranceBusinessService GetServiceByInfoNo(string infoNo) |
|
{ |
|
return _serviceCache.GetOrAdd(infoNo, key => key switch |
|
{ |
|
// "1101" => new PersonInfoService(), |
|
// "2101" => new RegistrationService(), |
|
// "2201" => new PreSettlementService(), |
|
// _ => throw new BusinessException($"未知的交易编号: {key}") |
|
}); |
|
} |
|
|
|
//入口方法 |
|
public Response ProcessRequest(InsuranceRequest request) |
|
{ |
|
ValidateRequest(request); |
|
ValidateParameterFormat(request); |
|
VerifySignature(request); |
|
|
|
var service = GetServiceByInfoNo(request.Infno); |
|
var result = service.Execute(request); |
|
|
|
|
|
//方法调用路径? |
|
|
|
return new Response |
|
{ |
|
Header = GenerateResponseHeader(request), |
|
Body = EncryptData(result), |
|
Signature = GenerateSignature(result) |
|
}; |
|
} |
|
|
|
// 新增参数格式验证 |
|
public static void ValidateParameterFormat(InsuranceRequest request) |
|
{ |
|
// 增强msgid格式校验 |
|
if (!Regex.IsMatch(request.MsgId, @"^\d{12}\d{14}\d{4}$")) |
|
throw new ValidationException("报文ID格式必须为12位机构编号+14位时间+4位流水号"); |
|
|
|
// 条件必填验证 |
|
if (!string.IsNullOrEmpty(request.Input) && string.IsNullOrEmpty(request.InsuplcAdmdvs)) |
|
throw new ValidationException("当交易输入包含人员编号时,参保地医保区划为必填项"); |
|
|
|
// 版本号格式校验 |
|
if (!Regex.IsMatch(request.Infver, @"^V\d+\.\d+$")) |
|
throw new ValidationException("接口版本号格式应为VX.X"); |
|
|
|
// 签名类型校验 |
|
if (!string.IsNullOrEmpty(request.Signtype) && !new[] { "SM2", "SM3" }.Contains(request.Signtype)) |
|
throw new ValidationException("签名类型只支持SM2/SM3"); |
|
|
|
// 由于 request.InfTime 是 DateTime 类型,先将其转换为指定格式的字符串再进行正则匹配 |
|
if (!Regex.IsMatch(request.InfTime.ToString("yyyyMMddHHmmss"), @"^\d{14}$")) |
|
throw new ValidationException("交易时间格式必须为yyyyMMddHHmmss"); |
|
|
|
if (request.OpterName?.Length > 50) |
|
throw new ValidationException("经办人姓名不能超过50个字符"); |
|
} |
|
|
|
// 增强版响应头生成 |
|
public static ResponseHeader GenerateResponseHeader(InsuranceRequest request) |
|
{ |
|
return new ResponseHeader |
|
{ |
|
Version = "1.0", |
|
// 由于 _config 不存在,这里假设添加一个默认值,实际使用时需要根据业务逻辑修改 |
|
SenderCode = "DefaultInstitutionCode", |
|
ReceiverCode = request.FixmedinsCode, |
|
MsgId = Guid.NewGuid().ToString("N").ToUpper(), |
|
InfTime = DateTime.Now.ToString("yyyyMMddHHmmss"), |
|
ResultCode = "1000", |
|
ResultMsg = "处理成功", |
|
SignatureAlgorithm = "HMAC-SHA256", |
|
EncryptionAlgorithm = "AES-256-GCM" |
|
}; |
|
} |
|
|
|
// 完整签名验证流程 |
|
public static void VerifySignature(InsuranceRequest request) |
|
{ |
|
var rawData = $"{request.MsgId}{request.InfTime}{request.FixmedinsCode}"; |
|
|
|
|
|
var hmac = new HMACSHA256(Encoding.UTF8.GetBytes("DefaultSignKey")); |
|
var computedSignature = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawData)); |
|
|
|
// 由于 InsuranceRequest 未包含 Signature 的定义,这里假设添加一个默认空字符串处理 |
|
if (!computedSignature.SequenceEqual(Convert.FromBase64String(""))) |
|
// 由于 SecurityException 可能未找到,使用更通用的 InvalidOperationException 替代 |
|
throw new InvalidOperationException("数字签名验证失败"); |
|
} |
|
|
|
// 医保系统通信模块 |
|
public async Task<Response> CallInsuranceSystem(string endpoint, object payload) |
|
{ |
|
// 由于 _httpClientFactory 不存在,使用新创建的 HttpClient 实例替代 |
|
using var client = new HttpClient(); |
|
// 由于 _config 不存在,暂时使用一个默认的 API Key,实际使用时需根据业务逻辑修改 |
|
client.DefaultRequestHeaders.Add("X-Medicare-API-Key", "DefaultApiKey"); |
|
|
|
var content = new StringContent( |
|
// 引入 System.Text.Json 命名空间以使用 JsonSerializer |
|
|
|
System.Text.Json.JsonSerializer.Serialize(payload), |
|
Encoding.UTF8, |
|
"application/json"); |
|
|
|
var response = await client.PostAsync(endpoint, content); |
|
response.EnsureSuccessStatusCode(); |
|
|
|
return await response.Content.ReadFromJsonAsync<Response>(); |
|
} |
|
|
|
public static string EncryptData(object data) |
|
{ |
|
var json = JsonSerializer.Serialize(data); |
|
// 由于 AesUtility 不存在,使用 .NET 内置的 AES 加密实现 |
|
|
|
|
|
// 定义一个临时的 AES 加密方法 |
|
static string EncryptWithAes(string plainText, string key) |
|
{ |
|
byte[] encrypted; |
|
using var aesAlg = Aes.Create(); |
|
{ |
|
aesAlg.Key = Encoding.UTF8.GetBytes(key); |
|
aesAlg.GenerateIV(); |
|
|
|
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); |
|
|
|
using (MemoryStream msEncrypt = new MemoryStream()) |
|
{ |
|
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) |
|
{ |
|
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) |
|
{ |
|
swEncrypt.Write(plainText); |
|
} |
|
encrypted = msEncrypt.ToArray(); |
|
} |
|
} |
|
|
|
// 将 IV 和加密后的数据拼接,以方便后续解密 |
|
byte[] combinedIvCt = new byte[aesAlg.IV.Length + encrypted.Length]; |
|
Array.Copy(aesAlg.IV, 0, combinedIvCt, 0, aesAlg.IV.Length); |
|
Array.Copy(encrypted, 0, combinedIvCt, aesAlg.IV.Length, encrypted.Length); |
|
|
|
return Convert.ToBase64String(combinedIvCt); |
|
} |
|
} |
|
|
|
// 由于 _config 不存在,使用一个默认的加密密钥替代,实际使用时需根据业务逻辑修改 |
|
return EncryptWithAes(json, "DefaultEncryptionKey"); |
|
} |
|
|
|
public static string GenerateSignature(object data) |
|
{ |
|
var json = JsonSerializer.Serialize(data); |
|
// 由于 HMACSHA256.HashData 没有单参数重载,需要创建 HMACSHA256 实例来计算哈希值 |
|
using var hmac = new HMACSHA256(); |
|
{ |
|
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(json)); |
|
return Convert.ToBase64String(hashBytes); |
|
} |
|
} |
|
} |
|
}
|
|
|