// OaApiService.cs
|
using System;
|
using System.Collections.Generic;
|
using System.Dynamic;
|
using System.Data.SqlClient;
|
using System.Data;
|
using System.IO;
|
using System.Net.Http;
|
using System.Security.Cryptography;
|
using System.Text;
|
using System.Threading.Tasks;
|
using NewPdaSqlServer.entity.Base;
|
using Newtonsoft.Json;
|
using Newtonsoft.Json.Linq;
|
using Org.BouncyCastle.Crypto.Parameters;
|
using Org.BouncyCastle.Security;
|
using NewPdaSqlServer.DB;
|
using SqlSugar;
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
using NewPdaSqlServer.entity;
|
using System.Linq;
|
|
public class OaApiService : Repository<dynamic>
|
{
|
private readonly HttpClient _httpClient;
|
private const string AppId = "303233EF-AC39-40C1-8364-AC989C6258A5";
|
private const string BaseUrl = "http://192.168.1.149:8099/api/ec/dev/auth/";
|
private const string WorkflowUrl = "http://192.168.1.149:8099/api/workflow/paService/doCreateRequest";
|
|
public OaApiService()
|
{
|
_httpClient = new HttpClient();
|
_httpClient.Timeout = TimeSpan.FromSeconds(30);
|
}
|
|
public dynamic SubmitIQCToOA(dynamic queryObj)
|
{
|
// 修改参数验证字段名
|
if (string.IsNullOrEmpty(queryObj.userId.ToString())) throw new Exception("用户id不允许为空");
|
if (string.IsNullOrEmpty(queryObj.qcczdGuid.ToString())) throw new Exception("异常处置单id不能为空");
|
//if (queryObj.qcczdGuid.IsNullOrEmpty()) throw new Exception("异常处置单id不能为空"); // 原参数名为 qcczdGuid
|
|
try
|
{
|
// 解析请求参数
|
//var query = queryObj;
|
//var query = JObject.FromObject(queryObj);
|
|
// 1. 注册获取凭证
|
var registResult = GetRegistAsync();
|
var secret = registResult["secrit"].ToString();
|
var spk = registResult["spk"].ToString();
|
|
// 2. 获取访问令牌
|
var tokenResult = ApplyTokenAsync(secret, spk);
|
var token = tokenResult["token"].ToString();
|
|
// 3. 准备请求头(移除Content-Type设置)
|
_httpClient.DefaultRequestHeaders.Clear();
|
_httpClient.DefaultRequestHeaders.Add("token", token);
|
_httpClient.DefaultRequestHeaders.Add("appid", AppId);
|
_httpClient.DefaultRequestHeaders.Add("userid", RSAEncrypt("1268", spk));
|
|
// 4. 构建请求体
|
var requestData = BuildRequestData(queryObj);
|
|
// 5. 发送请求
|
var response = PostForm(WorkflowUrl, requestData);
|
|
// 新增日志记录(在返回响应前)
|
using (var conn = new SqlConnection(DbHelperSQL.strConn))
|
{
|
using (var cmd = new SqlCommand("prc_log_create", conn))
|
{
|
try
|
{
|
conn.Open();
|
cmd.CommandType = CommandType.StoredProcedure;
|
SqlParameter[] logParams =
|
{
|
new("@edtUserGuid", SqlDbType.UniqueIdentifier) { Value = Guid.Parse(queryObj.userId.ToString()) },
|
new("@abtGuid", SqlDbType.UniqueIdentifier) { Value = Guid.Parse(queryObj.qcczdGuid.ToString()) },
|
new("@abtTable", SqlDbType.NVarChar, 40) { Value = "Mes_QC_Exceptional" },
|
new("@detail", SqlDbType.NVarChar, 2500) { Value = "提交IQC到OA系统" },
|
new("@hNo", SqlDbType.NVarChar, 100) { Value = "" },
|
new("@SendJson", SqlDbType.NVarChar) { Value = JObject.FromObject(requestData).ToString() },
|
new("@RtnJson", SqlDbType.NVarChar) { Value = response.Content.ToString() }
|
};
|
|
foreach (var param in logParams)
|
cmd.Parameters.Add(param);
|
|
cmd.ExecuteNonQuery();
|
}
|
catch (Exception logEx)
|
{
|
// 日志记录失败不中断主流程
|
Console.WriteLine($"日志记录失败: {logEx.Message}");
|
}
|
finally
|
{
|
conn.Close();
|
}
|
}
|
}
|
|
var result = JObject.Parse(response.Content.ToString());
|
|
// 新增状态更新(当OA返回成功时)
|
if (result.code?.ToString() == "SUCCESS")
|
{
|
var updateSql = @"UPDATE Mes_QC_Exceptional
|
SET checkStatus = 1,
|
checkDate = GETDATE(),
|
checkBy = (select top 1 u.[ACCOUNT] from [dbo].[SYS_USER] u where u.guid = @edtUserCode)
|
WHERE GUID = @inOrderGuid";
|
|
using (var conn = new SqlConnection(DbHelperSQL.strConn))
|
{
|
using (var cmd = new SqlCommand(updateSql, conn))
|
{
|
try
|
{
|
conn.Open();
|
//cmd.Parameters.AddWithValue("@dt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
cmd.Parameters.AddWithValue("@edtUserCode", queryObj.userId?.ToString());
|
cmd.Parameters.AddWithValue("@inOrderGuid", queryObj.qcczdGuid?.ToString());
|
cmd.ExecuteNonQuery();
|
}
|
catch (Exception updateEx)
|
{
|
Console.WriteLine($"状态更新失败: {updateEx.Message}");
|
}
|
finally
|
{
|
conn.Close();
|
}
|
}
|
}
|
}
|
|
return result; // 直接解析为JObject
|
|
}
|
catch (Exception ex)
|
{
|
dynamic error = new ExpandoObject();
|
error.code = "SYSTEM_INNER_ERROR"; // 新增标准错误码
|
error.errMsg = ex.Message; // 修正字段名匹配规范
|
error.data = new JObject();
|
error.reqFailMsg = new JObject();
|
return error;
|
}
|
}
|
|
private Dictionary<string, object> BuildRequestData(dynamic queryObj)
|
{
|
// 从query参数中获取实际业务数据
|
return new Dictionary<string, object>
|
{
|
{"mainData", BuildMainData(queryObj)},
|
{"requestName", $"IQC异常处置单流程-{DateTime.Now:yyyyMMddHHmmss}"},
|
{"workflowId", "379"},
|
//{"otherParams", BuildOtherParams()},
|
{"detailData", BuildDetailData(queryObj)}
|
};
|
}
|
|
private JArray createbhgpqxmszp(dynamic files)
|
{
|
var attachments = new JArray();
|
|
foreach (var file in files ?? Enumerable.Empty<dynamic>())
|
{
|
attachments.Add(new JObject
|
{
|
// 修正字段名称匹配标准格式
|
["filePath"] = file.url?.ToString() ?? string.Empty,
|
["fileName"] = file.FileName?.ToString() ?? "未命名文件"
|
});
|
}
|
|
return attachments;
|
}
|
private JArray BuildMainData(dynamic queryObj)
|
{
|
// 使用参数化查询防止SQL注入
|
const string mainSql = @"EXEC select_oa_BuildMainData @guid";
|
|
var mainData = Db.Ado.SqlQuery<dynamic>(mainSql, new { guid = queryObj.qcczdGuid });
|
|
// 更精确的数据存在性检查
|
if (mainData == null || !mainData.Any())
|
{
|
throw new Exception($"未找到异常处置单[{queryObj.qcczdGuid}]相关数据");
|
}
|
var firstRecord = mainData.First();
|
|
// 字段映射配置(字段名 -> 数据库列名)
|
var fieldMappings = new Dictionary<string, (string Field, string Default)>
|
{
|
["sqr"] = ("sqr", "0"), // 默认值改为数字
|
["sqrq"] = ("sqrq", ""), // 空字符串用于日期格式化
|
["szbm"] = ("szbm", "0"), // 默认值改为数字
|
["szdw"] = ("szdw", "0"), // 默认值改为数字
|
["ycczdh"] = ("ycczdh", "N/A"),
|
["lh"] = ("lh", "N/A"),
|
["khgys"] = ("khgys", "N/A"),
|
["jzmcwlmc"] = ("jzmcwlmc", "N/A"),
|
["dhdhtxt"] = ("dhdhtxt", "N/A"),
|
["bhgpqxms"] = ("bhgpqxms", "无缺陷描述")
|
};
|
|
// 动态生成字段集合
|
var result = new JArray();
|
foreach (var mapping in fieldMappings)
|
{
|
var value = mapping.Key switch
|
{
|
"sqr" or "szbm" or "szdw" =>
|
int.TryParse(GetDynamicValue(firstRecord, mapping.Value.Field, mapping.Value.Default), out int num)
|
? num : 0,
|
"sqrq" => DateTime.TryParse(GetDynamicValue(firstRecord, mapping.Value.Field, ""), out DateTime date)
|
? date.ToString("yyyy-MM-dd")
|
: DateTime.Now.ToString("yyyy-MM-dd"),
|
_ => GetDynamicValue(firstRecord, mapping.Value.Field, mapping.Value.Default)
|
};
|
result.Add(new JObject
|
{
|
["fieldName"] = mapping.Key,
|
["fieldValue"] = value
|
});
|
}
|
|
// 处理文件数据
|
const string fileSql = @"
|
SELECT
|
release_no+'-图片'+CAST(row_number() over (order by release_no) AS VARCHAR(20)) AS FileName,
|
'http://192.168.1.145:81/upload/'+url_Path as url
|
FROM [Mes_QC_Exceptional_Detail] A
|
LEFT JOIN MES_QA_ITEMS_DETECT_01 B ON A.releaseNo = B.release_no
|
RIGHT JOIN MES_FILE C ON C.parent_Guid = B.guid
|
WHERE pGuid = @guid";
|
|
var fileData = Db.Ado.SqlQuery<dynamic>(fileSql, new { guid = queryObj.qcczdGuid });
|
var files = CreateFileData(fileData);
|
|
// 添加文件字段
|
result.Add(new JObject
|
{
|
["fieldName"] = "bhgpqxmszp",
|
["fieldValue"] = files
|
});
|
|
return result;
|
}
|
|
// 辅助方法:安全获取dynamic对象值
|
private static string GetDynamicValue(dynamic record, string field, string defaultValue)
|
{
|
try
|
{
|
return ((IDictionary<string, object>)record)[field]?.ToString() ?? defaultValue;
|
}
|
catch
|
{
|
return defaultValue;
|
}
|
}
|
|
// 文件数据生成方法(示例保持原逻辑)
|
private JArray CreateFileData(IEnumerable<dynamic> fileData)
|
{
|
// 假设这是原createbhgpqxmszp方法的实现
|
return new JArray(fileData.Select(f => new JObject
|
{
|
["fileName"] = f.FileName,
|
["filePath"] = f.url
|
}));
|
}
|
|
// // 修改BuildMainData中的字段赋值
|
// private JArray BuildMainData(dynamic queryObj)
|
// {
|
// var sql = string.Format(@"select TOP 1 C.item_no AS wlbm,
|
// C.item_name AS jzmcwlmc,
|
// D.supp_name AS khgys,
|
// B.bill_no AS dhdhmes,
|
// B.remark AS bhgpqxms
|
//from Mes_QC_Exceptional A
|
// LEFT JOIN MES_INV_ITEM_ARN B
|
// ON A.aboutGuid = b.guid
|
// LEFT JOIN MES_ITEMS C ON A.itemId = c.item_id
|
// LEFT JOIN MES_SUPPLIER D ON B.supp_id = D.id
|
//WHERE a.guid = '{0}'", queryObj.qcczdGuid);
|
|
// var tBiqcczd = Db.Ado.SqlQuery<dynamic>(sql);
|
|
// // 修正数据获取逻辑
|
// if (tBiqcczd == null)
|
// {
|
// throw new Exception($"未找到异常处置单[{queryObj.qcczdGuid}]相关数据");
|
// }
|
// var data = tBiqcczd; // 转换为字典访问
|
|
// var wlbm = data[0]["wlbm"];
|
|
// var sql1 = string.Format(@"select release_no+'-图片'+CAST(row_number() over (order by release_no) AS VARCHAR(20)) AS FileName, 'http://192.168.1.145:81/upload/'+url_Path as url
|
//from [Mes_QC_Exceptional_Detail] A
|
// LEFT JOIN MES_QA_ITEMS_DETECT_01 B ON A.releaseNo = B.release_no
|
// LEFT JOIN MES_FILE C ON C.parent_Guid = B.guid
|
//WHERE pGuid = '{0}'", queryObj.qcczdGuid);
|
|
// var tBdataFile = Db.Ado.SqlQuery<dynamic>(sql1);
|
|
// var data_file = createbhgpqxmszp(tBdataFile);
|
|
|
// return new JArray
|
// {
|
// // 修改所有字段访问方式(以wlbm为例)
|
// new JObject
|
// {
|
// ["fieldName"] = "wlbm",
|
// ["fieldValue"] = data?["wlbm"]?.ToString() ?? "N/A" // 使用字典方式访问
|
// },
|
// new JObject
|
// {
|
// ["fieldName"] = "khgys",
|
// ["fieldValue"] = data?["khgys"]?.ToString() ?? "N/A" // 统一字典访问
|
// },
|
// new JObject
|
// {
|
// ["fieldName"] = "jzmcwlmc",
|
// ["fieldValue"] = data?["jzmcwlmc"]?.ToString() ?? "N/A" // 统一字典访问
|
// },
|
// new JObject
|
// {
|
// ["fieldName"] = "dhdhmes",
|
// ["fieldValue"] = data?["dhdhmes"]?.ToString() ?? "N/A" // 统一字典访问
|
// },
|
// new JObject
|
// {
|
// ["fieldName"] = "bhgpqxms",
|
// ["fieldValue"] = data?["bhgpqxms"]?.ToString() ?? "无缺陷描述" // 统一字典访问
|
// },
|
// new JObject
|
// {
|
// ["fieldName"] = "bhgpqxmszp",
|
// ["fieldValue"] = data_file // 使用生成的附件数组
|
// },
|
// };
|
// }
|
|
private JArray BuildAttachments(JToken attachments)
|
{
|
var array = new JArray();
|
|
if (attachments != null)
|
{
|
foreach (var att in attachments)
|
{
|
array.Add(new JObject
|
{
|
["filePath"] = att["url"]?.ToString(),
|
["fileName"] = att["fileName"]?.ToString()
|
});
|
}
|
}
|
return array;
|
}
|
|
private JArray BuildDetailData(dynamic queryObj)
|
{
|
// 使用参数化查询获取明细数据
|
const string mainSql = @"select ROW_NUMBER() over (ORDER BY Jy_Date) AS xh,releaseNo AS jydh, Jy_Date AS jydrq, batchQty as sjsl,
|
chouQty AS cys, badQty AS bls, badProb AS bll,C.USER_NAME AS jyr
|
from Mes_QC_Exceptional_Detail A
|
LEFT JOIN MES_QA_ITEMS_DETECT_01 B ON A.releaseNo = B.release_no
|
LEFT JOIN SYS_USER C ON C.ACCOUNT = B.fcheck_by
|
WHERE pGuid = @guid";
|
|
var details = Db.Ado.SqlQuery<dynamic>(mainSql, new { guid = queryObj.qcczdGuid });
|
|
var tableRecords = new JArray();
|
|
// 遍历每条明细数据
|
foreach (var record in details ?? Enumerable.Empty<dynamic>())
|
{
|
// 修正字段类型转换(数值类型去除字符串包裹)
|
var fields = new JArray
|
{
|
//new JObject { ["fieldName"] = "xh", ["fieldValue"] = record.xh?.ToString() ?? "" },
|
new JObject { ["fieldName"] = "jydh", ["fieldValue"] = record.jydh?.ToString() ?? "" },
|
new JObject { ["fieldName"] = "jyr", ["fieldValue"] = record.jyr?.ToString() ?? "" },
|
new JObject {
|
["fieldName"] = "jydrq",
|
// 格式化为"yyyy-MM-dd"字符串
|
["fieldValue"] = (record.jydrq != null
|
? Convert.ToDateTime(record.jydrq).ToString("yyyy-MM-dd ")
|
: DateTime.Now.ToString("yyyy-MM-dd"))
|
},
|
// 数值字段保持数字类型而非字符串
|
// 修正数值类型转换方式
|
new JObject { ["fieldName"] = "sjsl", ["fieldValue"] = (float)(record.sjsl ?? 0m) },
|
new JObject { ["fieldName"] = "cys", ["fieldValue"] = (float)(record.cys ?? 0) },
|
new JObject { ["fieldName"] = "bls", ["fieldValue"] = (float)(record.bls ?? 0) },
|
new JObject { ["fieldName"] = "bll", ["fieldValue"] = 1, }
|
};
|
|
tableRecords.Add(new JObject
|
{
|
["recordOrder"] = 0, // 实际业务中可能需要根据行号设置
|
["workflowRequestTableFields"] = fields
|
});
|
}
|
|
return new JArray
|
{
|
new JObject
|
{
|
["tableDBName"] = "formtable_main_33_dt1",
|
["workflowRequestTableRecords"] = tableRecords
|
}
|
};
|
}
|
|
private JObject BuildOtherParams()
|
{
|
return new JObject
|
{
|
["isnextflow"] = "1",
|
["delReqFlowFaild"] = "1"
|
};
|
}
|
|
private JObject GetRegistAsync()
|
{
|
using var rsa = new RSACryptoServiceProvider(2048);
|
var cpk = Convert.ToBase64String(rsa.ExportRSAPublicKey());
|
|
_httpClient.DefaultRequestHeaders.Clear();
|
_httpClient.DefaultRequestHeaders.Add("appid", AppId);
|
_httpClient.DefaultRequestHeaders.Add("cpk", cpk);
|
|
var response = _httpClient.PostAsync(BaseUrl + "regist", null).Result;
|
return JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
}
|
|
private JObject ApplyTokenAsync(string secret, string spk)
|
{
|
_httpClient.DefaultRequestHeaders.Clear();
|
_httpClient.DefaultRequestHeaders.Add("appid", AppId);
|
_httpClient.DefaultRequestHeaders.Add("secret", RSAEncrypt(secret, spk));
|
|
var response = _httpClient.PostAsync(BaseUrl + "applytoken", null).Result;
|
return JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
}
|
|
private string RSAEncrypt(string data, string publicKey)
|
{
|
//using var rsa = new RSACryptoServiceProvider();
|
//rsa.ImportRSAPublicKey(Convert.FromBase64String(publicKey), out _);
|
|
//var dataBytes = Encoding.UTF8.GetBytes(data);
|
//var encrypted = rsa.Encrypt(dataBytes, RSAEncryptionPadding.Pkcs1);
|
//return Convert.ToBase64String(encrypted);
|
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
|
string XML = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
|
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
|
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
|
string encryptedContent = string.Empty;
|
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
|
{
|
rsa.FromXmlString(XML);
|
byte[] encryptedData = rsa.Encrypt(Encoding.Default.GetBytes(data), false);
|
encryptedContent = Convert.ToBase64String(encryptedData);
|
}
|
return encryptedContent;
|
}
|
|
private dynamic PostForm(string url, Dictionary<string, object> data)
|
{
|
// 改用FormUrlEncodedContent并正确设置Content-Type
|
var formData = new List<KeyValuePair<string, string>>();
|
foreach (var item in data)
|
{
|
formData.Add(new KeyValuePair<string, string>(item.Key, item.Value.ToString()));
|
}
|
|
var content = new FormUrlEncodedContent(formData);
|
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
|
|
var response = _httpClient.PostAsync(url, content).Result;
|
var responseString = response.Content.ReadAsStringAsync().Result;
|
|
// 添加响应元数据
|
var responseInfo = new JObject
|
{
|
["StatusCode"] = (int)response.StatusCode,
|
["ReasonPhrase"] = response.ReasonPhrase,
|
["Headers"] = JToken.FromObject(response.Headers),
|
["Content"] = responseString
|
};
|
|
// 调试输出完整响应信息
|
Console.WriteLine($"完整响应:\n{responseInfo.ToString(Formatting.Indented)}");
|
|
return responseInfo;
|
}
|
|
public void Dispose()
|
{
|
_httpClient?.Dispose();
|
}
|
}
|