新框架PDA后端(祈禧6月初版本)
南骏 池
3 天以前 fca0719af6948fe8fa1e4f094f8e7dba339c7428
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
// 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();
    }
}