From 16294ca92738675d1f7c3e68c9070de2ce48bc33 Mon Sep 17 00:00:00 2001 From: LiJiaWen Date: Thu, 6 Aug 2026 16:13:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目全量提交 --- .gitignore | 54 ++++ CA-Platform-Linker.sln | 25 ++ CA-Platform-Linker/CA-Platform-Linker.csproj | 23 ++ .../Controllers/ClientController.cs | 230 +++++++++++++++ CA-Platform-Linker/Models/Coss/CossModels.cs | 78 +++++ CA-Platform-Linker/Models/DTO/ClientDto.cs | 80 ++++++ CA-Platform-Linker/Models/SignInfo.cs | 24 ++ CA-Platform-Linker/Program.cs | 66 +++++ .../Properties/launchSettings.json | 30 ++ CA-Platform-Linker/README.md | 272 ++++++++++++++++++ CA-Platform-Linker/Services/ClientService.cs | 160 +++++++++++ .../Services/Interfaces/IClientService.cs | 16 ++ CA-Platform-Linker/Startup.cs | 86 ++++++ CA-Platform-Linker/Tools/ConfigHelper.cs | 68 +++++ CA-Platform-Linker/Tools/CossHelper.cs | 93 ++++++ CA-Platform-Linker/Tools/LogHelper.cs | 13 + CA-Platform-Linker/Tools/MySecurity.cs | 253 ++++++++++++++++ .../Nlog/CallsiteNamespaceLayoutRenderer.cs | 30 ++ CA-Platform-Linker/Tools/SqlHelper.cs | 89 ++++++ .../appsettings.Development.json | 9 + CA-Platform-Linker/appsettings.json | 38 +++ CA-Platform-Linker/nlog.config | 77 +++++ 22 files changed, 1814 insertions(+) create mode 100644 .gitignore create mode 100644 CA-Platform-Linker.sln create mode 100644 CA-Platform-Linker/CA-Platform-Linker.csproj create mode 100644 CA-Platform-Linker/Controllers/ClientController.cs create mode 100644 CA-Platform-Linker/Models/Coss/CossModels.cs create mode 100644 CA-Platform-Linker/Models/DTO/ClientDto.cs create mode 100644 CA-Platform-Linker/Models/SignInfo.cs create mode 100644 CA-Platform-Linker/Program.cs create mode 100644 CA-Platform-Linker/Properties/launchSettings.json create mode 100644 CA-Platform-Linker/README.md create mode 100644 CA-Platform-Linker/Services/ClientService.cs create mode 100644 CA-Platform-Linker/Services/Interfaces/IClientService.cs create mode 100644 CA-Platform-Linker/Startup.cs create mode 100644 CA-Platform-Linker/Tools/ConfigHelper.cs create mode 100644 CA-Platform-Linker/Tools/CossHelper.cs create mode 100644 CA-Platform-Linker/Tools/LogHelper.cs create mode 100644 CA-Platform-Linker/Tools/MySecurity.cs create mode 100644 CA-Platform-Linker/Tools/Nlog/CallsiteNamespaceLayoutRenderer.cs create mode 100644 CA-Platform-Linker/Tools/SqlHelper.cs create mode 100644 CA-Platform-Linker/appsettings.Development.json create mode 100644 CA-Platform-Linker/appsettings.json create mode 100644 CA-Platform-Linker/nlog.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c58c90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# 编译输出目录 +[Dd]ebug/ +[Rr]elease/ +bin/ +obj/ + +# .NET 生成文件 +*.cs.user +*.csproj.user +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio +.vs/ +*.swp +*.vspx +*.vspscc +*.vssscc +_ReSharper*/ +*.ReSharper* + +# Rider / JetBrains +.idea/ +*.iml +*.iws +*.xml + +# VS Code +.vscode/ +*.code-workspace + +# 日志 +logs/ +*.log + +# 临时文件 +*.tmp +*.temp + +# 操作系统文件 +.DS_Store +Thumbs.db + +# dotnet tools 本地安装 +.dotnet/ + +# NuGet +*.nupkg +.nuget/ + +# 测试覆盖率 +coverage/ \ No newline at end of file diff --git a/CA-Platform-Linker.sln b/CA-Platform-Linker.sln new file mode 100644 index 0000000..0026c48 --- /dev/null +++ b/CA-Platform-Linker.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.37206.5 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CA-Platform-Linker", "CA-Platform-Linker\CA-Platform-Linker.csproj", "{4907FF24-C349-4A4E-96C0-E48391F3AACA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4907FF24-C349-4A4E-96C0-E48391F3AACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4907FF24-C349-4A4E-96C0-E48391F3AACA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4907FF24-C349-4A4E-96C0-E48391F3AACA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4907FF24-C349-4A4E-96C0-E48391F3AACA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EB187797-AD1E-4986-8418-9A5EF95767D8} + EndGlobalSection +EndGlobal diff --git a/CA-Platform-Linker/CA-Platform-Linker.csproj b/CA-Platform-Linker/CA-Platform-Linker.csproj new file mode 100644 index 0000000..0712f6c --- /dev/null +++ b/CA-Platform-Linker/CA-Platform-Linker.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + CA_Platform_Linker + + + + + + + + + + + + + Always + + + + + diff --git a/CA-Platform-Linker/Controllers/ClientController.cs b/CA-Platform-Linker/Controllers/ClientController.cs new file mode 100644 index 0000000..a2c2504 --- /dev/null +++ b/CA-Platform-Linker/Controllers/ClientController.cs @@ -0,0 +1,230 @@ +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 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 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 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 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}" + }); + } + } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Models/Coss/CossModels.cs b/CA-Platform-Linker/Models/Coss/CossModels.cs new file mode 100644 index 0000000..56a1b87 --- /dev/null +++ b/CA-Platform-Linker/Models/Coss/CossModels.cs @@ -0,0 +1,78 @@ +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 + { + 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; } + } +} diff --git a/CA-Platform-Linker/Models/DTO/ClientDto.cs b/CA-Platform-Linker/Models/DTO/ClientDto.cs new file mode 100644 index 0000000..4b49ef0 --- /dev/null +++ b/CA-Platform-Linker/Models/DTO/ClientDto.cs @@ -0,0 +1,80 @@ +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; } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Models/SignInfo.cs b/CA-Platform-Linker/Models/SignInfo.cs new file mode 100644 index 0000000..73743ec --- /dev/null +++ b/CA-Platform-Linker/Models/SignInfo.cs @@ -0,0 +1,24 @@ +using System; + +namespace CA_Platform_Linker.Models +{ + public class SignInfo + { + public long KeyNo { get; set; } + public int TextType { get; set; } + public int MZNum { get; set; } + public int ZYNum { get; set; } + public string SourceText { get; set; } + public string Base64Text { get; set; } + public string OperCode { get; set; } + public string OperName { get; set; } + public string SignDate { get; set; } + public string CertID { get; set; } + public string OperCert { get; set; } + public string SignData { get; set; } + public string TSData { get; set; } + public string SignID { get; set; } + public string OperType { get; set; } + public int SignWay { get; set; } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Program.cs b/CA-Platform-Linker/Program.cs new file mode 100644 index 0000000..ca6d113 --- /dev/null +++ b/CA-Platform-Linker/Program.cs @@ -0,0 +1,66 @@ +using CA_Platform_Linker.Tools; +using CA_Platform_Linker.Tools.Nlog; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using NLog.LayoutRenderers; +using NLog.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CA_Platform_Linker +{ + public class Program + { + public static void Main(string[] args) + { + #region ע��Nlog�Զ��岼����Ⱦ�� + LayoutRenderer.Register("callsite-namespace"); + #endregion + + //var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); // Nlog�����ļ���ʼ�� + try + { + LogHelper.Log.Info("===========program initialization==============="); + + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + + CreateHostBuilder(args).Build().Run(); + } + catch (Exception exception) + { + // Nlog���������ô��� + LogHelper.Log.Error(exception, "Stopped program because of exception"); + throw; + } + finally + { + // ȷ����Ӧ�ó����˳�ǰˢ�º�ֹͣ�ڲ���ʱ��/�̣߳�����Linux�ϵķֶι��ϣ� + NLog.LogManager.Shutdown(); + } + + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }) + .ConfigureLogging(logging => + { + //logging.ClearProviders(); // ���������������п���̨����� + // ��־���ã�����̨��־���ʱ�� + logging.AddConsole(c => c.TimestampFormat = "��yyyy-MM-dd HH:mm:ss��"); + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace).AddConsole(); // ����̨��־��� + }) + .UseNLog(); //������־�����������ע������Nlog; + } +} diff --git a/CA-Platform-Linker/Properties/launchSettings.json b/CA-Platform-Linker/Properties/launchSettings.json new file mode 100644 index 0000000..11b25fb --- /dev/null +++ b/CA-Platform-Linker/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5000", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "CA_Platform_Linker": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/CA-Platform-Linker/README.md b/CA-Platform-Linker/README.md new file mode 100644 index 0000000..7d08c89 --- /dev/null +++ b/CA-Platform-Linker/README.md @@ -0,0 +1,272 @@ +# CA平台链接器服务 + +这是一个客户端程序和CA服务中间的信息交互服务,提供登录验证、自动签名开启和字符串签名功能。 + +## 功能特性 + +1. **登录验证** - 支持扫码登录 +2. **自动签名开启** - 开启自动签名功能 +3. **字符串签名** - 支持多种签名方式 +4. **Base64编码/解码** - 提供数据转换工具 +5. **时间戳服务** - 创建时间戳 + +## API接口 + +### 1. 登录验证 +**接口**: `POST /Client/Login` + +**请求体**: +```json +{ + "account": "test", + "password": "123456" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "登录成功,请扫码", + "signDataId": "xxx", + "qrCode": "xxx" +} +``` + +### 2. 添加签名任务 +**接口**: `POST /Client/AddSignJob` + +**请求体**: +```json +{ + "title": "测试签名", + "data": "要签名的数据", + "userId": "用户ID(可选)", + "algo": "SM3withSM2", + "expiryDate": "1400" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "添加签名任务成功", + "signDataId": "xxx", + "qrCode": "xxx" +} +``` + +### 3. 自动签名 +**接口**: `POST /Client/AutoSign` + +**请求体**: +```json +{ + "userId": "用户ID", + "signToken": "签名令牌", + "data": "要签名的数据", + "algo": "SM3withSM2", + "expiryDate": "1400" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "自动签名成功", + "signResult": "签名结果", + "signCert": "签名证书", + "signDataId": "xxx" +} +``` + +### 4. 获取签名结果 +**接口**: `POST /Client/GetSignResult` + +**请求体**: +```json +{ + "signDataId": "签名数据ID" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "获取签名结果成功", + "jobStatus": "FINISH", + "signResult": "签名结果", + "signCert": "签名证书", + "userId": "用户ID" +} +``` + +### 5. 查询签名图片 +**接口**: `POST /Client/QueryImage` + +**请求体**: `"用户ID"` + +**响应**: +```json +{ + "success": true, + "message": "查询签名图片成功", + "image": "base64图片数据" +} +``` + +### 6. Base64编码 +**接口**: `POST /Client/Base64Encode` + +**请求体**: +```json +{ + "plainText": "要编码的文本" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "Base64编码成功", + "base64Data": "编码后的数据" +} +``` + +### 7. Base64解码 +**接口**: `POST /Client/Base64Decode` + +**请求体**: +```json +{ + "base64Data": "要解码的Base64数据" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "Base64解码成功", + "plainText": "解码后的文本" +} +``` + +### 8. 创建时间戳 +**接口**: `POST /Client/CreateTss` + +**请求体**: +```json +{ + "oriData": "原文数据", + "attachCert": true +} +``` + +**响应**: +```json +{ + "success": true, + "message": "创建时间戳成功", + "tsResp": "时间戳响应" +} +``` + +## 配置说明 + +在 `appsettings.json` 中配置CA服务相关参数: + +```json +{ + "CossBaseUrl": "https://newcoss-dev.isignet.cn:10201/coss/service/v1/", + "CossAppId": "APP_7B3F36A14E99410A80B37AEF332E3247", + "CossSecureCode": "DLwiH46Esb8ccNTkuSSVAadNTWUfW0sc", + "CossSignAlgo": "HMAC", + "CossVersion": "1.0" +} +``` + +## 使用示例 + +### 1. 启动服务 +```bash +dotnet run +``` + +### 2. 访问Swagger文档 +打开浏览器访问: `http://localhost:5000/LISPlatformLinker/swagger` + +### 3. 调用API示例 + +#### 登录并获取二维码 +```bash +curl -X POST "http://localhost:5000/LISPlatformLinker/Client/Login" \ + -H "Content-Type: application/json" \ + -d '{"account":"test","password":"123456"}' +``` + +#### 添加签名任务 +```bash +curl -X POST "http://localhost:5000/LISPlatformLinker/Client/AddSignJob" \ + -H "Content-Type: application/json" \ + -d '{"title":"测试签名","data":"Hello World","algo":"SM3withSM2"}' +``` + +#### 自动签名 +```bash +curl -X POST "http://localhost:5000/LISPlatformLinker/Client/AutoSign" \ + -H "Content-Type: application/json" \ + -d '{"userId":"xxx","signToken":"xxx","data":"Hello World"}' +``` + +## 项目结构 + +``` +CA-Platform-Linker/ +├── Controllers/ +│ └── ClientController.cs # 客户端控制器 +├── Models/ +│ ├── Coss/ +│ │ └── CossModels.cs # CA服务模型 +│ └── DTO/ +│ └── ClientDto.cs # 客户端DTO +├── Services/ +│ ├── Interfaces/ +│ │ └── IClientService.cs # 客户端服务接口 +│ └── ClientService.cs # 客户端服务实现 +├── Tools/ +│ ├── CossHelper.cs # CA服务帮助类 +│ ├── ConfigHelper.cs # 配置帮助类 +│ └── MySecurity.cs # 安全类 +└── appsettings.json # 配置文件 +``` + +## 注意事项 + +1. 所有接口都使用POST方法 +2. 请求和响应都使用JSON格式 +3. 签名数据会自动进行Base64编码 +4. 需要正确配置CA服务的连接参数 +5. 建议在生产环境中使用HTTPS协议 + +## 错误处理 + +所有接口都包含错误处理,当请求失败时会返回错误信息: + +```json +{ + "success": false, + "message": "错误描述" +} +``` + +## 日志 + +使用NLog进行日志记录,日志文件位于 `logs/` 目录下: +- `logs/info/` - 信息日志 +- `logs/error/` - 错误日志 +- `logs/debug/` - 调试日志 diff --git a/CA-Platform-Linker/Services/ClientService.cs b/CA-Platform-Linker/Services/ClientService.cs new file mode 100644 index 0000000..641b995 --- /dev/null +++ b/CA-Platform-Linker/Services/ClientService.cs @@ -0,0 +1,160 @@ +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> StartAutoSignAsync(string clientType, int timeRegion = 3600 * 24) + { + try + { + Hashtable ht = CossHelper.BuildBaseHashtable(clientType); + ht.Add("timeRegion", timeRegion.ToString()); + return await CossHelper.PostAsync("startAutoSign", ht, clientType); + } + catch (Exception ex) + { + LogHelper.Log.Error($"开启自动签名失败: {ex.Message}"); + return new CossResponse + { + status = "500", + message = ex.Message + }; + } + } + + public async Task> 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("addSignJob", ht, clientType); + } + catch (Exception ex) + { + LogHelper.Log.Error($"添加签名任务失败: {ex.Message}"); + return new CossResponse + { + status = "500", + message = ex.Message + }; + } + } + + public async Task> 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("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 + { + status = "500", + message = ex.Message + }; + } + } + + public async Task> GetSignResultAsync(string clientType, string signDataId) + { + try + { + Hashtable ht = CossHelper.BuildBaseHashtable(clientType); + ht.Add("signDataId", signDataId); + return await CossHelper.PostAsync("getSignResult", ht, clientType); + } + catch (Exception ex) + { + LogHelper.Log.Error($"获取签名结果失败: {ex.Message}"); + return new CossResponse + { + status = "500", + message = ex.Message + }; + } + } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Services/Interfaces/IClientService.cs b/CA-Platform-Linker/Services/Interfaces/IClientService.cs new file mode 100644 index 0000000..8f606cf --- /dev/null +++ b/CA-Platform-Linker/Services/Interfaces/IClientService.cs @@ -0,0 +1,16 @@ +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> StartAutoSignAsync(string clientType, int timeRegion = 3600 * 24); + Task> AddSignJobAsync(string clientType, string title, string data, string userId = "", string algo = "SM3withSM2", string expiryDate = "1400"); + Task> 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> GetSignResultAsync(string clientType, string signDataId); + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Startup.cs b/CA-Platform-Linker/Startup.cs new file mode 100644 index 0000000..18343ba --- /dev/null +++ b/CA-Platform-Linker/Startup.cs @@ -0,0 +1,86 @@ +using CA_Platform_Linker.Services; +using CA_Platform_Linker.Services.Interfaces; +using CA_Platform_Linker.Tools; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CA_Platform_Linker +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + //读取配置 + ConfigHelper.Initialize(Configuration); + + //初始化数据库连接 + SqlHelper.Initialize(Configuration); + + services.AddControllers(); + + //注册服务 + services.AddTransient(); + + services.AddSwaggerGen(s => + { + //标记swagger 信息(内容自定义) + //V1.0.0-->SwaggerEndpoint(url,name)-->url 使用 + s.SwaggerDoc("V1.0.0", new Microsoft.OpenApi.Models.OpenApiInfo + { + Title = "CA平台连接服务", + Version = "1.0.0", + }); + s.AddServer(new OpenApiServer + { + Url = "/CAPlatformLinker", + Description = "API全局路由" + }); + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UsePathBase("/CAPlatformLinker"); + + app.UseSwagger(); + app.UseSwaggerUI(su => + { + //url中[V1.0.0]与ConfigureServices 中配置的SwaggerDoc("V1.0.0",..) 保持一致 + su.SwaggerEndpoint("/swagger/V1.0.0/swagger.json", null); + }); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/CA-Platform-Linker/Tools/ConfigHelper.cs b/CA-Platform-Linker/Tools/ConfigHelper.cs new file mode 100644 index 0000000..9eacaec --- /dev/null +++ b/CA-Platform-Linker/Tools/ConfigHelper.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CA_Platform_Linker.Tools +{ + public static class ConfigHelper + { + private static IConfiguration _config; + + public static void Initialize(IConfiguration configuration) + { + _config = configuration; + } + + // LIS数据库连接字符串 + public static string ConnectionString => MySecurity.SDecryptString(_config.GetValue("ConnectionString")); + + // 检验平台接口地址 + public static string PlatformUrl => _config.GetValue("PlatformUrl"); + + // 医疗机构代码 + public static string HospitalCode => _config.GetValue("HospitalCode"); + + // 医疗机构名称 + public static string HospitalName => _config.GetValue("HospitalName"); + + // CA服务基础配置 + public static string CossBaseUrl => _config.GetValue("CossBaseUrl", "http://192.168.0.148:10201/coss/service/v1/"); + public static string CossSignAlgo => _config.GetValue("CossSignAlgo", "HMAC"); + public static string CossVersion => _config.GetValue("CossVersion", "1.0"); + + // 获取指定客户端的配置 + public static CossClientConfig GetCossClientConfig(string clientType) + { + var clientsSection = _config.GetSection("CossClients"); + var clientSection = clientsSection.GetSection(clientType); + + if (!clientSection.Exists()) + { + throw new ArgumentException($"未找到客户端类型 '{clientType}' 的配置"); + } + + return new CossClientConfig + { + AppId = clientSection.GetValue("AppId"), + SecureCode = clientSection.GetValue("SecureCode"), + TextType = clientSection.GetValue("TextType", 0) + }; + } + + // 获取所有支持的客户端类型 + public static List GetSupportedClientTypes() + { + var clientsSection = _config.GetSection("CossClients"); + return clientsSection.GetChildren().Select(x => x.Key).ToList(); + } + } + + public class CossClientConfig + { + public string AppId { get; set; } + public string SecureCode { get; set; } + public int TextType { get; set; } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Tools/CossHelper.cs b/CA-Platform-Linker/Tools/CossHelper.cs new file mode 100644 index 0000000..652b663 --- /dev/null +++ b/CA-Platform-Linker/Tools/CossHelper.cs @@ -0,0 +1,93 @@ +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> PostAsync(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>(responseStr); + } + catch (Exception ex) + { + LogHelper.Log.Error($"CA服务调用失败: {ex.Message}"); + return new CossResponse + { + 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); + } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/Tools/LogHelper.cs b/CA-Platform-Linker/Tools/LogHelper.cs new file mode 100644 index 0000000..18718a3 --- /dev/null +++ b/CA-Platform-Linker/Tools/LogHelper.cs @@ -0,0 +1,13 @@ +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CA_Platform_Linker.Tools +{ + public class LogHelper + { + public static readonly NLog.Logger Log = LogManager.GetCurrentClassLogger(); + } +} diff --git a/CA-Platform-Linker/Tools/MySecurity.cs b/CA-Platform-Linker/Tools/MySecurity.cs new file mode 100644 index 0000000..099a2fa --- /dev/null +++ b/CA-Platform-Linker/Tools/MySecurity.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace CA_Platform_Linker.Tools +{ + /// + /// MySecurity(安全类) 的摘要说明。 + /// + public class MySecurity + { + /// + /// 初始化安全类 + /// + public MySecurity() + { + ///默认密码 + key = "peis77911@*71"; + } + + private string key; //默认密钥 + + private byte[] sKey; + private byte[] sIV; + + #region 加密字符串 + + /// + /// 加密字符串 + /// + /// 输入字符串 + /// 密码,可以为“” + /// 输出加密后字符串 + static public string SEncryptString(string inputStr, string keyStr) + { + MySecurity ws = new MySecurity(); + return ws.EncryptString(inputStr, keyStr); + } + + /// + /// 加密字符串 + /// + /// 输入字符串 + /// 密码,可以为“” + /// 输出加密后字符串 + public string EncryptString(string inputStr, string keyStr) + { + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + if (keyStr == "") + keyStr = key; + byte[] inputByteArray = Encoding.Default.GetBytes(inputStr); + byte[] keyByteArray = Encoding.Default.GetBytes(keyStr); + SHA1 ha = new SHA1Managed(); + byte[] hb = ha.ComputeHash(keyByteArray); + sKey = new byte[8]; + sIV = new byte[8]; + for (int i = 0; i < 8; i++) + sKey[i] = hb[i]; + for (int i = 8; i < 16; i++) + sIV[i - 8] = hb[i]; + des.Key = sKey; + des.IV = sIV; + MemoryStream ms = new MemoryStream(); + CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + StringBuilder ret = new StringBuilder(); + foreach (byte b in ms.ToArray()) + { + ret.AppendFormat("{0:X2}", b); + } + cs.Close(); + ms.Close(); + return ret.ToString(); + } + + #endregion 加密字符串 + + #region 加密字符串 密钥为系统默认 0123456789 + + /// + /// 加密字符串 密钥为系统默认 + /// + /// 输入字符串 + /// 输出加密后字符串 + static public string SEncryptString(string inputStr) + { + MySecurity ws = new MySecurity(); + return ws.EncryptString(inputStr, ""); + } + + #endregion 加密字符串 密钥为系统默认 0123456789 + + #region 加密文件 + + /// + /// 加密文件 + /// + /// 输入文件路径 + /// 加密后输出文件路径 + /// 密码,可以为“” + /// + public bool EncryptFile(string filePath, string savePath, string keyStr) + { + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + if (keyStr == "") + keyStr = key; + FileStream fs = File.OpenRead(filePath); + byte[] inputByteArray = new byte[fs.Length]; + fs.Read(inputByteArray, 0, (int)fs.Length); + fs.Close(); + byte[] keyByteArray = Encoding.Default.GetBytes(keyStr); + SHA1 ha = new SHA1Managed(); + byte[] hb = ha.ComputeHash(keyByteArray); + sKey = new byte[8]; + sIV = new byte[8]; + for (int i = 0; i < 8; i++) + sKey[i] = hb[i]; + for (int i = 8; i < 16; i++) + sIV[i - 8] = hb[i]; + des.Key = sKey; + des.IV = sIV; + MemoryStream ms = new MemoryStream(); + CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + fs = File.OpenWrite(savePath); + foreach (byte b in ms.ToArray()) + { + fs.WriteByte(b); + } + fs.Close(); + cs.Close(); + ms.Close(); + return true; + } + + #endregion 加密文件 + + #region 解密字符串 + + /// + /// 解密字符串 + /// + /// 要解密的字符串 + /// 密钥 + /// 解密后的结果 + static public string SDecryptString(string inputStr, string keyStr) + { + MySecurity ws = new MySecurity(); + return ws.DecryptString(inputStr, keyStr); + } + + /// + /// 解密字符串 密钥为系统默认 + /// + /// 要解密的字符串 + /// 解密后的结果 + static public string SDecryptString(string inputStr) + { + MySecurity ws = new MySecurity(); + return ws.DecryptString(inputStr, ""); + } + + /// + /// 解密字符串 + /// + /// 要解密的字符串 + /// 密钥 + /// 解密后的结果 + public string DecryptString(string inputStr, string keyStr) + { + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + if (keyStr == "") + keyStr = key; + byte[] inputByteArray = new byte[inputStr.Length / 2]; + for (int x = 0; x < inputStr.Length / 2; x++) + { + int i = (Convert.ToInt32(inputStr.Substring(x * 2, 2), 16)); + inputByteArray[x] = (byte)i; + } + byte[] keyByteArray = Encoding.Default.GetBytes(keyStr); + SHA1 ha = new SHA1Managed(); + byte[] hb = ha.ComputeHash(keyByteArray); + sKey = new byte[8]; + sIV = new byte[8]; + for (int i = 0; i < 8; i++) + sKey[i] = hb[i]; + for (int i = 8; i < 16; i++) + sIV[i - 8] = hb[i]; + des.Key = sKey; + des.IV = sIV; + MemoryStream ms = new MemoryStream(); + CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + StringBuilder ret = new StringBuilder(); + return System.Text.Encoding.Default.GetString(ms.ToArray()); + } + + #endregion 解密字符串 + + #region 解密文件 + + /// + /// 解密文件 + /// + /// 输入文件路径 + /// 解密后输出文件路径 + /// 密码,可以为“” + /// + public bool DecryptFile(string filePath, string savePath, string keyStr) + { + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + if (keyStr == "") + keyStr = key; + FileStream fs = File.OpenRead(filePath); + byte[] inputByteArray = new byte[fs.Length]; + fs.Read(inputByteArray, 0, (int)fs.Length); + fs.Close(); + byte[] keyByteArray = Encoding.Default.GetBytes(keyStr); + SHA1 ha = new SHA1Managed(); + byte[] hb = ha.ComputeHash(keyByteArray); + sKey = new byte[8]; + sIV = new byte[8]; + for (int i = 0; i < 8; i++) + sKey[i] = hb[i]; + for (int i = 8; i < 16; i++) + sIV[i - 8] = hb[i]; + des.Key = sKey; + des.IV = sIV; + MemoryStream ms = new MemoryStream(); + CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + fs = File.OpenWrite(savePath); + foreach (byte b in ms.ToArray()) + { + fs.WriteByte(b); + } + fs.Close(); + cs.Close(); + ms.Close(); + return true; + } + + #endregion 解密文件 + } +} diff --git a/CA-Platform-Linker/Tools/Nlog/CallsiteNamespaceLayoutRenderer.cs b/CA-Platform-Linker/Tools/Nlog/CallsiteNamespaceLayoutRenderer.cs new file mode 100644 index 0000000..ddc9c14 --- /dev/null +++ b/CA-Platform-Linker/Tools/Nlog/CallsiteNamespaceLayoutRenderer.cs @@ -0,0 +1,30 @@ +using NLog; +using NLog.LayoutRenderers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CA_Platform_Linker.Tools.Nlog +{ + /// + /// Nlog 日志调用方命名空间名称 + /// + [LayoutRenderer("callsite-namespace")] + public class CallsiteNamespaceLayoutRenderer : LayoutRenderer + { + protected override void Append(StringBuilder builder, LogEventInfo logEvent) + { + if (logEvent != null && !string.IsNullOrEmpty(logEvent.CallerClassName) && logEvent.CallerClassName.Contains(".")) + { + // 获取命名空间(如果类型在命名空间中) + var namespaceName = logEvent.CallerClassName.ToString().Split(".")[0]; + if (!string.IsNullOrEmpty(namespaceName)) + { + builder.Append(namespaceName); + } + } + } + } +} diff --git a/CA-Platform-Linker/Tools/SqlHelper.cs b/CA-Platform-Linker/Tools/SqlHelper.cs new file mode 100644 index 0000000..a8fbcb0 --- /dev/null +++ b/CA-Platform-Linker/Tools/SqlHelper.cs @@ -0,0 +1,89 @@ +using CA_Platform_Linker.Models; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; +using System; + +namespace CA_Platform_Linker.Tools +{ + public static class SqlHelper + { + private static string _connectionString; + + public static void Initialize(IConfiguration configuration) + { + _connectionString = configuration.GetConnectionString("HisDB"); + } + + public static int InsertSignInfo(SignInfo signInfo) + { + if (string.IsNullOrEmpty(_connectionString)) + { + throw new InvalidOperationException("数据库连接字符串未配置"); + } + + string sql = @"INSERT INTO [DigitalSignData].[dbo].[SignInfo] + ([TextType], [MZNum], [ZYNum], [SourceText], [Base64Text], + [OperCode], [OperName], [SignDate], [CertID], [OperCert], + [SignData], [TSData], [SignID], [OperType], [SignWay]) + VALUES + (@TextType, @MZNum, @ZYNum, @SourceText, @Base64Text, + @OperCode, @OperName, @SignDate, @CertID, @OperCert, + @SignData, @TSData, @SignID, @OperType, @SignWay)"; + + using (SqlConnection conn = new SqlConnection(_connectionString)) + { + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("@TextType", signInfo.TextType); + cmd.Parameters.AddWithValue("@MZNum", signInfo.MZNum); + cmd.Parameters.AddWithValue("@ZYNum", signInfo.ZYNum); + cmd.Parameters.AddWithValue("@SourceText", (object)signInfo.SourceText ?? DBNull.Value); + cmd.Parameters.AddWithValue("@Base64Text", (object)signInfo.Base64Text ?? DBNull.Value); + cmd.Parameters.AddWithValue("@OperCode", (object)signInfo.OperCode ?? DBNull.Value); + cmd.Parameters.AddWithValue("@OperName", (object)signInfo.OperName ?? DBNull.Value); + cmd.Parameters.AddWithValue("@SignDate", (object)signInfo.SignDate ?? DBNull.Value); + cmd.Parameters.AddWithValue("@CertID", (object)signInfo.CertID ?? DBNull.Value); + cmd.Parameters.AddWithValue("@OperCert", (object)signInfo.OperCert ?? DBNull.Value); + cmd.Parameters.AddWithValue("@SignData", (object)signInfo.SignData ?? DBNull.Value); + cmd.Parameters.AddWithValue("@TSData", (object)signInfo.TSData ?? DBNull.Value); + cmd.Parameters.AddWithValue("@SignID", (object)signInfo.SignID ?? DBNull.Value); + cmd.Parameters.AddWithValue("@OperType", (object)signInfo.OperType ?? DBNull.Value); + cmd.Parameters.AddWithValue("@SignWay", signInfo.SignWay); + + conn.Open(); + return cmd.ExecuteNonQuery(); + } + } + } + + public static (string operCode, string operName) GetOperatorInfo(string bjcaMsspid) + { + if (string.IsNullOrEmpty(_connectionString)) + { + throw new InvalidOperationException("数据库连接字符串未配置"); + } + + string sql = @"SELECT [CODE], [NAME] + FROM [HisData].[dbo].[YSCODE] + WHERE [BJCA_MSSPID] = @BJCA_MSSPID"; + + using (SqlConnection conn = new SqlConnection(_connectionString)) + { + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("@BJCA_MSSPID", bjcaMsspid); + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + return (reader["CODE"].ToString(), reader["NAME"].ToString()); + } + } + } + } + + return (null, null); + } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/appsettings.Development.json b/CA-Platform-Linker/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/CA-Platform-Linker/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/CA-Platform-Linker/appsettings.json b/CA-Platform-Linker/appsettings.json new file mode 100644 index 0000000..00fbcb8 --- /dev/null +++ b/CA-Platform-Linker/appsettings.json @@ -0,0 +1,38 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "HisDB": "Data Source=192.168.0.222;User ID=XBDLisUser;Password=BlueFlag.Lis!@#;TrustServerCertificate=True" + }, + "CossBaseUrl": "http://192.168.0.148:10201/coss/service/v1/", + "CossSignAlgo": "HMAC", + "CossVersion": "1.0", + "CossClients": { + "LIS": { + "AppId": "APP_E559E1B8906045D4881D74F29361F6BB", + "SecureCode": "MGI0ODQ0NjYxZjg1NDgzZDhkMDNjYzI5YTM4MDA0ZDk=", + "TextType": 500 + }, + "PEIS": { + "AppId": "APP_6E254EA9538743088BD393C3D0212617", + "SecureCode": "OTdhMDRkMmY1YzVmNDczY2FlZjhhOWE5NmY1OTI2MzY=", + "TextType": 700 + }, + "Blood": { + "AppId": "APP_855883154E16433EBE12B8C73822AE17", + "SecureCode": "YjI1YjVkYTQ5OWMwNDYxOTkwY2MwZTUyYTkxMzc2MWU=", + "TextType": 600 + }, + "WeChat": { + "AppId": "APP_E56656ADD9DE48779F21D9FE50529E3A", + "SecureCode": "OTczNzkyMWNlMzk2NGE3YjgyMTdkMzRkNzBmODllNTk=", + "TextType": 800 + } + } +} \ No newline at end of file diff --git a/CA-Platform-Linker/nlog.config b/CA-Platform-Linker/nlog.config new file mode 100644 index 0000000..adde53d --- /dev/null +++ b/CA-Platform-Linker/nlog.config @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file