using Newtonsoft.Json.Linq; using Org.BouncyCastle.Ocsp; using System.Text.Json; using System.Text.Json.Nodes; using YieldChain.Helpers; using YLErp.DBModels; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.SystemModule; namespace YLErp.Modules.ClientModule { /// /// 保存客户信息 /// public class ClientSaveApiV2Service : ClientBaseService { const string ConsKey_Action = "_action"; const string ConsKey_Duties = "duties"; const string ConsKey_Bankcards = "bankcards"; const string ConsKey_Files = "files"; public ClientSaveApiV2Service(OptUserInfo userInfo) : base(userInfo) { } /// /// 保存客户信息 /// public ClientSaveApiV2Result Save(Dictionary dic) { if (dic is null) { throw new ServiceException("缺少输入参数"); } var result = new ClientSaveApiV2Result(); var dicData = new DicData(dic); var duties = dicData.GetSubDics(ConsKey_Duties); var bankcards = dicData.GetSubDics(ConsKey_Bankcards); var files = dicData.GetSubDics(ConsKey_Files); using var dbTrans = DbContext.Database.BeginTransaction(); var dbClient = SaveClient(dicData, out var isNew, out var resultMessage); var innerClient = new InnerClient { IsNew = isNew, ClientId = dbClient.id }; //保存客户联系人 if (duties != null && duties.Any()) { result.DutyResults = SaveClientDuties(innerClient, duties); } //保存客户银行账号 if (bankcards != null && bankcards.Any()) { result.BankcardResults = SaveClientBankcards(innerClient, bankcards); } //保存客户文件 if (files != null && files.Any()) { result.FileResults = SaveClientFiles(innerClient, files); } dbTrans.Commit(); result.ClientId = dbClient.id; result.ClientNumber = dbClient.Number; result.ResultMessage = resultMessage; return result; } //保存客户信息 private Client SaveClient(DicData dicData, out bool isNew, out string resultMessage) { Client client = null; switch (dicData.Action) { case ActionType.create: client = new Client(); break; case ActionType.update: { isNew = false; var clientNumber = dicData.GetStringValue(nameof(client.Number)); if (string.IsNullOrEmpty(clientNumber)) { throw new ServiceException("客户编号 必须填写"); } client = DbContext.client.AsNoTracking().FirstOrDefault(n => n.Number == clientNumber); if (client == null) { throw new ServiceException("要更新的客户信息不存在,客户编号:" + clientNumber); } } break; case ActionType.updateOrCreate: { var clientNumber = dicData.GetStringValue(nameof(client.Number)); if (!string.IsNullOrEmpty(clientNumber)) { client = DbContext.client.AsNoTracking().FirstOrDefault(n => n.Number == clientNumber); } client ??= new Client(); } break; case ActionType.delete: throw new ServiceException("不支持删除客户信息"); } isNew = client.id == 0; dicData.MapToClient(client, out var upset); if (isNew || upset.Count > 0) { resultMessage = isNew ? "客户新增成功" : "客户更新成功"; CheckClientModel(client); return new ClientSaveService(this).SaveData(client, upset, out bool blAudit); } resultMessage = string.Empty; return client; } #region----保存客户联系人---- private List SaveClientDuties(InnerClient client, IEnumerable> dutyDics) { var dbContactTypeList = new Lazy(() => { return DbContext.contactype.AsNoTracking().ToArray(); }); var seq = 0; var resultList = new List(dutyDics.Count()); foreach (var dic in dutyDics) { seq++; if (dic == null) { continue; } var dicData = new DicData(dic, client.IsNew); var idCardNo = dicData.GetStringValue(nameof(ClientDuty.IdCardNo)); if (string.IsNullOrWhiteSpace(idCardNo)) { throw new ServiceException($"创建、更新或删除客户人员信息时,证件号码不能为空,索引:第{seq}条"); } if (dicData.Action == ActionType.delete) { var dbDuty = DbContext.clientduty.FirstOrDefault(n => n.ApprovalOrder < 1 && n.ClientId == client.ClientId && n.IdCardNo == idCardNo); if (dbDuty != null) { new ClientDutyService(this).RemoveClientDuty(dbDuty.id); } resultList.Add(new ClientDutySaveApiV2Result { IdCardNo = idCardNo, ResultMessage = dbDuty == null ? "要删除的客户人员信息不存在" : "客户人员信息删除成功" }); } else { var result = SaveClientDuty(client: client, dicData: dicData, idCardNo: idCardNo, seq: seq, dbContactTypeList: dbContactTypeList); resultList.Add(result); } } return resultList; } private ClientDutySaveApiV2Result SaveClientDuty(InnerClient client, DicData dicData, string idCardNo, int seq, Lazy dbContactTypeList) { ClientDuty clientDuty; var predicate = PredicateBuilder.Create(n => n.ApprovalOrder < 1 && n.ClientId == client.ClientId && n.IdCardNo == idCardNo); switch (dicData.Action) { case ActionType.create: clientDuty = new ClientDuty(); if (!client.IsNew && DbContext.clientduty.Any(predicate)) { throw new ServiceException($"新增客户人员信息时,与已有客户人员的证件号码重复,证件号码:{idCardNo},索引:第{seq}条"); } break; case ActionType.update: if (client.IsNew) { throw new ServiceException("新增客户时,客户人员信息不支持更新"); } else { clientDuty = DbContext.clientduty.AsNoTracking().FirstOrDefault(predicate) ?? throw new ServiceException($"要更新的客户人员信息不存在,证件号码:{idCardNo},索引:第{seq}条"); } break; case ActionType.updateOrCreate: clientDuty = client.IsNew ? new ClientDuty() : DbContext.clientduty.AsNoTracking().FirstOrDefault(predicate) ?? new ClientDuty(); break; default: throw new ServiceException("[客户人员信息]不支持此操作:" + dicData.Action); } var isNew = clientDuty.id == 0; // 保存数据时联系人类型必填 var contactType1 = dicData.GetStringValue(nameof(clientDuty.ContactType)); if (isNew ? string.IsNullOrEmpty(contactType1) : contactType1 == string.Empty) { throw new ServiceException($"保存客户时,联系人类型不能为空,证件号码:{idCardNo},索引:第{seq}条"); } // 保存数据时联系人名称必填 var contactName1 = dicData.GetStringValue(nameof(clientDuty.ContactName)); if (isNew ? string.IsNullOrEmpty(contactName1) : contactName1 == string.Empty) { throw new ServiceException($"保存客户时,联系人名称不能为空,证件号码:{idCardNo},索引:第{seq}条"); } //职责类型 var contactType = dicData.GetStringValue(nameof(clientDuty.ContactType)); if (!string.IsNullOrEmpty(contactType)) { var dbContactType = dbContactTypeList.Value.FirstOrDefault(n => n.ContactType == contactType); if (dbContactType == null) { throw new ServiceException("[客户人员信息,ContactType未预期的职责类型:" + contactType); } else { dicData[nameof(clientDuty.ContactTypeId)] = dbContactType.id; } } //证件类型 var idCardType = dicData.GetStringValue(nameof(clientDuty.IdCardType)); if (!string.IsNullOrEmpty(idCardType)) { if (Enum.TryParse(idCardType, out var type)) { dicData[nameof(clientDuty.IdCardType)] = type; } else { throw new ServiceException("[客户人员信息,idCardType未预期的证件类型:" + idCardType); } } dicData[nameof(ClientDuty.ClientId)] = client.ClientId; //map and save dicData.MapToClientDuty(clientDuty, out var upset); new ClientDutyService(this).SaveClientDuty(clientDuty, upset); return new ClientDutySaveApiV2Result { IdCardNo = idCardNo, ResultMessage = isNew ? "客户人员信息新增成功" : "客户人员信息更新成功" }; } #endregion #region----保存客户银行卡---- private List SaveClientBankcards(InnerClient client, IEnumerable> bankcardDics) { var useList = new Lazy>(() => { return BLL.DictionaryBLL.GetList("资金用途", false); }); var seq = 0; var resultList = new List(bankcardDics.Count()); foreach (var dic in bankcardDics) { seq++; if (dic == null) { continue; } var dicData = new DicData(dic, client.IsNew); var cardNo = dicData.GetStringValue(nameof(ClientBankCard.Card)); if (string.IsNullOrWhiteSpace(cardNo)) { throw new ServiceException($"创建、更新或删除客户的银行卡信息时,银行账号不能为空,索引:第{seq}条"); } if (dicData.Action == ActionType.delete) { var cardNoR = cardNo.Replace(" ", ""); var dbBankcard = DbContext.bankcard.FirstOrDefault(n => n.ApprovalOrder < 1 && n.ClientId == client.ClientId && n.Card == cardNoR); if (dbBankcard != null) { new ClientBankCardService(this).InvalidBankcard(dbBankcard.id, out _); } resultList.Add(new ClientBankCardSaveApiV2Result { Card = cardNo, ResultMessage = dbBankcard == null ? "要删除的客户银行卡信息不存在" : "客户银行卡信息删除成功" }); } else { var result = SaveBankcard(client, dicData, cardNo, seq,useList:useList); resultList.Add(result); } } return resultList; } private ClientBankCardSaveApiV2Result SaveBankcard(InnerClient client, DicData dicData, string cardNo, int seq, Lazy> useList) { ClientBankCard bankcard; var predicate = PredicateBuilder.Create(n => n.ApprovalOrder < 1 && n.ClientId == client.ClientId && n.Card == cardNo && n.ValidState != ConsGlobal.InValid); switch (dicData.Action) { case ActionType.create: bankcard = new ClientBankCard(); if (!client.IsNew && DbContext.bankcard.Any(predicate)) { throw new ServiceException($"新增客户银行卡时,与已有客户银行卡的银行账号重复,银行账号:{cardNo},索引:第{seq}条"); } break; case ActionType.update: if (client.IsNew) { throw new ServiceException("新增客户时,客户银行卡信息不支持更新"); } else { bankcard = DbContext.bankcard.AsNoTracking().FirstOrDefault(predicate) ?? throw new ServiceException($"要更新的客户银行卡信息不存在或已无效,银行账号:{cardNo},索引:第{seq}条"); } break; case ActionType.updateOrCreate: bankcard = client.IsNew ? new ClientBankCard() : DbContext.bankcard.AsNoTracking().FirstOrDefault(predicate) ?? new ClientBankCard(); break; default: throw new ServiceException("[客户银行卡信息]不支持此操作:" + dicData.Action); } var isNew = bankcard.id == 0; // 保存客户时开户行型必填 var bank = dicData.GetStringValue(nameof(bankcard.Bank)); if (isNew ? string.IsNullOrEmpty(bank) : bank == string.Empty) { throw new ServiceException($"保存客户时,开户行不能为空,银行账号:{cardNo},索引:第{seq}条"); } // 保存客户时银行账号称必填 var card = dicData.GetStringValue(nameof(bankcard.Card)); if (isNew ? string.IsNullOrEmpty(card) : card == string.Empty) { throw new ServiceException($"保存客户时,银行账号不能为空,银行账号:{cardNo},索引:第{seq}条"); } // 保存客户时户名称必填 var clientName = dicData.GetStringValue(nameof(bankcard.ClientName)); if (isNew ? string.IsNullOrEmpty(clientName) : clientName == string.Empty) { throw new ServiceException($"保存客户时,户名不能为空,银行账号:{cardNo},索引:第{seq}条"); } // 保存客户时validState状态验证 var validState = dicData.GetStringValue(nameof(bankcard.ValidState)); if (isNew ? string.IsNullOrEmpty(validState) : validState == string.Empty) { throw new ServiceException($"保存客户时,validState不能为空,银行账号:{cardNo},索引:第{seq}条"); } if (!string.IsNullOrEmpty(validState) && validState != "Valid" && validState!= "InValid") { throw new ServiceException($"保存客户时,validState不符合预期[Valid/InValid],注意大小写,银行账号:{cardNo},索引:第{seq}条"); } // 保存客户时,验证资金用途 var use = dicData.GetStringValue(nameof(bankcard.Use)); if (!string.IsNullOrEmpty(use) && !useList.Value.Exists(g => g.Value == use)) { throw new ServiceException($"保存客户时,Use(资金用途)不符合预期,银行账号:{cardNo},索引:第{seq}条"); } dicData[nameof(ClientBankCard.ClientId)] = client.ClientId; dicData.MapToClientBankcard(bankcard, out var upset); new ClientBankCardService(this).SaveBankCard(bankcard, out _, upset); return new ClientBankCardSaveApiV2Result { Card = cardNo, ResultMessage = isNew ? "客户银行卡信息新增成功" : "客户银行卡信息更新成功" }; } #endregion #region----保存客户文件---- private List SaveClientFiles(InnerClient client, IEnumerable> fileDics) { var signTypeList = new Lazy>(() => { return BLL.DictionaryBLL.GetList("权益类签署版本", false); }); var fileTypeList = new Lazy>(() => { return BLL.DictionaryBLL.GetList("文件类型", false); }); var seq = 0; var resultList = new List(fileDics.Count()); foreach (var dic in fileDics) { seq++; if (dic == null) { continue; } var dicData = new DicData(dic, client.IsNew); var fileName = dicData.GetStringValue(nameof(client_file.FileName)); if (string.IsNullOrWhiteSpace(fileName)) { throw new ServiceException($"上传客户文件出错,文件名称不能为空,索引:第{seq}条"); } if (dicData.Action == ActionType.delete) { var protocolNumber = dicData.GetStringValue(nameof(client_file.ProtocolNumber)); var ret = new ClientFileService(this).DeleteFile(client.ClientId, fileName, protocolNumber); if (ret.IsSuccess) { resultList.Add(new ClientFileSaveApiV2Result { FileName = fileName, ResultMessage = ret.Message }); } else { throw new ServiceException($"删除客户文件出错,索引:第{seq}条," + ret.Message); } } else { var result = SaveClientFile(client, dicData, fileName, seq, signTypeList: signTypeList, fileTypeList: fileTypeList); resultList.Add(result); } } return resultList; } private ClientFileSaveApiV2Result SaveClientFile(InnerClient client, DicData dicData, string fileName, int seq, Lazy> signTypeList, Lazy> fileTypeList) { //// 校验权益类签署版本 //var signType = dicData.GetStringValue("FileTypeName"); //if (!string.IsNullOrEmpty(signType) && !signTypeList.Value.Exists(g => g.Value == signType)) //{ // throw new ServiceException($"上传客户文件出错,权益类签署版本不符合预期,索引:第{seq}条"); //} //// 校验文件类型 //var fileTypeName = dicData.GetStringValue("FileTypeName"); //if (!string.IsNullOrEmpty(fileTypeName)&&!fileTypeList.Value.Exists(g => g.Value == fileTypeName)) //{ // throw new ServiceException($"上传客户文件出错,文件类型不符合预期,索引:第{seq}条"); //} // 校验文件内容 var fileContent = dicData.GetStringValue("FileContent"); if (string.IsNullOrWhiteSpace(fileContent)) { throw new ServiceException($"上传客户文件出错,文件内容不能为空,索引:第{seq}条"); } byte[] bytes; try { bytes = Convert.FromBase64String(fileContent); } catch (Exception ex) { throw new ServiceException($"解析客户文件内容出错,索引:第{seq}条,{ex.Message}"); } // 校验操作时间 var optDate = dicData.GetStringValue(nameof(client_file.OptDate)); if (string.IsNullOrWhiteSpace(optDate)) { dicData[nameof(Client.OptDate)] = DateTime.Now; } var clientFile = DbContext.client_file.AsNoTracking() .FirstOrDefault(n => n.ApprovalOrder < 1 && n.ClientId == client.ClientId && n.FileName == fileName && n.IsValid) ?? new client_file(); dicData[nameof(client_file.ClientId)] = client.ClientId; dicData.MapToClientFile(clientFile, out var upset); try { clientFile.FlagStr = "FromWebAPI"; var ret = new ClientFileService(this).SaveClientFiles(clientFile , dicData.GetStringValue("SideProtocolNumber") , new List { new YLErp.Models.UploadFileModel{ FileName = fileName, OpenReadStream = ()=> new MemoryStream(bytes), Length = bytes.Length, ContentType = fileName.EndsWith(".pdf",StringComparison.OrdinalIgnoreCase)?"application/pdf":"" } }); return new ClientFileSaveApiV2Result { FileName = fileName, ResultMessage = ret }; } catch (Exception ex) { throw new ServiceException($"保存客户文件出错,索引:第{seq}条,{ex.Message}"); } } #endregion private void CheckClientModel(Client client) { var propertys = client.GetType().GetProperties(); var config = ClientEditConfigService.GetConfig(true); var selectFields = ClientEditConfigService.GetAllFormEditFieldByTableId("basic").Where(g => g.type == "select"|| g.type == "select-m" || g.type == "select-sm"); foreach (var field in selectFields) { var prop = propertys.Where(a => a.Name == field.name).FirstOrDefault(); if (prop == null) { continue; } var value = prop.GetValue(client, null); if (value != null) { if (field.appendBlank && (prop.PropertyType == typeof(int) && (int)value == 0)) { continue; } var selectValues = config.selects.Where(a => a.Key == field.name).SelectMany(a => a.Value).Select(a => a.Value); if (selectValues?.Count() == 0) { continue; } var item = selectValues.FirstOrDefault(g => g == value.ToString()); if (item == null) { throw new ServiceException($"[客户人员信息,[{field.name}]非预期的选项"); } } } } class InnerClient { /// /// 是否新增 /// public bool IsNew { get; set; } public int ClientId { get; set; } } enum ActionType { update, create, updateOrCreate, delete } class DicData : Dictionary { public DicData(IDictionary dic) : base(dic, StringComparer.OrdinalIgnoreCase) { Action = GetAction(GetStringValue("_action")); } public DicData(IDictionary dic, bool isNewClient) : base(dic, StringComparer.OrdinalIgnoreCase) { Action = isNewClient ? ActionType.create : GetAction(GetStringValue("_action")); } public ActionType Action { get; } private static ActionType GetAction(string action) => action switch { "update" => ActionType.update, "create" => ActionType.create, "updateOrCreate" => ActionType.updateOrCreate, "delete" => ActionType.delete, _ => string.IsNullOrEmpty(action) ? ActionType.create : throw new ServiceException("未预期的'_action'值:" + action) }; public string GetStringValue(string key) { return TryGetValue(key, out var val) ? val?.ToString() : null; } public IEnumerable> GetSubDics(string key) { if (TryGetValue(key, out var arr)) { if (arr is IEnumerable> t1) { return t1; } if (arr is IEnumerable> t2) { return t2.Cast>(); } if (arr is JArray jarr) { return jarr.ToObject[]>() .Select(n => new Dictionary(n.Select(m => new KeyValuePair(m.Key, m.Value)))) .ToArray(); } if (arr is JsonArray jsarr) { return jsarr.Deserialize[]>() .Select(n => new Dictionary(n.Select(m => new KeyValuePair(m.Key, m.Value)))) .ToArray(); } throw new ServiceException("解析数据失败,参数名称:" + key); } return null; } /// /// 将字典数据映射到客户数据对象 /// public void MapToClient(Client client, out HashSet upset) => InnerMap(client, out upset); /// /// 将字典数据映射到客户人员信息数据对象 /// public void MapToClientDuty(ClientDuty duty, out HashSet upset) => InnerMap(duty, out upset); /// /// 将字典数据映射到客户银行卡信息数据对象 /// public void MapToClientBankcard(ClientBankCard bankcard, out HashSet upset) => InnerMap(bankcard, out upset); /// /// 将字典数据映射到客户文件信息数据对象 /// public void MapToClientFile(client_file file, out HashSet upset) => InnerMap(file, out upset); private void InnerMap(T target, out HashSet upset) where T : DBModelBase { upset = new HashSet(Keys, StringComparer.OrdinalIgnoreCase); upset.Remove(ConsKey_Action); upset.Remove(ConsKey_Duties); upset.Remove(ConsKey_Bankcards); upset.Remove(ConsKey_Files); upset.Remove(nameof(DBModelBase.id)); if (target.id > 0) { if (typeof(T) == typeof(Client)) { upset.Remove(nameof(Client.Number)); } else if (typeof(T) == typeof(ClientDuty)) { upset.Remove(nameof(ClientDuty.IdCardNo)); } else if (typeof(T) == typeof(ClientBankCard)) { upset.Remove(nameof(ClientBankCard.Card)); } } if (upset.Count > 0) { var set = upset; ObjectHelper.MapToObject(this, target, pname => set.Contains(pname)); } } } } public class ClientSaveApiV2Result { public int ClientId { get; set; } public string ClientNumber { get; set; } public string ResultMessage { get; set; } public IEnumerable DutyResults { get; set; } public IEnumerable BankcardResults { get; set; } public IEnumerable FileResults { get; set; } } public class ClientDutySaveApiV2Result { public string IdCardNo { get; set; } public string ResultMessage { get; set; } } public class ClientBankCardSaveApiV2Result { public string Card { get; set; } public string ResultMessage { get; set; } } public class ClientFileSaveApiV2Result { public string FileName { get; set; } public string ResultMessage { get; set; } } }