diff --git a/.gitignore b/.gitignore index d9ddf09a..0f2a2af1 100644 --- a/.gitignore +++ b/.gitignore @@ -226,3 +226,4 @@ logfile /.idea /.idea/.idea.YLerpbase/.idea /NuGet.Config +/.trae diff --git a/Framework/YLErp.Core/DBModels/ClientCashInCashOut.cs b/Framework/YLErp.Core/DBModels/ClientCashInCashOut.cs index 0158db1c..edb11156 100644 --- a/Framework/YLErp.Core/DBModels/ClientCashInCashOut.cs +++ b/Framework/YLErp.Core/DBModels/ClientCashInCashOut.cs @@ -109,6 +109,12 @@ namespace YLErp.DBModels [DisplayName("流水号")] public string SerialNumber { get; set; } + /// + /// 银行流水ID + /// + [DisplayName("银行流水ID")] + public string BankFlowId { get; set; } + [DisplayName("账户余额")] public double? AccountBalance { get; set; } @@ -255,6 +261,7 @@ namespace YLErp.DBModels public const string 系统操作_预付金返息 = "系统操作-预付金返息"; public const string 人工操作_其他 = "人工操作-其他"; public const string 人工操作_预付金 = "人工操作-预付金"; + public const string 系统 = "系统"; /// /// all action diff --git a/Framework/YLErp.Resources/DbUpdate/Ver-5.5.0/prod.sql b/Framework/YLErp.Resources/DbUpdate/Ver-5.5.0/prod.sql index 3e46a9e4..9b92f923 100644 --- a/Framework/YLErp.Resources/DbUpdate/Ver-5.5.0/prod.sql +++ b/Framework/YLErp.Resources/DbUpdate/Ver-5.5.0/prod.sql @@ -1,5 +1,7 @@ SET FOREIGN_KEY_CHECKS=0; +ALTER TABLE `ClientCashInCashOut` ADD COLUMN `BankFlowId` varchar(100) NULL DEFAULT NULL COMMENT '银行流水ID' AFTER `SerialNumber`; + CREATE TABLE `client_variety_span_cfg` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键Id', `ValueDate` date NOT NULL COMMENT '生效日期', diff --git a/YLErpDAL/Model/ExternalCashFlow/ExternalCashFlowModels.cs b/YLErpDAL/Model/ExternalCashFlow/ExternalCashFlowModels.cs new file mode 100644 index 00000000..cb7c2466 --- /dev/null +++ b/YLErpDAL/Model/ExternalCashFlow/ExternalCashFlowModels.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; + +namespace YLErp.Model.ExternalCashFlow +{ + /// + /// 外部资金流水接口响应模型 + /// + public class ExternalCashFlowResponse + { + /// + /// 响应码 + /// + public string code { get; set; } + + /// + /// 响应消息 + /// + public string message { get; set; } + + /// + /// 响应数据 + /// + public List data { get; set; } + } + + /// + /// 外部资金流水数据模型 + /// + public class ExternalCashFlowData + { + /// + /// 流水 ID + /// + public string flowId { get; set; } + + /// + /// 账号 + /// + public string account { get; set; } + + /// + /// 金额 + /// + public string amount { get; set; } + + /// + /// 户名 + /// + public string name { get; set; } + + /// + /// 银行名称 + /// + public string bankName { get; set; } + + /// + /// 币种 + /// + public string currency { get; set; } + + /// + /// 入账日期(可能为 null) + /// + public string entryDate { get; set; } + + /// + /// 交易时间 + /// + public string transTime { get; set; } + + /// + /// 借贷标识:D-借,C-贷 + /// + public string debitCreditMark { get; set; } + + /// + /// 用途描述(可能为 null) + /// + public string useDescribe { get; set; } + + /// + /// 补充信息(可能为 null) + /// + public string supplementaryDetails { get; set; } + + /// + /// 对方账户 + /// + public string counterpartyAccount { get; set; } + + /// + /// 对方户名 + /// + public string counterpartyName { get; set; } + + /// + /// 对方银行名称 + /// + public string counterpartyBankName { get; set; } + } + + /// + /// 外部资金流水接口请求参数 + /// + public class ExternalCashFlowRequest + { + /// + /// 令牌 + /// + public string token { get; set; } + + /// + /// 账号 + /// + public string account { get; set; } + + /// + /// 开始日期 + /// + public string beginDate { get; set; } + + /// + /// 结束日期 + /// + public string endDate { get; set; } + + /// + /// 客户ID + /// + public int? clientId { get; set; } + } +} diff --git a/YLErpDAL/Services/CashRecordModule/ExternalCashFlowOptions.cs b/YLErpDAL/Services/CashRecordModule/ExternalCashFlowOptions.cs new file mode 100644 index 00000000..c9c7330a --- /dev/null +++ b/YLErpDAL/Services/CashRecordModule/ExternalCashFlowOptions.cs @@ -0,0 +1,25 @@ +namespace YLErp.Services.CashRecordModule +{ + /// + /// 外部资金流水配置选项 + /// + public class ExternalCashFlowOptions + { + public const string SectionName = "ExternalCashFlow"; + + /// + /// 令牌 + /// + public string Token { get; set; } + + /// + /// 基础URL + /// + public string BaseUrl { get; set; } + + /// + /// 超时秒数 + /// + public int TimeoutSeconds { get; set; } = 60; + } +} diff --git a/YLErpDAL/Services/CashRecordModule/ExternalCashFlowService.cs b/YLErpDAL/Services/CashRecordModule/ExternalCashFlowService.cs new file mode 100644 index 00000000..dd87becd --- /dev/null +++ b/YLErpDAL/Services/CashRecordModule/ExternalCashFlowService.cs @@ -0,0 +1,109 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using YLErp.Model.ExternalCashFlow; +using Qdp.Foundation.Utilities; +using YLErp.Modules.ApiModule; +using Microsoft.Extensions.Options; + +namespace YLErp.Services.CashRecordModule +{ + /// + /// 外部资金流水接口调用服务 + /// + public interface IExternalCashFlowService + { + /// + /// 获取资金流水数据 + /// + /// 令牌 + /// 账号 + /// 开始日期 + /// 结束日期 + /// 资金流水数据 + Task GetCashFlowDataAsync(string token, string account, string beginDate, string endDate); + } + + /// + /// 外部资金流水接口调用服务实现 + /// + public class ExternalCashFlowService : IExternalCashFlowService + { + private static readonly IYcLogger logger = LogFactory.GetLogger(); + private readonly string _baseUrl; + private readonly int _timeoutSeconds; + + public ExternalCashFlowService(IOptions options) + { + var config = options?.Value ?? new ExternalCashFlowOptions(); + _baseUrl = config.BaseUrl ?? "http://10.99.56.25:8082"; + _timeoutSeconds = config.TimeoutSeconds > 0 ? config.TimeoutSeconds : 60; + } + + /// + /// 获取资金流水数据 + /// + /// 令牌 + /// 账号 + /// 开始日期 + /// 结束日期 + /// 资金流水数据 + public async Task GetCashFlowDataAsync(string token, string account, string beginDate, string endDate) + { + try + { + // 构建请求 URL + var apiUrl = $"{_baseUrl}/eusp/queryStatement"; + var queryString = $"?token={Uri.EscapeDataString(token)}" + + $"&account={Uri.EscapeDataString(account)}" + + $"&beginDate={Uri.EscapeDataString(beginDate)}" + + $"&endDate={Uri.EscapeDataString(endDate)}"; + + var requestUrl = apiUrl + queryString; + logger.Info($"调用外部资金流水接口:{requestUrl}"); + + // 创建 HttpClient 并设置超时 + using (var httpClient = new HttpClient()) + { + httpClient.Timeout = TimeSpan.FromSeconds(_timeoutSeconds); + + // 发送 GET 请求 + var response = await httpClient.GetAsync(requestUrl); + response.EnsureSuccessStatusCode(); + + // 读取响应内容 + var responseContent = await response.Content.ReadAsStringAsync(); + logger.Info($"外部资金流水接口返回:{responseContent}"); + + // 反序列化为响应对象 + var responseObj = JsonHelper.Deserialize(responseContent); + + if (responseObj == null) + { + logger.Error("外部资金流水接口响应为空"); + throw new Exception("外部资金流水接口响应为空"); + } + + if (responseObj.code != "0") + { + logger.Error($"外部资金流水接口调用失败:code={responseObj.code}, message={responseObj.message}"); + throw new Exception($"外部资金流水接口调用失败:{responseObj.message}"); + } + + return responseObj; + } + } + catch (TaskCanceledException ex) + { + logger.Error($"调用外部资金流水接口超时:{ex.Message}", ex); + throw new Exception("调用外部资金流水接口超时,请检查网络连接"); + } + catch (Exception ex) + { + logger.Error($"调用外部资金流水接口失败:{ex.Message}", ex); + throw new Exception($"调用外部资金流水接口失败:{ex.Message}"); + } + } + } +} diff --git a/YLErpWeb/Controllers/entryexitController.cs b/YLErpWeb/Controllers/entryexitController.cs index 5c48b481..37b39cc2 100644 --- a/YLErpWeb/Controllers/entryexitController.cs +++ b/YLErpWeb/Controllers/entryexitController.cs @@ -1,6 +1,10 @@ -using YLErp.Abstract; +using YLErp.Abstract; +using YLErp.DBModels; using YLErp.Modules.ClientModule; using YLErp.Modules.TradeModule.QueryModule; +using YLErp.Services.CashRecordModule; +using YLErp.Model.ExternalCashFlow; +using Microsoft.Extensions.Configuration; namespace YLErp.Web.Controllers { @@ -8,10 +12,14 @@ namespace YLErp.Web.Controllers { readonly IViewRenderService _viewRenderer; private IKafkaProduce kafkaProduceHelper; - public entryexitController(IViewRenderService viewRenderer,IKafkaProduce kafkaProduce) + private readonly IExternalCashFlowService _externalCashFlowService; + private readonly IConfiguration _configuration; + public entryexitController(IViewRenderService viewRenderer,IKafkaProduce kafkaProduce, IExternalCashFlowService externalCashFlowService, IConfiguration configuration) { _viewRenderer = viewRenderer; kafkaProduceHelper = kafkaProduce; + _externalCashFlowService = externalCashFlowService; + _configuration = configuration; } [MyAuthorize("结算管理-出入金维护")] public ActionResult entryexitList() @@ -510,5 +518,111 @@ namespace YLErp.Web.Controllers var sList = new ClientCashInCashOutDataService(CurUser).EntryexitApprovalQuery(req); return Json(sList); } + + /// + /// 获取银行流水数据 + /// + /// 请求参数 + /// 银行流水数据 + [HttpPost] + public async Task GetBankCashFlow(ExternalCashFlowRequest req) + { + try + { + var token = _configuration["ExternalCashFlow:Token"] ?? req.token; + var result = await _externalCashFlowService.GetCashFlowDataAsync(token, req.account, req.beginDate, req.endDate); + + if (result?.data == null || result.data.Count == 0) + { + return JsonSuccess("未获取到银行流水数据"); + } + + var clientId = req.clientId; + if (!clientId.HasValue || clientId == 0) + { + return JsonError("请选择客户"); + } + + var existingFlowIds = yldb.ClientCashInCashOut + .Where(c => !string.IsNullOrEmpty(c.BankFlowId)) + .Select(c => c.BankFlowId) + .ToHashSet(); + + var service = new ClientCashInCashOutDataService(CurUser); + var saveCount = 0; + var skipCount = 0; + var duplicateCount = 0; + var invalidCounterpartyCount = 0; + + foreach (var flow in result.data) + { + if (string.IsNullOrEmpty(flow.transTime)) + { + skipCount++; + continue; + } + + if (string.IsNullOrEmpty(flow.counterpartyAccount)) + { + invalidCounterpartyCount++; + continue; + } + + if (existingFlowIds.Contains(flow.flowId)) + { + duplicateCount++; + continue; + } + + // 根据 counterpartyAccount 查找对应的 BankCard + var bankcard = clientDB.bankcard + .Where(b => b.Card == flow.counterpartyAccount && b.ValidState != "InValid") + .FirstOrDefault(); + + if (bankcard == null) + { + invalidCounterpartyCount++; + continue; + } + + var happenDate = DateTime.TryParse(flow.transTime, out var dt) ? dt : DateTime.Now; + var direction = flow.debitCreditMark == "C" ? "入金" : "出金"; + var amount = Math.Abs(double.TryParse(flow.amount, out var amt) ? amt : 0); + + var cashRecord = new ClientCashInCashOut + { + ClientId = bankcard.ClientId, + ClientName = bankcard.ClientName, + Direction = direction, + Money = amount, + HappenDate = happenDate, + Action = ClientCashInCashOut.系统, + CurrencyCode = flow.currency ?? "CNY", + BankFlowId = flow.flowId, + SerialNumber = flow.flowId, + OpenBankCard = flow.counterpartyAccount, + Comments = $"银行流水导入 | 对方账号:{flow.counterpartyAccount} | 对方户名:{flow.counterpartyName} | 用途:{flow.useDescribe}" + }; + + try + { + service.SaveEntryexit(null, cashRecord); + existingFlowIds.Add(flow.flowId); + saveCount++; + } + catch (Exception ex) + { + LogFactory.GetLogger().Error($"保存银行流水记录失败:{ex.Message}", ex); + } + } + + return JsonSuccess($"获取银行流水成功,共处理{result.data.Count}条,成功保存{saveCount}条,跳过{skipCount}条(无效对手方账号{invalidCounterpartyCount}条,重复{duplicateCount}条)"); + } + catch (Exception ex) + { + LogFactory.GetLogger().Error("获取银行流水失败", ex); + return JsonError(ex.Message); + } + } } } diff --git a/YLErpWeb/Program.cs b/YLErpWeb/Program.cs index 9352aba4..4d7a3f76 100644 --- a/YLErpWeb/Program.cs +++ b/YLErpWeb/Program.cs @@ -49,8 +49,10 @@ try options.MultipartBodyLengthLimit = int.MaxValue; //不限制文件上传大小 }); builder.Services.Configure(builder.Configuration.GetSection("KafkaConfig")); + builder.Services.Configure(builder.Configuration.GetSection("ExternalCashFlow")); builder.Services.AddScoped(); builder.Services.AddSingleton(); + builder.Services.AddTransient(); builder.Services.AddResponseCompression(options => { options.Providers.Add(); diff --git a/YLErpWeb/Views/entryexit/entryexitList.cshtml b/YLErpWeb/Views/entryexit/entryexitList.cshtml index 8948a6d9..aaafc651 100644 --- a/YLErpWeb/Views/entryexit/entryexitList.cshtml +++ b/YLErpWeb/Views/entryexit/entryexitList.cshtml @@ -1,4 +1,4 @@ -@using YLErp +@using YLErp @{ ViewBag.Title = "资金记录"; Layout = "~/Views/Shared/_MainLayout.cshtml"; @@ -672,6 +672,42 @@ $form.submit(); } + function getBankCashFlow() { + var beginDate = $("#DateFromHappenDate").val(); + var endDate = $("#DateToHappenDate").val(); + var clientId = $("#ClientId").val(); + + if (!beginDate || !endDate) { + main.message("请选择发生时间范围"); + return; + } + + function formatDate(dateStr) { + if (!dateStr) return ""; + var date = new Date(dateStr); + var year = date.getFullYear(); + var month = ("0" + (date.getMonth() + 1)).slice(-2); + var day = ("0" + date.getDate()).slice(-2); + return year + month + day; + } + + main.post("/entryexit/GetBankCashFlow", { + clientId: clientId ? clientId[0] : null, + account: "", + beginDate: formatDate(beginDate), + endDate: formatDate(endDate) + }).done(function (data) { + if (data.success) { + main.message("获取银行流水成功"); + console.log(data.obj); + } else { + main.message(data.msg); + } + }).fail(function (error) { + main.message("获取银行流水失败:" + error.responseText); + }); + } + } @@ -711,6 +747,7 @@ } } @MyControls.Btn("导出", "downEntryexit()") + @MyControls.Btn("获取银行流水", "getBankCashFlow()")
@if (CurUser.结算管理_出入金新增) diff --git a/YLErpWeb/appsettings.dev.json b/YLErpWeb/appsettings.dev.json index cf0fb784..3e91ecd7 100644 --- a/YLErpWeb/appsettings.dev.json +++ b/YLErpWeb/appsettings.dev.json @@ -88,5 +88,9 @@ "auth_url": "http://127.0.0.1:8888/sso/login", //SSO授权地址 "callback_url": "http://localhost:49462/Account/Callback", //回调处理地址 "get_user_url": "http://127.0.0.1:8888/sso/getSsoUser" //获取用户信息接口地址 + }, + "ExternalCashFlow": { + "Token": "50cd1507-5361-4cbc-9368-e848c69bb0f6", + "BaseUrl": "http://10.99.56.25:8082" } } \ No newline at end of file