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 764 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,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; } |
||||
} |
||||
} |
||||
@ -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 |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -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