using SqlSugar; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Web; using VOL.Core.Services; using VOL.YSErp.Models; using VOL.YSErp.Models.Biz; using static Dm.net.buffer.ByteArrayBuffer; namespace VOL.YSErp.Services.Biz { public class YSERPService { private readonly string _apiUrl; private readonly string _appKey; private readonly string _appSecret; private string _accessToken; private DateTime? _tokenExpiry; private readonly SemaphoreSlim _tokenLock = new SemaphoreSlim(1, 1); private SystemToken _token; private readonly HttpClient _httpClient; // YS API URLs private readonly string _tokenUrl; private readonly string _employeeListUrl; private readonly string _updateEmployeeUrl; private readonly string _deptListUrl; private readonly string _empInfoUrl; public YSERPService(SystemToken token, YSConfig config) { _token = token; _apiUrl = config.ApiUrl.TrimEnd('/'); _appKey = config.Appkey; _appSecret = config.AppSecret; _httpClient = new HttpClient(); _tokenUrl = $"{_apiUrl}/iuap-api-auth/open-auth/selfAppAuth/getAccessToken"; _employeeListUrl = $"{_apiUrl}/iuap-api-gateway/yonbip/hrcloud/staff/listdetailmdd"; _updateEmployeeUrl = $"{_apiUrl}/iuap-api-gateway/yonbip/hrcloud/batchInsertOrUpdateDetailNew"; _deptListUrl = $"{_apiUrl}/iuap-api-gateway/yonbip/digitalModel/basedoc/dept/list"; _empInfoUrl = $"{_apiUrl}/iuap-api-gateway/yonbip/hrcloud/HRCloud/getStaffDetail"; } private string GenerateSignature(Dictionary parameters) { try { var filteredParams = new Dictionary(); foreach (var param in parameters) { if (param.Key != "signature") { filteredParams[param.Key] = param.Value; } } var sortedParams = new SortedDictionary(filteredParams); var paramStr = string.Join("", sortedParams.Select(p => p.Key + p.Value)); using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_appSecret))) { var signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(paramStr)); return Convert.ToBase64String(signatureBytes); } } catch (Exception ex) { //_logger.LogError(ex, "生成签名时出错"); return null; } } private async Task<(string Token, DateTime Expiry)?> GetAccessTokenAsync() { try { var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(); var parameters = new Dictionary { ["appKey"] = _appKey, ["timestamp"] = timestamp }; var signature = GenerateSignature(parameters); if (string.IsNullOrEmpty(signature)) { return null; } parameters["signature"] = signature; var queryString = string.Join("&", parameters.Select(p => $"{p.Key}={HttpUtility.UrlEncode(p.Value)}")); var fullUrl = $"{_tokenUrl}?{queryString}"; var response = await _httpClient.GetAsync(fullUrl); if (!response.IsSuccessStatusCode) { //_logger.LogError($"获取access_token失败: HTTP {response.StatusCode}"); return null; } var content = await response.Content.ReadAsStringAsync(); var data = JsonSerializer.Deserialize(content); if (data.GetProperty("code").GetString() != "00000") { var errorMsg = data.GetProperty("message").GetString(); //_logger.LogError($"获取access_token失败: {errorMsg}"); return null; } var tokenData = data.GetProperty("data"); var accessToken = tokenData.GetProperty("access_token").GetString(); var expireSeconds = tokenData.GetProperty("expire").GetInt32(); return (accessToken, DateTime.Now.AddSeconds(expireSeconds - 300)); } catch (Exception ex) { //_logger.LogError(ex, "获取access_token时出错"); return null; } } private async Task GetValidTokenAsync() { await _tokenLock.WaitAsync(); try { if (string.IsNullOrEmpty(_token.Token) || !_token.IsTokenExpiry()) { //_logger.LogInformation("YS access_token已过期,重新获取..."); var result = await GetAccessTokenAsync(); if (result.HasValue) { (_accessToken, _tokenExpiry) = result.Value; _token.Token = _accessToken; _token.TokenEndTime = _tokenExpiry ?? DateTime.Now; } else { //_logger.LogError("无法获取有效的YS access_token"); return null; } } return _token.Token; } finally { _tokenLock.Release(); } } public async Task> GetAllEmployeesAsync() { var token = await GetValidTokenAsync(); if (string.IsNullOrEmpty(token)) { return new List(); } var allEmployees = new List(); var pageIndex = 1; const int pageSize = 50; Logger.Info($"员工信息API: {_employeeListUrl}"); while (true) { try { Logger.Info($"获取YS员工数据,第 {pageIndex} 页..."); var payload = new { pageIndex, pageSize, }; var request = new HttpRequestMessage(HttpMethod.Post, $"{_employeeListUrl}?access_token={token}") { Content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") }; var response = await _httpClient.SendAsync(request); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { Logger.Error("认证失败: 无效的API令牌或权限不足"); _accessToken = null; return []; } response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var data = JsonSerializer.Deserialize>(content); if (!new[] { "200", "00000" }.Contains(data.code)) { var errorMsg = data.message; Logger.Error($"API业务错误: {errorMsg}"); throw new Exception(errorMsg); } var recordList = data.data.recordList; if (recordList?.Count == 0) { Logger.Info("没有更多员工记录"); break; } var toAdd = recordList.Where(r => !allEmployees.Any(e => e.id == r.id)).ToList(); allEmployees.AddRange(toAdd); var haveNext = data.data.haveNextPage; var pageCount = data.data.pageCount; if (!haveNext || pageIndex >= pageCount) { break; } //Logger.Info($"第 {pageIndex}/{pageCount} 页,获取 {recordList.Count} 条员工记录"); pageIndex++; await Task.Delay(500); // 避免请求过快 } catch (Exception ex) { Logger.Error($"获取员工数据失败,{ex}"); throw; } } //_logger.LogInformation($"共获取 {allEmployees.Count} 条YS员工记录"); return allEmployees; } public async Task UpdateEmployeesAsync(List> employeeUpdates) { var token = await GetValidTokenAsync(); if (string.IsNullOrEmpty(token)) { return false; } //_logger.LogInformation($"YS更新内容: {JsonSerializer.Serialize(employeeUpdates)}"); try { var request = new HttpRequestMessage(HttpMethod.Post, $"{_updateEmployeeUrl}?access_token={token}") { Content = new StringContent( JsonSerializer.Serialize(new { data = employeeUpdates }), Encoding.UTF8, "application/json") }; var response = await _httpClient.SendAsync(request); //_logger.LogInformation($"YS更新响应状态: {response.StatusCode}"); var content = await response.Content.ReadAsStringAsync(); //_logger.LogInformation($"YS更新响应内容: {content}"); response.EnsureSuccessStatusCode(); var data = JsonSerializer.Deserialize(content); if (data.GetProperty("code").GetString() == "00000") { //_logger.LogInformation($"成功更新 {employeeUpdates.Count} 名员工信息"); if (data.TryGetProperty("data", out var resultData)) { if (resultData.TryGetProperty("messages", out var messages)) { foreach (var msg in messages.EnumerateArray()) { //_logger.LogWarning($"失败信息: {msg}"); } } } return true; } else { var errorMsg = data.GetProperty("message").GetString(); Logger.Error($"更新失败: {errorMsg}"); return false; } } catch (Exception ex) { Logger.Error( $"更新员工信息失败,{ex}"); return false; } } public async Task> GetAllDepartmentsAsync() { var token = await GetValidTokenAsync(); if (string.IsNullOrEmpty(token)) { return []; } var allDepts = new List(); //Logger.Info($"员工信息API: {_employeeListUrl}"); try { var payload = new { data = new { code = new List(), pubts = new List() { "1900-01-01 00:00:00" }, // 必须带一个参数,不然接口返回的数据格式有问题 2025年9月26日 } }; var request = new HttpRequestMessage(HttpMethod.Post, $"{_deptListUrl}?access_token={token}") { Content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") }; var response = await _httpClient.SendAsync(request); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { Logger.Error("认证失败: 无效的API令牌或权限不足"); _accessToken = null; return []; } response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var data = JsonSerializer.Deserialize>(content); if (!new[] { "200", "00000" }.Contains(data.code)) { var errorMsg = data.message; Logger.Error($"API业务错误: {errorMsg}"); throw new Exception(errorMsg); } var recordList = data.data; if(recordList?.Count == 0) { Logger.Info("没有更多部门记录"); return allDepts; } else { allDepts.AddRange(recordList ?? []); } } catch (Exception ex) { Logger.Error($"获取部门数据失败,{ex}"); throw; } //_logger.LogInformation($"共获取 {allEmployees.Count} 条YS员工记录"); return allDepts; } public async Task GetEmpInfoById(string empId) { var token = await GetValidTokenAsync(); if (string.IsNullOrEmpty(token)) { return new YSERPEmployeeInfo(); } try { var payload = new { id = empId }; var request = new HttpRequestMessage(HttpMethod.Post, $"{_empInfoUrl}?access_token={token}") { Content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") }; var response = await _httpClient.SendAsync(request); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { Logger.Error("认证失败: 无效的API令牌或权限不足"); _accessToken = null; return new YSERPEmployeeInfo(); } response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var data = JsonSerializer.Deserialize>(content); if (!new[] { "200", "00000" }.Contains(data.code)) { var errorMsg = data.message; Logger.Error($"API业务错误: {errorMsg}"); throw new Exception(errorMsg); } return data.data; } catch (Exception ex) { Logger.Error($"获取部门数据失败,{ex}"); throw; } } public void Dispose() { _tokenLock?.Dispose(); _httpClient?.Dispose(); } } }