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 _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 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(); } 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); } } } }