Compare commits

...

3 Commits
main ... E-Sign

Author SHA1 Message Date
LiJiaWen fcea814670 增加启动时自动初始化逻辑 2 weeks ago
LiJiaWen fc0bf769ad e签宝接口代码 3 weeks ago
LiJiaWen f9e66a8a77 e签宝分支备注 4 weeks ago
  1. 13
      CA-Platform-Linker/.config/dotnet-tools.json
  2. 2
      CA-Platform-Linker/CA-Platform-Linker.csproj
  3. 230
      CA-Platform-Linker/Controllers/ClientController.cs
  4. 173
      CA-Platform-Linker/Controllers/OnlineHospitalController.cs
  5. 78
      CA-Platform-Linker/Models/Coss/CossModels.cs
  6. 80
      CA-Platform-Linker/Models/DTO/ClientDto.cs
  7. 135
      CA-Platform-Linker/Models/DTO/EsignDto.cs
  8. 21
      CA-Platform-Linker/Properties/PublishProfiles/FolderProfile.pubxml
  9. 160
      CA-Platform-Linker/Services/ClientService.cs
  10. 16
      CA-Platform-Linker/Services/Interfaces/IClientService.cs
  11. 14
      CA-Platform-Linker/Services/Interfaces/IOnlineHospitalService.cs
  12. 318
      CA-Platform-Linker/Services/OnlineHospitalService.cs
  13. 47
      CA-Platform-Linker/Startup.cs
  14. 54
      CA-Platform-Linker/Tools/ConfigHelper.cs
  15. 93
      CA-Platform-Linker/Tools/CossHelper.cs
  16. 82
      CA-Platform-Linker/Tools/EsignSignatureHelper.cs
  17. 25
      CA-Platform-Linker/Tools/SdkState.cs
  18. 179
      CA-Platform-Linker/Tools/SqlHelper.cs
  19. 32
      CA-Platform-Linker/appsettings.json
  20. 0
      e签宝连接服务.txt

@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "10.0.11",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.5" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NLog" Version="4.7.15" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.3" />

@ -1,230 +0,0 @@
using CA_Platform_Linker.Models.Coss;
using CA_Platform_Linker.Models.DTO;
using CA_Platform_Linker.Services.Interfaces;
using CA_Platform_Linker.Tools;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class ClientController : ControllerBase
{
private readonly IClientService _clientService;
public ClientController(IClientService clientService)
{
_clientService = clientService;
}
[HttpPost]
public async Task<IActionResult> Login([FromQuery] string clientType = "LIS")
{
try
{
var result = await _clientService.StartAutoSignAsync(clientType, 3600 * 24);
LogHelper.Log.Info(result.ToString());
if (result.status == "200")
{
return Ok(new ApiLoginResponse
{
Success = true,
Message = "二维码生成成功,请扫码登录",
SignDataId = result.data.signDataId,
QrCode = result.data.qrCode
});
}
else
{
return BadRequest(new ApiLoginResponse
{
Success = false,
Message = $"二维码生成失败: {result.message}"
});
}
}
catch (Exception ex)
{
LogHelper.Log.Error($"二维码生成失败: {ex.Message}");
return BadRequest(new ApiLoginResponse
{
Success = false,
Message = $"二维码生成失败: {ex.Message}"
});
}
}
[HttpPost]
public async Task<IActionResult> AddSignJob([FromBody] ApiSignRequest request, [FromQuery] string clientType = "LIS")
{
return BadRequest(new ApiSignResponse
{
Success = false,
Message = "接口已停用,请使用 AutoSign 接口"
});
try
{
var result = await _clientService.AddSignJobAsync(
clientType,
request.Title,
request.Data,
request.UserId,
request.Algo,
request.ExpiryDate
);
if (result.status == "200")
{
return Ok(new ApiSignResponse
{
Success = true,
Message = "添加签名任务成功",
SignDataId = result.data.signDataId,
QrCode = result.data.qrCode
});
}
else
{
return BadRequest(new ApiSignResponse
{
Success = false,
Message = $"添加签名任务失败: {result.message}"
});
}
}
catch (Exception ex)
{
LogHelper.Log.Error($"添加签名任务失败: {ex.Message}");
return BadRequest(new ApiSignResponse
{
Success = false,
Message = $"添加签名任务失败: {ex.Message}"
});
}
}
[HttpPost]
public async Task<IActionResult> AutoSign([FromBody] ApiAutoSignRequest request, [FromQuery] string clientType = "LIS", [FromQuery] int clientOper = 0)
{
try
{
if (clientOper < 0 || clientOper > 99)
{
return BadRequest(new ApiAutoSignResponse
{
Success = false,
Message = "clientOper 参数值必须在 0-99 范围内"
});
}
var result = await _clientService.AutoSignAsync(
clientType,
request.UserId,
request.SignToken,
request.Data,
request.Algo,
request.ExpiryDate,
request.MZNum,
request.ZYNum,
request.SignID,
clientOper
);
if (result.status == "200")
{
return Ok(new ApiAutoSignResponse
{
Success = true,
Message = "自动签名成功",
SignResult = result.data.signResult,
SignCert = result.data.signCert,
SignDataId = result.data.signDataId
});
}
else
{
LogHelper.Log.Error($"自动签名失败: {result.message}");
return BadRequest(new ApiAutoSignResponse
{
Success = false,
Message = $"自动签名失败: {result.message}"
});
}
}
catch (Exception ex)
{
LogHelper.Log.Error($"自动签名失败: {ex.Message}");
return BadRequest(new ApiAutoSignResponse
{
Success = false,
Message = $"自动签名失败: {ex.Message}"
});
}
}
[HttpPost]
public async Task<IActionResult> GetSignResult([FromBody] ApiGetSignResultRequest request, [FromQuery] string clientType = "LIS")
{
try
{
var result = await _clientService.GetSignResultAsync(clientType, request.SignDataId);
if (result.status == "200")
{
string message = "查询中";
bool isLoginSuccess = false;
if (result.data.jobStatus == "FINISH")
{
message = "登录成功";
isLoginSuccess = true;
}
else if (result.data.jobStatus == "PROCESSING")
{
message = "等待扫码";
}
else if (result.data.jobStatus == "FAIL")
{
message = "签名失败";
}
else if (result.data.jobStatus == "TIMEOUT")
{
message = "登录超时";
}
return Ok(new ApiGetSignResultResponse
{
Success = true,
Message = message,
JobStatus = result.data.jobStatus,
SignResult = result.data.signResult,
SignCert = result.data.signCert,
UserId = result.data.msspId,
IsLoginSuccess = isLoginSuccess
});
}
else
{
return BadRequest(new ApiGetSignResultResponse
{
Success = false,
Message = $"获取签名结果失败: {result.message}"
});
}
}
catch (Exception ex)
{
LogHelper.Log.Error($"获取签名结果失败: {ex.Message}");
return BadRequest(new ApiGetSignResultResponse
{
Success = false,
Message = $"获取签名结果失败: {ex.Message}"
});
}
}
}
}

@ -0,0 +1,173 @@
using CA_Platform_Linker.Models.DTO;
using CA_Platform_Linker.Services.Interfaces;
using CA_Platform_Linker.Tools;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class OnlineHospitalController : ControllerBase
{
private readonly IOnlineHospitalService _onlineHospitalService;
public OnlineHospitalController(IOnlineHospitalService onlineHospitalService)
{
_onlineHospitalService = onlineHospitalService;
}
// SDK初始化已改为程序启动后自动执行(Startup中启动后台任务,每30秒循环调用init),不再对外暴露
//[HttpGet]
//public async Task<IActionResult> InitSDK()
//{
// try
// {
// var result = await _onlineHospitalService.InitSDKAsync();
// if (result.errCode == 0)
// {
// return Ok(ApiResponse.Ok(result.msg));
// }
// LogHelper.Log.Error($"初始化SDK失败: {result.msg}");
// return Ok(ApiResponse.Fail(result.msg));
// }
// catch (Exception ex)
// {
// LogHelper.Log.Error($"初始化SDK异常: {ex.Message}");
// return Ok(ApiResponse.Fail($"初始化SDK异常: {ex.Message}"));
// }
//}
[HttpPost]
public async Task<IActionResult> SignWithP7([FromBody] EsignSignRequest request)
{
if (!SdkState.IsInitialized)
{
return Ok(ApiResponse.Fail("SDK尚未初始化成功,请稍后重试"));
}
try
{
if (string.IsNullOrEmpty(request.DoctorCode))
{
return Ok(ApiResponse.Fail("医生编号不能为空"));
}
if (string.IsNullOrEmpty(request.PlainText))
{
return Ok(ApiResponse.Fail("签名数据不能为空"));
}
var result = await _onlineHospitalService.SignWithP7Async(request.DoctorCode, request.PlainText);
if (result.errCode == 0)
{
var data = new
{
signature = result.signature,
algorithm = result.algorithm,
signServiceId = result.signServiceId,
certBean = result.certBean
};
return Ok(ApiResponse.Ok(data, result.msg));
}
LogHelper.Log.Error($"P7数据签名失败: {result.msg}");
return Ok(ApiResponse.Fail(result.msg));
}
catch (Exception ex)
{
LogHelper.Log.Error($"P7数据签名异常: {ex.Message}");
return Ok(ApiResponse.Fail($"P7数据签名异常: {ex.Message}"));
}
}
[HttpPost]
public async Task<IActionResult> VerifyP7([FromBody] EsignVerifyRequest request)
{
if (!SdkState.IsInitialized)
{
return Ok(ApiResponse.Fail("SDK尚未初始化成功,请稍后重试"));
}
try
{
if (string.IsNullOrEmpty(request.PlainText))
{
return Ok(ApiResponse.Fail("签名原文不能为空"));
}
if (string.IsNullOrEmpty(request.Signature))
{
return Ok(ApiResponse.Fail("签名结果不能为空"));
}
var result = await _onlineHospitalService.VerifyP7Async(request.PlainText, request.Signature);
if (result.errCode == 0)
{
return Ok(ApiResponse.Ok(result.certBean, result.msg));
}
LogHelper.Log.Error($"P7数据验签失败: {result.msg}");
return Ok(ApiResponse.Fail(result.msg));
}
catch (Exception ex)
{
LogHelper.Log.Error($"P7数据验签异常: {ex.Message}");
return Ok(ApiResponse.Fail($"P7数据验签异常: {ex.Message}"));
}
}
[HttpPost]
public async Task<IActionResult> CreatePersonAccount([FromBody] EsignCreatePersonRequest request)
{
if (!SdkState.IsInitialized)
{
return Ok(ApiResponse.Fail("SDK尚未初始化成功,请稍后重试"));
}
try
{
if (string.IsNullOrEmpty(request.DoctorCode))
{
return Ok(ApiResponse.Fail("医生编号不能为空"));
}
var result = await _onlineHospitalService.CreatePersonAccountAsync(request.DoctorCode, request.flowId);
if (result.errCode == 0)
{
return Ok(ApiResponse.Ok(new { accountId = result.accountId }, result.msg));
}
LogHelper.Log.Error($"创建个人签署账号失败: {result.msg}");
return Ok(ApiResponse.Fail(result.msg));
}
catch (Exception ex)
{
LogHelper.Log.Error($"创建个人签署账号异常: {ex.Message}");
return Ok(ApiResponse.Fail($"创建个人签署账号异常: {ex.Message}"));
}
}
[HttpPost]
public async Task<IActionResult> GetIndivAuthUrl([FromBody] EsignIndivAuthRequest request)
{
if (!SdkState.IsInitialized)
{
return Ok(ApiResponse.Fail("SDK尚未初始化成功,请稍后重试"));
}
try
{
if (string.IsNullOrEmpty(request.DoctorCode))
{
return Ok(ApiResponse.Fail("医生编号不能为空"));
}
var result = await _onlineHospitalService.GetIndivAuthUrlAsync(request.DoctorCode);
if (result.code == 0)
{
return Ok(ApiResponse.Ok(result.data, result.message));
}
LogHelper.Log.Error($"获取个人核身地址失败: {result.message}");
return Ok(ApiResponse.Fail(result.message));
}
catch (Exception ex)
{
LogHelper.Log.Error($"获取个人核身地址异常: {ex.Message}");
return Ok(ApiResponse.Fail($"获取个人核身地址异常: {ex.Message}"));
}
}
}
}

@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Models.Coss
{
public class CossBaseRequest
{
public string version { get; set; }
public string appId { get; set; }
public string signAlgo { get; set; }
public string signature { get; set; }
}
public class CossResponse<T>
{
public string status { get; set; }
public string message { get; set; }
public T data { get; set; }
}
public class CossResultData
{
public string signDataId { get; set; }
public string qrCode { get; set; }
public string jobStatus { get; set; }
public string msspId { get; set; }
public string signCert { get; set; }
public string signResult { get; set; }
public string image { get; set; }
public string tsResp { get; set; }
}
public class StartAutoSignRequest : CossBaseRequest
{
public string timeRegion { get; set; }
}
public class AddSignJobRequest : CossBaseRequest
{
public string title { get; set; }
public string userId { get; set; }
public string dataType { get; set; }
public string algo { get; set; }
public string description { get; set; }
public string data { get; set; }
public string expiryDate { get; set; }
}
public class AutoSignRequest : CossBaseRequest
{
public string userId { get; set; }
public string title { get; set; }
public string dataType { get; set; }
public string algo { get; set; }
public string description { get; set; }
public string data { get; set; }
public string signToken { get; set; }
public string expiryDate { get; set; }
}
public class GetSignResultRequest : CossBaseRequest
{
public string signDataId { get; set; }
}
public class QueryImageRequest : CossBaseRequest
{
public string userId { get; set; }
}
public class CreateTssRequest : CossBaseRequest
{
public string oriData { get; set; }
public string attachCert { get; set; }
}
}

@ -1,80 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace CA_Platform_Linker.Models.DTO
{
public class ApiLoginResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public string SignDataId { get; set; }
public string QrCode { get; set; }
}
public class ApiSignRequest
{
[Required(ErrorMessage = "标题不能为空")]
public string Title { get; set; }
[Required(ErrorMessage = "数据不能为空")]
public string Data { get; set; }
public string UserId { get; set; }
public string Algo { get; set; } = "SM3withSM2";
public string ExpiryDate { get; set; } = "1400";
}
public class ApiSignResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public string SignDataId { get; set; }
public string QrCode { get; set; }
public string SignResult { get; set; }
public string SignCert { get; set; }
}
public class ApiAutoSignRequest
{
[Required(ErrorMessage = "用户ID不能为空")]
public string UserId { get; set; }
[Required(ErrorMessage = "签名令牌不能为空")]
public string SignToken { get; set; }
[Required(ErrorMessage = "数据不能为空")]
public string Data { get; set; }
public string Algo { get; set; } = "SM3withSM2";
public string ExpiryDate { get; set; } = "1400";
public int MZNum { get; set; } = 0;
public int ZYNum { get; set; } = 0;
public string SignID { get; set; } = string.Empty;
}
public class ApiAutoSignResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public string SignResult { get; set; }
public string SignCert { get; set; }
public string SignDataId { get; set; }
}
public class ApiGetSignResultRequest
{
[Required(ErrorMessage = "签名数据ID不能为空")]
public string SignDataId { get; set; }
}
public class ApiGetSignResultResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public string JobStatus { get; set; }
public string SignResult { get; set; }
public string SignCert { get; set; }
public string UserId { get; set; }
public bool IsLoginSuccess { get; set; }
}
}

@ -0,0 +1,135 @@
using System.ComponentModel.DataAnnotations;
namespace CA_Platform_Linker.Models.DTO
{
/// <summary>
/// 统一API响应模型
/// </summary>
public class ApiResponse<T>
{
public bool Success { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public static ApiResponse<T> Ok(T data, string message = "操作成功")
=> new ApiResponse<T> { Success = true, Message = message, Data = data };
public static ApiResponse<T> Fail(string message)
=> new ApiResponse<T> { Success = false, Message = message };
}
/// <summary>
/// 无数据负载的统一响应
/// </summary>
public class ApiResponse : ApiResponse<object>
{
public static ApiResponse Ok(string message = "操作成功")
=> new ApiResponse { Success = true, Message = message };
public new static ApiResponse Fail(string message)
=> new ApiResponse { Success = false, Message = message };
}
public class EsignSdkResponse
{
public int errCode { get; set; }
public string msg { get; set; }
public bool errShow { get; set; }
}
public class EsignSignRequest
{
[Required(ErrorMessage = "医生编号不能为空")]
public string DoctorCode { get; set; }
[Required(ErrorMessage = "签名数据不能为空")]
public string PlainText { get; set; }
}
public class EsignSdkSignResponse
{
public int errCode { get; set; }
public string msg { get; set; }
public bool errShow { get; set; }
public string signServiceId { get; set; }
public string algorithm { get; set; }
public string signature { get; set; }
public EsignCertBean certBean { get; set; }
}
public class EsignCertBean
{
public string sn { get; set; }
public string cn { get; set; }
public string ou { get; set; }
public string issuerCN { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public string cert { get; set; }
}
public class EsignAccountQueryResponse
{
public int errCode { get; set; }
public string msg { get; set; }
public bool errShow { get; set; }
public string accountId { get; set; }
public string name { get; set; }
public string accountType { get; set; }
public string idNo { get; set; }
public string idNoType { get; set; }
}
public class EsignVerifyRequest
{
[Required(ErrorMessage = "签名原文不能为空")]
public string PlainText { get; set; }
[Required(ErrorMessage = "签名结果不能为空")]
public string Signature { get; set; }
}
public class EsignVerifyResponse
{
public int errCode { get; set; }
public string msg { get; set; }
public bool errShow { get; set; }
public EsignCertBean certBean { get; set; }
}
public class EsignCreatePersonRequest
{
[Required(ErrorMessage = "医生编号不能为空")]
public string DoctorCode { get; set; }
public string flowId { get; set; }
}
public class EsignCreatePersonResponse
{
public int errCode { get; set; }
public string msg { get; set; }
public bool errShow { get; set; }
public string accountId { get; set; }
}
public class EsignIndivAuthRequest
{
[Required(ErrorMessage = "医生编号不能为空")]
public string DoctorCode { get; set; }
}
public class EsignIndivAuthResponse
{
public int code { get; set; }
public string message { get; set; }
public EsignIndivAuthData data { get; set; }
}
public class EsignIndivAuthData
{
public string flowId { get; set; }
public string shortLink { get; set; }
public string url { get; set; }
}
}

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>False</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\netcoreapp3.1\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>4907ff24-c349-4a4e-96c0-e48391f3aaca</ProjectGuid>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

@ -1,160 +0,0 @@
using CA_Platform_Linker.Models;
using CA_Platform_Linker.Models.Coss;
using CA_Platform_Linker.Services.Interfaces;
using CA_Platform_Linker.Tools;
using CA_Platform_Linker.Tools.Nlog;
using System;
using System.Collections;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Services
{
public class ClientService : IClientService
{
public async Task<CossResponse<CossResultData>> StartAutoSignAsync(string clientType, int timeRegion = 3600 * 24)
{
try
{
Hashtable ht = CossHelper.BuildBaseHashtable(clientType);
ht.Add("timeRegion", timeRegion.ToString());
return await CossHelper.PostAsync<CossResultData>("startAutoSign", ht, clientType);
}
catch (Exception ex)
{
LogHelper.Log.Error($"开启自动签名失败: {ex.Message}");
return new CossResponse<CossResultData>
{
status = "500",
message = ex.Message
};
}
}
public async Task<CossResponse<CossResultData>> AddSignJobAsync(string clientType, string title, string data, string userId = "", string algo = "SM3withSM2", string expiryDate = "1400")
{
try
{
string base64Data = data;
if (!string.IsNullOrEmpty(data))
{
base64Data = CossHelper.Base64Encode(data);
}
Hashtable ht = CossHelper.BuildBaseHashtable(clientType);
ht.Add("title", title);
ht.Add("dataType", "DATA");
ht.Add("algo", algo);
ht.Add("description", clientType + "签名任务");
ht.Add("data", base64Data);
ht.Add("expiryDate", expiryDate);
if (!string.IsNullOrEmpty(userId))
{
ht.Add("userId", userId);
}
return await CossHelper.PostAsync<CossResultData>("addSignJob", ht, clientType);
}
catch (Exception ex)
{
LogHelper.Log.Error($"添加签名任务失败: {ex.Message}");
return new CossResponse<CossResultData>
{
status = "500",
message = ex.Message
};
}
}
public async Task<CossResponse<CossResultData>> AutoSignAsync(string clientType, string userId, string signToken, string data, string algo = "SM3withSM2", string expiryDate = "1400", int mzNum = 0, int zyNum = 0, string signID = "", int clientOper = 0)
{
try
{
string base64Data = data;
if (!string.IsNullOrEmpty(data))
{
base64Data = CossHelper.Base64Encode(data);
}
Hashtable ht = CossHelper.BuildBaseHashtable(clientType);
ht.Add("userId", userId);
ht.Add("title", clientType + "自动签名");
ht.Add("dataType", "DATA");
ht.Add("algo", algo);
ht.Add("description", clientType + "自动签名");
ht.Add("data", base64Data);
ht.Add("signToken", signToken);
ht.Add("expiryDate", expiryDate);
var result = await CossHelper.PostAsync<CossResultData>("autoSign", ht, clientType);
if (result.status == "200")
{
_ = Task.Run(() =>
{
try
{
var clientConfig = ConfigHelper.GetCossClientConfig(clientType);
var (operCode, operName) = SqlHelper.GetOperatorInfo(userId);
var signInfo = new SignInfo
{
TextType = clientConfig.TextType + clientOper,
MZNum = mzNum,
ZYNum = zyNum,
SourceText = data,
Base64Text = "",
OperCode = operCode ?? "",
OperName = operName ?? "",
SignDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
CertID = "",
OperCert = result.data.signCert,
SignData = result.data.signResult,
TSData = "",
SignID = signID,
OperType = clientType + "自动签名",
SignWay = 1
};
SqlHelper.InsertSignInfo(signInfo);
LogHelper.Log.Info($"签名记录已写入数据库,SignDataId: {result.data.signDataId}");
}
catch (Exception ex)
{
LogHelper.Log.Error($"写入签名记录失败:{ex.Message}");
}
});
}
return result;
}
catch (Exception ex)
{
LogHelper.Log.Error($"自动签名失败: {ex.Message}");
return new CossResponse<CossResultData>
{
status = "500",
message = ex.Message
};
}
}
public async Task<CossResponse<CossResultData>> GetSignResultAsync(string clientType, string signDataId)
{
try
{
Hashtable ht = CossHelper.BuildBaseHashtable(clientType);
ht.Add("signDataId", signDataId);
return await CossHelper.PostAsync<CossResultData>("getSignResult", ht, clientType);
}
catch (Exception ex)
{
LogHelper.Log.Error($"获取签名结果失败: {ex.Message}");
return new CossResponse<CossResultData>
{
status = "500",
message = ex.Message
};
}
}
}
}

@ -1,16 +0,0 @@
using CA_Platform_Linker.Models.Coss;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Services.Interfaces
{
public interface IClientService
{
Task<CossResponse<CossResultData>> StartAutoSignAsync(string clientType, int timeRegion = 3600 * 24);
Task<CossResponse<CossResultData>> AddSignJobAsync(string clientType, string title, string data, string userId = "", string algo = "SM3withSM2", string expiryDate = "1400");
Task<CossResponse<CossResultData>> AutoSignAsync(string clientType, string userId, string signToken, string data, string algo = "SM3withSM2", string expiryDate = "1400", int mzNum = 0, int zyNum = 0, string signID = "", int clientOper = 0);
Task<CossResponse<CossResultData>> GetSignResultAsync(string clientType, string signDataId);
}
}

@ -0,0 +1,14 @@
using CA_Platform_Linker.Models.DTO;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Services.Interfaces
{
public interface IOnlineHospitalService
{
Task<EsignSdkResponse> InitSDKAsync();
Task<EsignSdkSignResponse> SignWithP7Async(string doctorCode, string plainText);
Task<EsignVerifyResponse> VerifyP7Async(string plainText, string signature);
Task<EsignCreatePersonResponse> CreatePersonAccountAsync(string doctorCode, string flowId);
Task<EsignIndivAuthResponse> GetIndivAuthUrlAsync(string doctorCode);
}
}

@ -0,0 +1,318 @@
using CA_Platform_Linker.Models.DTO;
using CA_Platform_Linker.Services.Interfaces;
using CA_Platform_Linker.Tools;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Services
{
public class OnlineHospitalService : IOnlineHospitalService
{
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(15)
};
private const string SdkInitPath = "/paas-sdk-service/timevale/init";
private const string SdkSignP7Path = "/paas-sdk-service/timevale/user/sign/localDigitalSignWithP7";
private const string SdkVerifyP7Path = "/paas-sdk-service/timevale/user/sign/digitalVerifyWithP7";
private const string SdkCreatePersonPath = "/paas-sdk-service/timevale/account/addPerson";
private const string OpenAuthUrlPath = "/v2/identity/auth/web/indivAuthUrl";
private static string BuildUrl(string path) => ConfigHelper.EsignSdkBaseUrl + path;
private static async Task<string> PostJsonAsync(string url, object body, string logTag)
{
string strJson = JsonConvert.SerializeObject(body);
LogHelper.Log.Info($"{logTag} 请求: {url} | {strJson}");
using (var content = new StringContent(strJson, Encoding.UTF8, "application/json"))
using (var response = await _httpClient.PostAsync(url, content))
{
string responseStr = await response.Content.ReadAsStringAsync();
LogHelper.Log.Info($"{logTag} 响应: {responseStr}");
return responseStr;
}
}
private static string FormatTimeoutMsg(string url, Exception ex)
{
if (ex is TaskCanceledException || ex.InnerException is TaskCanceledException)
{
return $"请求超时,e签宝SDK服务无响应,请检查 {ConfigHelper.EsignSdkBaseUrl} 是否启动且可访问";
}
if (ex is HttpRequestException || ex.InnerException is HttpRequestException)
{
return $"无法连接e签宝SDK服务 {ConfigHelper.EsignSdkBaseUrl},请确认服务已启动且网络可达: {ex.Message}";
}
return ex.Message;
}
public async Task<EsignSdkResponse> InitSDKAsync()
{
try
{
var requestBody = new
{
projectConfig = new
{
projectId = ConfigHelper.EsignProjectId,
projectSecret = ConfigHelper.EsignProjectSecret,
itsmApiUrl = ConfigHelper.EsignItsmApiUrl
},
httpConfig = new
{
httpType = "HTTP",
retry = 5
},
signConfig = new
{
algorithm = "HMACSHA256"
}
};
string responseStr = await PostJsonAsync(BuildUrl(SdkInitPath), requestBody, "初始化SDK");
return JsonConvert.DeserializeObject<EsignSdkResponse>(responseStr);
}
catch (Exception ex)
{
string msg = FormatTimeoutMsg(BuildUrl(SdkInitPath), ex);
LogHelper.Log.Error($"初始化SDK失败: {msg}");
return new EsignSdkResponse
{
errCode = 500,
msg = msg
};
}
}
public async Task<EsignSdkSignResponse> SignWithP7Async(string doctorCode, string plainText)
{
try
{
var (accountId, doctorName) = SqlHelper.GetCaAccountInfo(doctorCode);
if (string.IsNullOrEmpty(accountId))
{
return new EsignSdkSignResponse
{
errCode = 404,
msg = $"未找到医生 {doctorCode} 的CA账号,请先创建签署账号"
};
}
var requestBody = new
{
accountId = accountId,
plainText = plainText
};
string responseStr = await PostJsonAsync(BuildUrl(SdkSignP7Path), requestBody, "P7数据签名");
var result = JsonConvert.DeserializeObject<EsignSdkSignResponse>(responseStr);
if (result.errCode == 0)
{
try
{
SqlHelper.SaveSignRecord(
doctorCode, doctorName, accountId, plainText,
result.signature, result.algorithm, result.signServiceId,
result.certBean?.sn, result.certBean?.cn, result.certBean?.issuerCN,
result.certBean?.startDate, result.certBean?.endDate);
LogHelper.Log.Info($"签名记录已存档: {doctorCode}");
}
catch (Exception dbEx)
{
LogHelper.Log.Error($"签名记录存档失败: {dbEx.Message}");
}
}
return result;
}
catch (Exception ex)
{
string msg = FormatTimeoutMsg(BuildUrl(SdkSignP7Path), ex);
LogHelper.Log.Error($"P7数据签名失败: {msg}");
return new EsignSdkSignResponse
{
errCode = 500,
msg = msg
};
}
}
public async Task<EsignVerifyResponse> VerifyP7Async(string plainText, string signature)
{
try
{
var requestBody = new
{
plainText = plainText,
signature = signature
};
string responseStr = await PostJsonAsync(BuildUrl(SdkVerifyP7Path), requestBody, "P7数据验签");
return JsonConvert.DeserializeObject<EsignVerifyResponse>(responseStr);
}
catch (Exception ex)
{
string msg = FormatTimeoutMsg(BuildUrl(SdkVerifyP7Path), ex);
LogHelper.Log.Error($"P7数据验签失败: {msg}");
return new EsignVerifyResponse
{
errCode = 500,
msg = msg
};
}
}
public async Task<EsignCreatePersonResponse> CreatePersonAccountAsync(string doctorCode, string flowId)
{
try
{
string name;
string personId;
if (doctorCode == "54333")
{
name = "李嘉文";
personId = "230183199612120231";
}
else
{
var (n, p) = SqlHelper.GetDoctorInfo(doctorCode);
name = n;
personId = p;
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(personId))
{
return new EsignCreatePersonResponse
{
errCode = 404,
msg = $"未找到医生 {doctorCode} 的信息,请确认医生编号正确"
};
}
}
var requestBody = new
{
name = name,
idNo = personId,
idNoType = "MAINLAND",
encrypt = false
};
string responseStr = await PostJsonAsync(BuildUrl(SdkCreatePersonPath), requestBody, "创建个人签署账号");
var result = JsonConvert.DeserializeObject<EsignCreatePersonResponse>(responseStr);
if (result.errCode == 0 && !string.IsNullOrEmpty(result.accountId))
{
SqlHelper.SaveCaAccountId(doctorCode, name, result.accountId);
LogHelper.Log.Info($"保存CA账号映射成功: {doctorCode} -> {result.accountId}");
}
return result;
}
catch (Exception ex)
{
string msg = FormatTimeoutMsg(BuildUrl(SdkCreatePersonPath), ex);
LogHelper.Log.Error($"创建个人签署账号失败: {msg}");
return new EsignCreatePersonResponse
{
errCode = 500,
msg = msg
};
}
}
public async Task<EsignIndivAuthResponse> GetIndivAuthUrlAsync(string doctorCode)
{
try
{
string name;
string personId;
if (doctorCode == "54333")
{
name = "李嘉文";
personId = "230183199612120231";
}
else
{
var (n, p) = SqlHelper.GetDoctorInfo(doctorCode);
name = n;
personId = p;
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(personId))
{
return new EsignIndivAuthResponse
{
code = 404,
message = $"未找到医生 {doctorCode} 的信息,请确认医生编号正确"
};
}
}
var requestBody = new
{
indivInfo = new
{
name = name,
certNo = personId,
certType = "INDIVIDUAL_CH_IDCARD"
},
configParams = new
{
indivUneditableInfo = new[] { "name", "certNo", "certType" }
},
contextInfo = new
{
origin = "BROWSER",
showResultPage = true
}
};
string url = ConfigHelper.EsignOpenApiBaseUrl + OpenAuthUrlPath;
string strJson = JsonConvert.SerializeObject(requestBody);
LogHelper.Log.Info($"获取个人核身地址请求: {url} | {strJson}");
// 先创建Content获取实际Content-Type值,确保签名与发送一致
var content = new StringContent(strJson, Encoding.UTF8, "application/json");
string actualContentType = content.Headers.ContentType.ToString();
// e签宝开放平台签名鉴权
var (signature, contentMD5, timestamp) = EsignSignatureHelper.BuildSignature(
"POST", OpenAuthUrlPath, strJson, ConfigHelper.EsignProjectSecret, actualContentType);
content.Headers.Add("Content-MD5", contentMD5);
using (var request = new HttpRequestMessage(HttpMethod.Post, url))
{
request.Headers.Add("X-Tsign-Open-App-Id", ConfigHelper.EsignProjectId);
request.Headers.Add("X-Tsign-Open-Auth-Mode", "Signature");
request.Headers.Add("X-Tsign-Open-Ca-Signature", signature);
request.Headers.Add("X-Tsign-Open-Ca-Timestamp", timestamp);
request.Headers.Add("Accept", "*/*");
request.Content = content;
using (var response = await _httpClient.SendAsync(request))
{
string responseStr = await response.Content.ReadAsStringAsync();
LogHelper.Log.Info($"获取个人核身地址响应: {responseStr}");
return JsonConvert.DeserializeObject<EsignIndivAuthResponse>(responseStr);
}
}
}
catch (Exception ex)
{
string msg = FormatTimeoutMsg(ConfigHelper.EsignOpenApiBaseUrl + OpenAuthUrlPath, ex);
LogHelper.Log.Error($"获取个人核身地址失败: {msg}");
return new EsignIndivAuthResponse
{
code = 500,
message = msg
};
}
}
}
}

@ -37,7 +37,7 @@ namespace CA_Platform_Linker
services.AddControllers();
//注册服务
services.AddTransient<IClientService, ClientService>();
services.AddTransient<IOnlineHospitalService, OnlineHospitalService>();
services.AddSwaggerGen(s =>
{
@ -81,6 +81,51 @@ namespace CA_Platform_Linker
{
endpoints.MapControllers();
});
// 启动后台任务:每30秒重试初始化SDK,初始化成功后结束任务并开放接口调用
StartSdkInitTask(app.ApplicationServices);
}
/// <summary>
/// 启动SDK初始化后台任务:第一次先等30秒再调用init,失败则每30秒重试,成功即结束
/// </summary>
private void StartSdkInitTask(IServiceProvider services)
{
System.Threading.Tasks.Task.Run(async () =>
{
LogHelper.Log.Info("SDK初始化任务已启动,将在30秒后开始第一次调用");
// 第一次进入循环时先等30秒
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(30));
int retryCount = 0;
while (true)
{
retryCount++;
string now = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
LogHelper.Log.Info($"[第{retryCount}次初始化尝试] 开始时间: {now}");
try
{
var service = services.GetRequiredService<IOnlineHospitalService>();
var result = await service.InitSDKAsync();
if (result.errCode == 0)
{
SdkState.SetInitialized(true);
LogHelper.Log.Info($"[第{retryCount}次初始化尝试] 成功,完成时间: {System.DateTime.Now:yyyy-MM-dd HH:mm:ss},已开放接口调用,结束初始化任务");
return;
}
LogHelper.Log.Warn($"[第{retryCount}次初始化尝试] 失败,时间: {now},errCode: {result.errCode},失败原因: {result.msg},将在30秒后重试");
}
catch (System.Exception ex)
{
LogHelper.Log.Error($"[第{retryCount}次初始化尝试] 异常,时间: {now},异常类型: {ex.GetType().Name},异常信息: {ex.Message},将在30秒后重试");
}
LogHelper.Log.Info($"等待30秒后开始第{retryCount + 1}次尝试,预计时间: {System.DateTime.Now.AddSeconds(30):yyyy-MM-dd HH:mm:ss}");
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(30));
}
});
}
}
}

@ -15,54 +15,14 @@ namespace CA_Platform_Linker.Tools
_config = configuration;
}
// LIS数据库连接字符串
public static string ConnectionString => MySecurity.SDecryptString(_config.GetValue<string>("ConnectionString"));
// e签宝PaaS SDK 3.0配置
public static string EsignSdkBaseUrl => _config.GetValue<string>("EsignSdk:BaseUrl", "http://localhost:8080");
public static string EsignProjectId => _config.GetValue<string>("EsignSdk:ProjectId", "");
public static string EsignProjectSecret => _config.GetValue<string>("EsignSdk:ProjectSecret", "");
public static string EsignItsmApiUrl => _config.GetValue<string>("EsignSdk:ItsmApiUrl", "http://smlitsm.tsign.cn:8080/tgmonitor/rest/app!getAPIInfo2");
// 检验平台接口地址
public static string PlatformUrl => _config.GetValue<string>("PlatformUrl");
// e签宝开放平台OpenAPI地址 - 沙箱:https://smlopenapi.esign.cn 正式:https://openapi.esign.cn
public static string EsignOpenApiBaseUrl => _config.GetValue<string>("EsignSdk:OpenApiBaseUrl", "https://smlopenapi.esign.cn");
// 医疗机构代码
public static string HospitalCode => _config.GetValue<string>("HospitalCode");
// 医疗机构名称
public static string HospitalName => _config.GetValue<string>("HospitalName");
// CA服务基础配置
public static string CossBaseUrl => _config.GetValue<string>("CossBaseUrl", "http://192.168.0.148:10201/coss/service/v1/");
public static string CossSignAlgo => _config.GetValue<string>("CossSignAlgo", "HMAC");
public static string CossVersion => _config.GetValue<string>("CossVersion", "1.0");
// 获取指定客户端的配置
public static CossClientConfig GetCossClientConfig(string clientType)
{
var clientsSection = _config.GetSection("CossClients");
var clientSection = clientsSection.GetSection(clientType);
if (!clientSection.Exists())
{
throw new ArgumentException($"未找到客户端类型 '{clientType}' 的配置");
}
return new CossClientConfig
{
AppId = clientSection.GetValue<string>("AppId"),
SecureCode = clientSection.GetValue<string>("SecureCode"),
TextType = clientSection.GetValue<int>("TextType", 0)
};
}
// 获取所有支持的客户端类型
public static List<string> GetSupportedClientTypes()
{
var clientsSection = _config.GetSection("CossClients");
return clientsSection.GetChildren().Select(x => x.Key).ToList();
}
}
public class CossClientConfig
{
public string AppId { get; set; }
public string SecureCode { get; set; }
public int TextType { get; set; }
}
}

@ -1,93 +0,0 @@
using CA_Platform_Linker.Models.Coss;
using CA_Platform_Linker.Tools;
using CA_Platform_Linker.Tools.Nlog;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace CA_Platform_Linker.Tools
{
public class CossHelper
{
private static readonly HttpClient _httpClient = new HttpClient();
public static string GetSignature(string secret, string content)
{
secret = secret ?? "";
var encoding = Encoding.UTF8;
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(content);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
public static string GetSignatureValue(Hashtable ht, string secureCode)
{
string str = "";
ArrayList akeys = new ArrayList(ht.Keys);
akeys.Sort();
foreach (string key in akeys)
{
str += key + "=" + ht[key] + "&";
}
str = str.Substring(0, str.Length - 1);
return GetSignature(secureCode, str);
}
public static Hashtable BuildBaseHashtable(string clientType)
{
var clientConfig = ConfigHelper.GetCossClientConfig(clientType);
Hashtable ht = new Hashtable();
ht.Add("version", ConfigHelper.CossVersion);
ht.Add("appId", clientConfig.AppId);
ht.Add("signAlgo", ConfigHelper.CossSignAlgo);
return ht;
}
public static async Task<CossResponse<T>> PostAsync<T>(string apiName, Hashtable ht, string clientType)
{
try
{
var clientConfig = ConfigHelper.GetCossClientConfig(clientType);
ht.Add("signature", GetSignatureValue(ht, clientConfig.SecureCode));
string strJson = JsonConvert.SerializeObject(ht);
string url = ConfigHelper.CossBaseUrl + apiName;
var content = new StringContent(strJson, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
string responseStr = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<CossResponse<T>>(responseStr);
}
catch (Exception ex)
{
LogHelper.Log.Error($"CA服务调用失败: {ex.Message}");
return new CossResponse<T>
{
status = "500",
message = ex.Message
};
}
}
public static string Base64Encode(string plainText)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
public static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
}
}

@ -0,0 +1,82 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace CA_Platform_Linker.Tools
{
/// <summary>
/// e签宝开放平台请求签名鉴权工具
/// 算法:HmacSHA256
/// </summary>
public static class EsignSignatureHelper
{
/// <summary>
/// 计算请求签名值
/// </summary>
/// <param name="method">HTTP方法(POST/GET),全大写</param>
/// <param name="pathAndParameters">接口路径(含query参数),不含host</param>
/// <param name="body">请求体JSON字符串(GET请求传空字符串)</param>
/// <param name="appSecret">应用密钥AppSecret</param>
/// <param name="contentType">Content-Type值,需与实际发送的完全一致</param>
/// <returns>签名值 + Content-MD5 + 时间戳 的元组</returns>
public static (string signature, string contentMD5, string timestamp) BuildSignature(
string method, string pathAndParameters, string body, string appSecret, string contentType)
{
string accept = "*/*";
string date = "";
string headers = "";
// 1. 计算Content-MD5(Body的MD5后Base64编码)
string contentMD5 = string.IsNullOrEmpty(body) ? "" : ComputeContentMD5(body);
// 2. 拼接待签名字符串
StringBuilder sb = new StringBuilder();
sb.Append(method).Append("\n")
.Append(accept).Append("\n")
.Append(contentMD5).Append("\n")
.Append(contentType).Append("\n")
.Append(date).Append("\n");
// Headers为空时不加\n
sb.Append(headers).Append(pathAndParameters);
string plaintext = sb.ToString();
// 3. 使用AppSecret进行HmacSHA256计算后Base64编码
string signature = HmacSHA256Base64(plaintext, appSecret);
// 4. 毫秒级时间戳
string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
return (signature, contentMD5, timestamp);
}
/// <summary>
/// 计算Content-MD5:MD5摘要后Base64编码
/// </summary>
private static string ComputeContentMD5(string body)
{
using (MD5 md5 = MD5.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(body);
byte[] hash = md5.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
/// <summary>
/// HmacSHA256计算后Base64编码
/// </summary>
private static string HmacSHA256Base64(string message, string secret)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
{
byte[] digestBytes = hmac.ComputeHash(messageBytes);
return Convert.ToBase64String(digestBytes);
}
}
}
}

@ -0,0 +1,25 @@
using System.Threading;
namespace CA_Platform_Linker.Tools
{
/// <summary>
/// SDK初始化状态管理
/// </summary>
public static class SdkState
{
private static int _isInitialized = 0;
/// <summary>
/// SDK是否已初始化成功
/// </summary>
public static bool IsInitialized => _isInitialized == 1;
/// <summary>
/// 标记SDK初始化状态
/// </summary>
public static void SetInitialized(bool initialized)
{
Interlocked.Exchange(ref _isInitialized, initialized ? 1 : 0);
}
}
}

@ -1,5 +1,5 @@
using CA_Platform_Linker.Models;
using Microsoft.Data.SqlClient;
using System.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using System;
@ -14,76 +14,189 @@ namespace CA_Platform_Linker.Tools
_connectionString = configuration.GetConnectionString("HisDB");
}
public static int InsertSignInfo(SignInfo signInfo)
public static (string operCode, string operName) GetOperatorInfo(string bjcaMsspid)
{
if (string.IsNullOrEmpty(_connectionString))
{
throw new InvalidOperationException("数据库连接字符串未配置");
}
string sql = @"INSERT INTO [DigitalSignData].[dbo].[SignInfo]
([TextType], [MZNum], [ZYNum], [SourceText], [Base64Text],
[OperCode], [OperName], [SignDate], [CertID], [OperCert],
[SignData], [TSData], [SignID], [OperType], [SignWay])
VALUES
(@TextType, @MZNum, @ZYNum, @SourceText, @Base64Text,
@OperCode, @OperName, @SignDate, @CertID, @OperCert,
@SignData, @TSData, @SignID, @OperType, @SignWay)";
string sql = @"SELECT [CODE], [NAME]
FROM [HisData].[dbo].[YSCODE]
WHERE [BJCA_MSSPID] = @BJCA_MSSPID";
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@TextType", signInfo.TextType);
cmd.Parameters.AddWithValue("@MZNum", signInfo.MZNum);
cmd.Parameters.AddWithValue("@ZYNum", signInfo.ZYNum);
cmd.Parameters.AddWithValue("@SourceText", (object)signInfo.SourceText ?? DBNull.Value);
cmd.Parameters.AddWithValue("@Base64Text", (object)signInfo.Base64Text ?? DBNull.Value);
cmd.Parameters.AddWithValue("@OperCode", (object)signInfo.OperCode ?? DBNull.Value);
cmd.Parameters.AddWithValue("@OperName", (object)signInfo.OperName ?? DBNull.Value);
cmd.Parameters.AddWithValue("@SignDate", (object)signInfo.SignDate ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertID", (object)signInfo.CertID ?? DBNull.Value);
cmd.Parameters.AddWithValue("@OperCert", (object)signInfo.OperCert ?? DBNull.Value);
cmd.Parameters.AddWithValue("@SignData", (object)signInfo.SignData ?? DBNull.Value);
cmd.Parameters.AddWithValue("@TSData", (object)signInfo.TSData ?? DBNull.Value);
cmd.Parameters.AddWithValue("@SignID", (object)signInfo.SignID ?? DBNull.Value);
cmd.Parameters.AddWithValue("@OperType", (object)signInfo.OperType ?? DBNull.Value);
cmd.Parameters.AddWithValue("@SignWay", signInfo.SignWay);
cmd.Parameters.AddWithValue("@BJCA_MSSPID", bjcaMsspid);
conn.Open();
return cmd.ExecuteNonQuery();
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
return (reader["CODE"].ToString(), reader["NAME"].ToString());
}
}
}
}
public static (string operCode, string operName) GetOperatorInfo(string bjcaMsspid)
return (null, null);
}
public static (string name, string personId) GetDoctorInfo(string doctorCode)
{
if (string.IsNullOrEmpty(_connectionString))
{
throw new InvalidOperationException("数据库连接字符串未配置");
}
string sql = @"SELECT [CODE], [NAME]
FROM [HisData].[dbo].[YSCODE]
WHERE [BJCA_MSSPID] = @BJCA_MSSPID";
string sql = @"SELECT TOP 1 [NAME], [PERSONID]
FROM [hisdata].[dbo].[yscode]
WHERE [CODE] = @CODE";
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@BJCA_MSSPID", bjcaMsspid);
cmd.Parameters.AddWithValue("@CODE", doctorCode);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
return (reader["CODE"].ToString(), reader["NAME"].ToString());
return (reader["NAME"]?.ToString(), reader["PERSONID"]?.ToString());
}
}
}
}
return (null, null);
}
public static string GetCaAccountId(string doctorCode)
{
var (accountId, _) = GetCaAccountInfo(doctorCode);
return accountId;
}
public static (string accountId, string doctorName) GetCaAccountInfo(string doctorCode)
{
if (string.IsNullOrEmpty(_connectionString))
{
throw new InvalidOperationException("数据库连接字符串未配置");
}
string sql = @"SELECT [CAAccountId], [YSName]
FROM [hisdata].[dbo].[ca_yscode]
WHERE [YSCode] = @YSCode";
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@YSCode", doctorCode);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
string accountId = reader["CAAccountId"]?.ToString();
string doctorName = reader["YSName"]?.ToString();
return (accountId, doctorName);
}
}
}
}
return (null, null);
}
public static void SaveCaAccountId(string ySCode, string ySName, string caAccountId)
{
if (string.IsNullOrEmpty(_connectionString))
{
throw new InvalidOperationException("数据库连接字符串未配置");
}
string checkSql = @"SELECT COUNT(1) FROM [hisdata].[dbo].[ca_yscode] WHERE [YSCode] = @YSCode";
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
using (SqlCommand checkCmd = new SqlCommand(checkSql, conn))
{
checkCmd.Parameters.AddWithValue("@YSCode", ySCode);
int count = (int)checkCmd.ExecuteScalar();
if (count > 0)
{
string updateSql = @"UPDATE [hisdata].[dbo].[ca_yscode]
SET [YSName] = @YSName, [CAAccountId] = @CAAccountId
WHERE [YSCode] = @YSCode";
using (SqlCommand cmd = new SqlCommand(updateSql, conn))
{
cmd.Parameters.AddWithValue("@YSName", ySName);
cmd.Parameters.AddWithValue("@CAAccountId", caAccountId);
cmd.Parameters.AddWithValue("@YSCode", ySCode);
cmd.ExecuteNonQuery();
}
}
else
{
string insertSql = @"INSERT INTO [hisdata].[dbo].[ca_yscode]
([YSCode], [YSName], [CAAccountId])
VALUES (@YSCode, @YSName, @CAAccountId)";
using (SqlCommand cmd = new SqlCommand(insertSql, conn))
{
cmd.Parameters.AddWithValue("@YSCode", ySCode);
cmd.Parameters.AddWithValue("@YSName", ySName);
cmd.Parameters.AddWithValue("@CAAccountId", caAccountId);
cmd.ExecuteNonQuery();
}
}
}
}
}
public static void SaveSignRecord(string doctorCode, string doctorName, string accountId, string plainText,
string signature, string algorithm, string signServiceId,
string certSn, string certCn, string certIssuerCN,
string certStartDate, string certEndDate)
{
if (string.IsNullOrEmpty(_connectionString))
{
throw new InvalidOperationException("数据库连接字符串未配置");
}
string sql = @"INSERT INTO [hisdata].[dbo].[ca_sign_record]
([YSCode], [YSName], [CAAccountId], [PlainText], [Signature], [Algorithm],
[SignServiceId], [CertSN], [CertCN], [CertIssuerCN],
[CertStartDate], [CertEndDate], [SignTime])
VALUES (@YSCode, @YSName, @CAAccountId, @PlainText, @Signature, @Algorithm,
@SignServiceId, @CertSN, @CertCN, @CertIssuerCN,
@CertStartDate, @CertEndDate, GETDATE())";
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@YSCode", doctorCode);
cmd.Parameters.AddWithValue("@YSName", (object)doctorName ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CAAccountId", (object)accountId ?? DBNull.Value);
cmd.Parameters.AddWithValue("@PlainText", (object)plainText ?? DBNull.Value);
cmd.Parameters.AddWithValue("@Signature", (object)signature ?? DBNull.Value);
cmd.Parameters.AddWithValue("@Algorithm", (object)algorithm ?? DBNull.Value);
cmd.Parameters.AddWithValue("@SignServiceId", (object)signServiceId ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertSN", (object)certSn ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertCN", (object)certCn ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertIssuerCN", (object)certIssuerCN ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertStartDate", (object)certStartDate ?? DBNull.Value);
cmd.Parameters.AddWithValue("@CertEndDate", (object)certEndDate ?? DBNull.Value);
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
}
}

@ -8,31 +8,13 @@
},
"AllowedHosts": "*",
"ConnectionStrings": {
"HisDB": "Data Source=192.168.0.222;User ID=XBDLisUser;Password=BlueFlag.Lis!@#;TrustServerCertificate=True"
"HisDB": "Data Source=.;User ID=sa;Password=xbdLis!@#77911;TrustServerCertificate=True"
},
"CossBaseUrl": "http://192.168.0.148:10201/coss/service/v1/",
"CossSignAlgo": "HMAC",
"CossVersion": "1.0",
"CossClients": {
"LIS": {
"AppId": "APP_E559E1B8906045D4881D74F29361F6BB",
"SecureCode": "MGI0ODQ0NjYxZjg1NDgzZDhkMDNjYzI5YTM4MDA0ZDk=",
"TextType": 500
},
"PEIS": {
"AppId": "APP_6E254EA9538743088BD393C3D0212617",
"SecureCode": "OTdhMDRkMmY1YzVmNDczY2FlZjhhOWE5NmY1OTI2MzY=",
"TextType": 700
},
"Blood": {
"AppId": "APP_855883154E16433EBE12B8C73822AE17",
"SecureCode": "YjI1YjVkYTQ5OWMwNDYxOTkwY2MwZTUyYTkxMzc2MWU=",
"TextType": 600
},
"WeChat": {
"AppId": "APP_E56656ADD9DE48779F21D9FE50529E3A",
"SecureCode": "OTczNzkyMWNlMzk2NGE3YjgyMTdkMzRkNzBmODllNTk=",
"TextType": 800
}
"EsignSdk": {
"BaseUrl": "http://localhost:26880",
"ProjectId": "7439130864",
"ProjectSecret": "8e41a513af5d6d5267caf7bc4dc9cd25",
"ItsmApiUrl": "http://smlitsm.tsign.cn:8080/tgmonitor/rest/app!getAPIInfo2",
"OpenApiBaseUrl": "https://smlopenapi.esign.cn"
}
}
Loading…
Cancel
Save