callm 1 settimana fa
parent
commit
128e05b7dc

+ 15 - 0
UAS_MES_ZDXW/.workbuddy/memory/2026-10-13.md

@@ -22,3 +22,18 @@
 - 串口行 x 坐标统一对齐:每对"标签+控件"间距统一 10~16px,串口1/串口2两行列 x 完全对齐
 - ComList 宽 200→180, Handshake 宽 180→150,总宽 1470 < ClientSize 1482
 - 命令行 y=560,串口1 y=624,串口2 y=692,sncode y=760
+
+### Query_SOP 上传缺页修复
+- 问题:上传SOP时偶尔缺页,根因是多线程并发导致4个竞态问题
+- 根因1: DataHelper.connection 是 static 全局唯一,多线程并发 ExecuteSql 互相覆盖 command 字段,catch 时 connection=new(...) 重置导致其他线程命令中断
+- 根因2: ps_attachsop=ps_attachsop||fp_id||';' 并发拼接非原子(读-改-写竞态)
+- 根因3: Spire.Xls 的 SaveToPdf/SaveToImage 非线程安全,多 Workbook 并发转换部分失败
+- 根因4: SaveFileToJPG catch 只 Console.WriteLine,SaveFileToPDF 无 try-catch,异常被吞
+- 修复方案:
+  - uploadfile() 去掉多线程,改为串行 for 循环处理每个 Sheet
+  - SaveFileToJPG/SaveFileToPDF 从 void 改为返回 string fp_id,不再直接 UPDATE 数据库
+  - 内存 List<string> successIds 收集所有成功上传的 fp_id
+  - 循环结束后一次性 UPDATE ps_attachsop='id1;id2;id3;' (不再用 || 拼接)
+  - 每个Sheet失败重试2次
+  - 失败的Sheet收集到 failedSheets 列表,最后 MessageBox 提示用户哪些缺页
+  - 进度提示 [1/total] 处理Sheet: xxx

+ 136 - 55
UAS_MES_ZDXW/FunctionCode/Query/Query_SOP.cs

@@ -22,6 +22,12 @@ namespace UAS_MES_NEW.Query
         string usercode;
         string sourcecode;
 
+        public Query_SOP()
+        {
+            InitializeComponent();
+        }
+
+
         public Query_SOP(string iUserCode, string iSource)
         {
             usercode = iUserCode;
@@ -63,104 +69,179 @@ namespace UAS_MES_NEW.Query
             workbook.ConverterSetting.JPEGQuality = 100;
             workbook.ConverterSetting.XDpi = 600;
             workbook.ConverterSetting.YDpi = 600;
-            List<Worksheet> list = new List<Worksheet>();
-            List<string> filename = new List<string>();
+            //先清空旧的 ps_attachsop,避免残留旧数据
             dh.ExecuteSql("update ProductSOP set ps_attachsop='' where ps_prodcode='" + pr_code.Text + "'", "update");
             OperatResult.AppendText("一共Sheet: " + workbook.Worksheets.Count + "\n");
-            try
+
+            //串行处理每个Sheet,避免并发导致缺页
+            //根因:DataHelper.connection是static全局唯一,多线程并发ExecuteSql会互相覆盖command字段
+            //      且ps_attachsop=ps_attachsop||fp_id并发拼接非原子,后提交覆盖先提交
+            //      Spire.Xls的SaveToPdf/SaveToImage也不是线程安全的
+            //方案:串行转换+上传,内存收集fp_id,最后一次性UPDATE
+            List<string> successIds = new List<string>();
+            List<string> failedSheets = new List<string>();
+            int total = workbook.Worksheets.Count;
+
+            for (int i = 0; i < total; i++)
             {
-                for (int i = 0; i < workbook.Worksheets.Count; i++)
+                string sheetName = workbook.Worksheets[i].Name;
+                OperatResult.AppendText("[" + (i + 1) + "/" + total + "] 处理Sheet: " + sheetName + "\n");
+                try
                 {
+                    //处理TextBox(放大文字,保持原有逻辑)
                     for (int k = 0; k < workbook.Worksheets[i].TextBoxes.Count; k++)
                     {
                         if (workbook.Worksheets[i].TextBoxes[k].RichText.Text.Length > 1)
                         {
-                            //OperatResult.AppendText(workbook.Worksheets[i].TextBoxes[k].ID + " " + workbook.Worksheets[i].TextBoxes[k].Name + " " + workbook.Worksheets[i].TextBoxes[k].RichText.GetFont(0).FontName + " " + workbook.Worksheets[i].TextBoxes[k].RichText.GetFont(0).Size);
                             workbook.Worksheets[i].TextBoxes[k].RichText.Text = workbook.Worksheets[i].TextBoxes[k].RichText.Text;
                             workbook.Worksheets[i].TextBoxes[k].Width = workbook.Worksheets[i].TextBoxes[k].Width * 2;
                             workbook.Worksheets[i].TextBoxes[k].Height = workbook.Worksheets[i].TextBoxes[k].Height * 2;
                             workbook.Worksheets[i].TextBoxes[k].HAlignment = CommentHAlignType.Center;
                         }
                     }
+
+                    //复制到独立Workbook再转换,避免污染原workbook
                     Workbook bw = new Workbook();
                     Worksheet she = bw.CreateEmptySheet();
-                    she.Name = workbook.Worksheets[i].Name;
+                    she.Name = sheetName;
                     she.CopyFrom(workbook.Worksheets[i]);
+
+                    string fp_id = "";
                     if (JPG.Checked)
                     {
-                        var myThread = new Thread(() => SaveFileToJPG(she));
-                        myThread.Start();
-                        filename.Add(she.Name + ".jpg");
+                        fp_id = SaveFileToJPG(she);
                     }
                     else
                     {
-                        var myThread = new Thread(() => SaveFileToPDF(she));
-                        myThread.Start();
-                        filename.Add(she.Name + ".pdf");
+                        fp_id = SaveFileToPDF(she);
                     }
+
+                    if (fp_id != "")
+                    {
+                        successIds.Add(fp_id);
+                        OperatResult.AppendText("  -> 成功, fp_id=" + fp_id + "\n");
+                    }
+                    else
+                    {
+                        failedSheets.Add(sheetName);
+                        OperatResult.AppendText("  -> 失败\n");
+                    }
+                }
+                catch (Exception ex)
+                {
+                    failedSheets.Add(sheetName);
+                    OperatResult.AppendText("  -> 异常: " + ex.Message + "\n");
                 }
             }
-            catch (Exception ex)
+
+            //所有Sheet处理完成后,一次性拼接fp_id写入数据库
+            if (successIds.Count > 0)
             {
-                OperatResult.AppendText(ex.Message + "\n");
+                string idList = "";
+                foreach (string id in successIds)
+                {
+                    idList += id + ";";
+                }
+                dh.ExecuteSql("update ProductSOP set ps_attachsop='" + idList + "' where ps_prodcode='" + pr_code.Text + "'", "update");
+                OperatResult.AppendText("已写入" + successIds.Count + "个文件到ProductSOP\n");
             }
-        }
 
-        private void SaveFileToPDF(Worksheet sheet)
-        {
-            if (!Directory.Exists(Application.StartupPath + @"\" + pr_code.Text))
+            if (failedSheets.Count > 0)
             {
-                Directory.CreateDirectory(Application.StartupPath + @"\" + pr_code.Text);
+                string msg = "以下Sheet上传失败,可能缺页:\n" + string.Join(", ", failedSheets.ToArray());
+                OperatResult.AppendText(msg + "\n");
+                MessageBox.Show(msg, "上传部分失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
             }
-            sheet.SaveToPdf(Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".pdf");
-            OperatResult.AppendText("解析PDF【" + sheet.Name + ".pdf】" + "\n");
-            Dictionary<string, object> dic = new Dictionary<string, object>();
-            dic.Add("em_name", "管理员");
-            dic.Add("em_code", "ADMIN");
-            dic.Add("caller", "ProductSOP");
-            OperatResult.AppendText("上传文件【" + sheet.Name + ".pdf" + "】\n");
-            string fp_id = UploadFilesToRemoteUrl("http://192.168.101.253:8099/mes/MEScommon/uploadFiles.action?_noc=1", Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".pdf", dic);
-            if (fp_id != "")
+            else
             {
-                dh.ExecuteSql("update ProductSOP set ps_attachsop=ps_attachsop||" + fp_id + "||';' where ps_prodcode='" + pr_code.Text + "'", "update");
+                OperatResult.AppendText("全部" + total + "个Sheet上传成功\n");
             }
         }
 
-        private void SaveFileToJPG(Worksheet sheet)
+        private string SaveFileToPDF(Worksheet sheet)
         {
-            try
+            string fp_id = "";
+            //失败重试2次
+            for (int retry = 0; retry < 2; retry++)
             {
-                if (!Directory.Exists(Application.StartupPath + @"\" + pr_code.Text))
+                try
                 {
-                    Directory.CreateDirectory(Application.StartupPath + @"\" + pr_code.Text);
+                    if (!Directory.Exists(Application.StartupPath + @"\" + pr_code.Text))
+                    {
+                        Directory.CreateDirectory(Application.StartupPath + @"\" + pr_code.Text);
+                    }
+                    string filePath = Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".pdf";
+                    sheet.SaveToPdf(filePath);
+                    OperatResult.AppendText("解析PDF【" + sheet.Name + ".pdf】" + "\n");
+                    Dictionary<string, object> dic = new Dictionary<string, object>();
+                    dic.Add("em_name", "管理员");
+                    dic.Add("em_code", "ADMIN");
+                    dic.Add("caller", "ProductSOP");
+                    OperatResult.AppendText("上传文件【" + sheet.Name + ".pdf" + "】\n");
+                    fp_id = UploadFilesToRemoteUrl("http://192.168.101.253:8099/mes/MEScommon/uploadFiles.action?_noc=1", filePath, dic);
+                    if (fp_id != "")
+                    {
+                        break;
+                    }
+                    if (retry == 0)
+                    {
+                        OperatResult.AppendText("首次上传失败,重试中...\n");
+                    }
                 }
-                ExcelPicture picture = sheet.Pictures.Add(@"图片\电子受控章.png");
-                picture.Width = 113;
-                picture.Height = 38;
-                picture.LeftColumn = 20;
-                picture.TopRowOffset = 20;
-                ExcelPicture picture1 = sheet.Pictures.Add(@"图片\签名.png");
-                picture1.Width = 100;
-                picture1.Height = 38;
-                picture1.LeftColumn = 15;
-                picture1.TopRowOffset = 600;
-                sheet.SaveToImage(Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".jpg");
-                OperatResult.AppendText("解析图片【" + sheet.Name + ".jpg】" + "\n");
-                Dictionary<string, object> dic = new Dictionary<string, object>();
-                dic.Add("em_name", "管理员");
-                dic.Add("em_code", "ADMIN");
-                dic.Add("caller", "ProductSOP");
-                OperatResult.AppendText("上传文件【" + sheet.Name + ".jpg" + "】\n");
-                string fp_id = UploadFilesToRemoteUrl("http://192.168.101.253:8099/mes/MEScommon/uploadFiles.action?_noc=1", Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".jpg", dic);
-                if (fp_id != "")
+                catch (Exception ex)
                 {
-                    dh.ExecuteSql("update ProductSOP set ps_attachsop=ps_attachsop||" + fp_id + "||';' where ps_prodcode='" + pr_code.Text + "'", "update");
+                    OperatResult.AppendText("SaveFileToPDF异常: " + ex.Message + (retry == 0 ? " 重试中..." : "") + "\n");
                 }
             }
-            catch (Exception ex)
+            return fp_id;
+        }
+
+        private string SaveFileToJPG(Worksheet sheet)
+        {
+            string fp_id = "";
+            //失败重试2次
+            for (int retry = 0; retry < 2; retry++)
             {
-                Console.WriteLine(ex.Message);
+                try
+                {
+                    if (!Directory.Exists(Application.StartupPath + @"\" + pr_code.Text))
+                    {
+                        Directory.CreateDirectory(Application.StartupPath + @"\" + pr_code.Text);
+                    }
+                    ExcelPicture picture = sheet.Pictures.Add(@"图片\电子受控章.png");
+                    picture.Width = 113;
+                    picture.Height = 38;
+                    picture.LeftColumn = 20;
+                    picture.TopRowOffset = 20;
+                    ExcelPicture picture1 = sheet.Pictures.Add(@"图片\签名.png");
+                    picture1.Width = 100;
+                    picture1.Height = 38;
+                    picture1.LeftColumn = 15;
+                    picture1.TopRowOffset = 600;
+                    string filePath = Application.StartupPath + @"\" + pr_code.Text + @"\" + sheet.Name + ".jpg";
+                    sheet.SaveToImage(filePath);
+                    OperatResult.AppendText("解析图片【" + sheet.Name + ".jpg】" + "\n");
+                    Dictionary<string, object> dic = new Dictionary<string, object>();
+                    dic.Add("em_name", "管理员");
+                    dic.Add("em_code", "ADMIN");
+                    dic.Add("caller", "ProductSOP");
+                    OperatResult.AppendText("上传文件【" + sheet.Name + ".jpg" + "】\n");
+                    fp_id = UploadFilesToRemoteUrl("http://192.168.101.253:8099/mes/MEScommon/uploadFiles.action?_noc=1", filePath, dic);
+                    if (fp_id != "")
+                    {
+                        break;
+                    }
+                    if (retry == 0)
+                    {
+                        OperatResult.AppendText("首次上传失败,重试中...\n");
+                    }
+                }
+                catch (Exception ex)
+                {
+                    OperatResult.AppendText("SaveFileToJPG异常: " + ex.Message + (retry == 0 ? " 重试中..." : "") + "\n");
+                }
             }
+            return fp_id;
         }
 
         /// <summary>