Compare commits
3 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
fcea814670 | 2 weeks ago |
|
|
fc0bf769ad | 3 weeks ago |
|
|
f9e66a8a77 | 4 weeks ago |
20 changed files with 988 additions and 861 deletions
@ -0,0 +1,13 @@ |
|||||||
|
{ |
||||||
|
"version": 1, |
||||||
|
"isRoot": true, |
||||||
|
"tools": { |
||||||
|
"dotnet-ef": { |
||||||
|
"version": "10.0.11", |
||||||
|
"commands": [ |
||||||
|
"dotnet-ef" |
||||||
|
], |
||||||
|
"rollForward": false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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,96 +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; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> |
|
||||||
/// 验时间戳返回数据 |
|
||||||
/// </summary> |
|
||||||
public class CossVerifyResultData |
|
||||||
{ |
|
||||||
/// <summary> |
|
||||||
/// 验签结果:1=通过,-1=时间戳验证不通过,-2=原文验证不通过,-3=不是所信任的根,-4=证书未生效,-5=查询不到此证书,-6=服务器证书过期 |
|
||||||
/// </summary> |
|
||||||
public int verifyRes { get; set; } |
|
||||||
public string verifyMsg { get; set; } |
|
||||||
} |
|
||||||
|
|
||||||
public class VerifyTssRequest : CossBaseRequest |
|
||||||
{ |
|
||||||
public string oriData { get; set; } |
|
||||||
public string tsResp { 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; } |
||||||
|
} |
||||||
|
} |
||||||
@ -1,229 +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(async () => |
|
||||||
{ |
|
||||||
string tsData = ""; |
|
||||||
try |
|
||||||
{ |
|
||||||
// 1. 协同加盖时间戳 |
|
||||||
var createTssRes = await CreateTssAsync(clientType, base64Data); |
|
||||||
if (createTssRes.status != "200" || string.IsNullOrEmpty(createTssRes.data?.tsResp)) |
|
||||||
{ |
|
||||||
string errMsg = $"加盖时间戳失败: {createTssRes.message ?? "tsResp为空"}"; |
|
||||||
LogHelper.Log.Error(errMsg); |
|
||||||
return; |
|
||||||
} |
|
||||||
tsData = createTssRes.data.tsResp; |
|
||||||
LogHelper.Log.Info($"加盖时间戳成功,SignDataId: {result.data.signDataId}"); |
|
||||||
|
|
||||||
// 2. 时间戳验签 |
|
||||||
var verifyTssRes = await VerifyTssAsync(clientType, base64Data, tsData); |
|
||||||
if (verifyTssRes.status != "200" || verifyTssRes.data?.verifyRes != 1) |
|
||||||
{ |
|
||||||
string errMsg = $"时间戳验签失败: {verifyTssRes.message ?? verifyTssRes.data?.verifyMsg ?? "未知错误"}"; |
|
||||||
LogHelper.Log.Error(errMsg); |
|
||||||
return; |
|
||||||
} |
|
||||||
LogHelper.Log.Info($"时间戳验签通过,SignDataId: {result.data.signDataId}"); |
|
||||||
|
|
||||||
// 3. 验签通过,写入数据库 |
|
||||||
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 = result.data.msspId ?? "", |
|
||||||
OperCert = result.data.signCert, |
|
||||||
SignData = result.data.signResult, |
|
||||||
TSData = 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 |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> |
|
||||||
/// 协同加盖时间戳(参考Demo: createAndGetTssInfo) |
|
||||||
/// </summary> |
|
||||||
public async Task<CossResponse<CossResultData>> CreateTssAsync(string clientType, string oriData) |
|
||||||
{ |
|
||||||
try |
|
||||||
{ |
|
||||||
Hashtable ht = CossHelper.BuildBaseHashtable(clientType); |
|
||||||
ht.Add("oriData", oriData); |
|
||||||
ht.Add("attachCert", "true"); |
|
||||||
return await CossHelper.PostAsync<CossResultData>("createAndGetTssInfo", ht, clientType); |
|
||||||
} |
|
||||||
catch (Exception ex) |
|
||||||
{ |
|
||||||
LogHelper.Log.Error($"加盖时间戳失败: {ex.Message}"); |
|
||||||
return new CossResponse<CossResultData> |
|
||||||
{ |
|
||||||
status = "500", |
|
||||||
message = ex.Message |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> |
|
||||||
/// 时间戳验签(参考Demo: verifyTS) |
|
||||||
/// </summary> |
|
||||||
public async Task<CossResponse<CossVerifyResultData>> VerifyTssAsync(string clientType, string oriData, string tsResp) |
|
||||||
{ |
|
||||||
try |
|
||||||
{ |
|
||||||
Hashtable ht = CossHelper.BuildBaseHashtable(clientType); |
|
||||||
ht.Add("oriData", oriData); |
|
||||||
ht.Add("tsResp", tsResp); |
|
||||||
return await CossHelper.PostAsync<CossVerifyResultData>("verifyTS", ht, clientType); |
|
||||||
} |
|
||||||
catch (Exception ex) |
|
||||||
{ |
|
||||||
LogHelper.Log.Error($"时间戳验签失败: {ex.Message}"); |
|
||||||
return new CossResponse<CossVerifyResultData> |
|
||||||
{ |
|
||||||
status = "500", |
|
||||||
message = ex.Message |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,26 +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); |
|
||||||
|
|
||||||
/// <summary> |
|
||||||
/// 协同加盖时间戳(参考Demo: createAndGetTssInfo) |
|
||||||
/// </summary> |
|
||||||
Task<CossResponse<CossResultData>> CreateTssAsync(string clientType, string oriData); |
|
||||||
|
|
||||||
/// <summary> |
|
||||||
/// 时间戳验签(参考Demo: verifyTS) |
|
||||||
/// </summary> |
|
||||||
Task<CossResponse<CossVerifyResultData>> VerifyTssAsync(string clientType, string oriData, string tsResp); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -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 |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue