StandardInterface/MES.Service/Dto/service/RKJDto.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MES.Service/Dto/service/XJPageResult.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MES.Service/Modes/QsItemOqcReq.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MES.Service/service/QC/LljService.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MES.Service/service/QC/RKJService.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MES.Service/service/QcIssueResult/QcIssueResultManager.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MESApplication/Controllers/QC/RKJController.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MESApplication/Controllers/QcIssueResult/QcIssueResultController.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
StandardInterface/MESApplication/appsettings.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
StandardInterface/MES.Service/Dto/service/RKJDto.cs
@@ -23,6 +23,18 @@ // 送检数量 public decimal? quantity { get; set; } // 不良原因 public string? BLYY { get; set; } // 评审状态 public string? PSZT { get; set; } // 所属车间 public string? SSCJ { get; set; } // 检验单号 public string? releaseNo { get; set; } //========================================== //巡检主子表需要的数据 public QsItemOqcReq? from { get; set; } StandardInterface/MES.Service/Dto/service/XJPageResult.cs
@@ -13,4 +13,6 @@ public string? UserIndex { get; set; } public string? fsubmit { get; set; } } StandardInterface/MES.Service/Modes/QsItemOqcReq.cs
@@ -161,4 +161,28 @@ /// </summary> [SugarColumn(ColumnName = "QUANTITY")] public decimal? Quantity { get; set; } /// <summary> /// 不良原因 /// </summary> [SugarColumn(ColumnName = "BLYY")] public string? Blyy { get; set; } /// <summary> /// 评审状态 /// </summary> [SugarColumn(ColumnName = "PSZT")] public string? Pszt { get; set; } /// <summary> /// 所属车间 /// </summary> [SugarColumn(ColumnName = "SSCJ")] public string? Sscj { get; set; } /// <summary> /// 不良描述 /// </summary> [SugarColumn(ColumnName = "FNG_DESC")] public string? FngDesc { get; set; } } StandardInterface/MES.Service/service/QC/LljService.cs
@@ -45,7 +45,7 @@ e, f, g, i) => a.FcheckResu != null) .WhereIF(id > 0, (a, b, e, f, g, i) => a.Id == id) .WhereIF(queryObj.createUser != "PL017" && queryObj.UserIndex == "0", (a, b, .WhereIF(queryObj.createUser != "PL017" && queryObj.createUser != "HMLYY" && queryObj.UserIndex == "0", (a, b, e, f, g, i) => (g.Fcode == queryObj.createUser || i.Fcode == queryObj.createUser || (g.Fcode == null && i.Fcode == null)))//判断此单的检验员,或者检验为空就显示此单据 StandardInterface/MES.Service/service/QC/RKJService.cs
@@ -1,10 +1,14 @@ using MES.Service.DB; using MES.Service.Dto.service; using MES.Service.Modes; using MES.Service.Modes.QcIssueResult; using MES.Service.service.QcIssueResult; using MES.Service.util; using SqlSugar; using System.Linq; using System.Data; using System.Net; using System.Web; namespace MES.Service.service.QC; @@ -624,6 +628,13 @@ !"未完成".Equals(queryObj.result), (a, da, b) => a.FcheckResu != null && a.FcheckResu != "") .WhereIF(id > 0, (a, da, b) => a.Id == id) // 添加fsubmit字段过滤逻辑 .WhereIF( StringUtil.IsNotNullOrEmpty(queryObj.fsubmit) && queryObj.fsubmit == "1", (a, da, b) => a.Fsubmit == 1) .WhereIF( StringUtil.IsNullOrEmpty(queryObj.fsubmit) || queryObj.fsubmit == "0", (a, da, b) => a.Fsubmit == 0 || a.Fsubmit == null) .Select((a, da, b) => new QsItemOqcReq { Id = a.Id, @@ -641,6 +652,11 @@ Remarks = a.Remarks, WorkShop = a.WorkShop, DepartmentId = a.DepartmentId, Fsubmit = a.Fsubmit, // 添加fsubmit字段到返回结果 Blyy = a.Blyy, // 添加不良原因字段 Pszt = a.Pszt, // 添加评审状态字段 Sscj = a.Sscj, // 添加所属车间字段 FngDesc = a.FngDesc, // 添加不良描述字段 // 从关联表获取的字段 ItemName = b.ItemName, ItemModel = b.ItemModel, @@ -811,6 +827,78 @@ { return SqlSugarHelper.UseTransactionWithOracle(db => { // 获取检验单详细信息 var inspectionData = db.Queryable<QsItemOqcReq>() .Where(it => it.Id == id) .First(); if (inspectionData == null) { throw new Exception("检验单不存在"); } // 检查钉钉推送条件:PSZT为待判,且不良原因、不良描述、所属车间不为空 bool shouldPushToDingTalk = inspectionData.Pszt == "待判" && !string.IsNullOrEmpty(inspectionData.Blyy) && !string.IsNullOrEmpty(inspectionData.FngDesc) && !string.IsNullOrEmpty(inspectionData.Sscj); // 如果满足推送条件,则推送到钉钉 if (shouldPushToDingTalk) { try { // 构建钉钉推送数据 var dingTalkData = new GetQcIssueResultDetail { FbatchQty = inspectionData.Quantity?.ToString(), // 送检数量 ItemName = inspectionData.ItemName, ItemNo = inspectionData.ItemNo, SuppName = "生产入库", // 固定值 AppicationReason = inspectionData.FngDesc, // 不良描述 BadReason = inspectionData.Blyy, // 不良原因 remark = inspectionData.Remarks, ReleaseNo = inspectionData.ReleaseNo, StaffNo = userNo, IqcStatus = inspectionData.Pszt, // 评审状态 Department = inspectionData.Sscj, // 使用所属车间作为部门 WorkShop = inspectionData.Sscj, // 所属车间 EMERGENCY = "0" // 固定值为0 }; // 调用钉钉推送服务 var qcIssueResultManager = new QcIssueResultManager(); string dingTalkResult = qcIssueResultManager.GetProcessNo(dingTalkData, "RKJ"); // 解析钉钉返回结果,获取流程实例ID if (dingTalkResult.Contains("钉钉工作流实例启动成功")) { // 提取instanceId int startIndex = dingTalkResult.IndexOf("\"InstanceId\":\"") + 14; int endIndex = dingTalkResult.IndexOf("\"", startIndex); if (startIndex > 13 && endIndex > startIndex) { string instanceId = dingTalkResult.Substring(startIndex, endIndex - startIndex); // 保存钉钉流程信息 qcIssueResultManager.SaveProcessNo( inspectionData.ReleaseNo, instanceId, userNo, inspectionData.Sscj, inspectionData.Pszt ); } } } catch (Exception ex) { // 钉钉推送失败不影响检验提交,只记录日志 Console.WriteLine($"钉钉推送失败: {ex.Message}"); } } // 更新检验单状态 return db.Updateable<QsItemOqcReq>() .SetColumns(it => it.Fsubmit == 1) // 设置提交状态为1(已提交) .SetColumns(it => it.FcheckBy == userNo) // 设置检验人 @@ -851,4 +939,220 @@ return (result, message); } /// <summary> /// 获取附件信息 /// </summary> /// <param name="ItemNo">物料编码</param> /// <returns>附件列表</returns> public List<QamftpDto> GetAttachments(string ItemNo) { var db = SqlSugarHelper.GetInstance(); try { return db.Queryable<MesQamftp>() .Where(x => x.ItemNo == ItemNo) .OrderBy(x => x.Fdate, OrderByType.Desc) .Select(x => new QamftpDto { Id = x.Id, itemNo = x.ItemNo, Ftype = x.Ftype, Fattach = x.Fattach, Fversion = x.Fversion, Fdate = x.Fdate, CreateBy = x.CreateBy, CreateDate = x.CreateDate, Company = x.Company, Factory = x.Factory, F_type = x.F_type, LastupdateBy = x.LastupdateBy, LastupdateDate = x.LastupdateDate, ItemId = x.ItemId }).ToList(); } catch (Exception ex) { throw new Exception($"查询附件信息失败: {ex.Message}"); } } /// <summary> /// 从FTP服务器获取文件 /// </summary> /// <param name="itemNo">物料编码</param> /// <param name="fileName">文件名</param> /// <param name="ftpServer">FTP服务器地址</param> /// <returns>文件字节数组</returns> public byte[] GetFtpFile(string itemNo, string fileName, string ftpServer) { // 参数验证 if (string.IsNullOrEmpty(itemNo) || string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(ftpServer)) { throw new ArgumentException("参数不能为空: itemNo, fileName, ftpServer"); } string ftpUser = "hm_ftp"; string ftpPwd = "dell_123"; // 标准化FTP服务器地址 string normalizedServer = NormalizeFtpServer(ftpServer); // 构建FTP文件路径 - RKJ使用FQC文件夹 string ftpPath = $"{normalizedServer}/FQC/{itemNo}/{fileName}"; try { var request = (System.Net.FtpWebRequest)System.Net.WebRequest.Create(ftpPath); request.Method = System.Net.WebRequestMethods.Ftp.DownloadFile; request.Credentials = new System.Net.NetworkCredential(ftpUser, ftpPwd); request.UseBinary = true; request.UsePassive = false; request.Timeout = 30000; // 30秒超时 request.ReadWriteTimeout = 30000; using (var response = (System.Net.FtpWebResponse)request.GetResponse()) using (var ftpStream = response.GetResponseStream()) using (var ms = new System.IO.MemoryStream()) { if (ftpStream == null) { throw new Exception("FTP响应流为空"); } ftpStream.CopyTo(ms); var fileBytes = ms.ToArray(); if (fileBytes.Length == 0) { return null; // 文件为空或不存在 } return fileBytes; } } catch (System.Net.WebException ex) { if (ex.Response is System.Net.FtpWebResponse ftpResponse) { switch (ftpResponse.StatusCode) { case System.Net.FtpStatusCode.ActionNotTakenFileUnavailable: return null; // 文件不存在 case System.Net.FtpStatusCode.NotLoggedIn: throw new Exception("FTP认证失败,请检查用户名和密码"); case System.Net.FtpStatusCode.ActionNotTakenFilenameNotAllowed: throw new Exception("文件名不被允许或路径无效"); default: throw new Exception($"FTP错误 ({ftpResponse.StatusCode}): {ftpResponse.StatusDescription}"); } } // 处理超时和网络错误 if (ex.Status == System.Net.WebExceptionStatus.Timeout) { throw new Exception("FTP连接超时,请稍后重试"); } throw new Exception($"FTP连接失败: {ex.Message}"); } catch (Exception ex) { throw new Exception($"获取FTP文件失败: {ex.Message}"); } } /// <summary> /// 获取文件内容类型 /// </summary> /// <param name="fileName">文件名</param> /// <returns>内容类型</returns> public string GetContentType(string fileName) { if (string.IsNullOrEmpty(fileName)) return "application/octet-stream"; var extension = System.IO.Path.GetExtension(fileName).ToLowerInvariant(); return extension switch { ".pdf" => "application/pdf", ".jpg" or ".jpeg" => "image/jpeg", ".png" => "image/png", ".gif" => "image/gif", ".bmp" => "image/bmp", ".txt" => "text/plain", ".doc" => "application/msword", ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".xls" => "application/vnd.ms-excel", ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".ppt" => "application/vnd.ms-powerpoint", ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", _ => "application/octet-stream" }; } /// <summary> /// 标准化FTP服务器地址 /// </summary> /// <param name="ftpServer">FTP服务器地址</param> /// <returns>标准化后的地址</returns> private string NormalizeFtpServer(string ftpServer) { if (string.IsNullOrEmpty(ftpServer)) throw new ArgumentException("FTP服务器地址不能为空"); // 移除可能的协议前缀 var normalized = ftpServer.Trim(); if (normalized.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase)) { normalized = normalized.Substring(6); } else if (normalized.StartsWith("ftps://", StringComparison.OrdinalIgnoreCase)) { normalized = normalized.Substring(7); } // 确保以ftp://开头 if (!normalized.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase)) { normalized = "ftp://" + normalized; } return normalized; } /// <summary> /// 保存下拉框字段(不良原因、评审状态、所属车间) /// </summary> /// <param name="dto">包含字段数据的DTO</param> /// <returns>更新结果</returns> public int SaveDropdownFields(RKJDto dto) { return SqlSugarHelper.UseTransactionWithOracle(db => { return db.Updateable<QsItemOqcReq>() .SetColumns(it => it.Blyy == dto.BLYY) .SetColumns(it => it.Pszt == dto.PSZT) .SetColumns(it => it.Sscj == dto.SSCJ) .Where(it => it.ReleaseNo == dto.releaseNo) .ExecuteCommand(); }); } /// <summary> /// 保存不良描述 /// </summary> /// <param name="gid">检验单ID</param> /// <param name="fngDesc">不良描述</param> /// <returns>更新结果</returns> public int SaveFngDesc(decimal gid, string fngDesc) { return SqlSugarHelper.UseTransactionWithOracle(db => { return db.Updateable<QsItemOqcReq>() .SetColumns(it => it.FngDesc == fngDesc) .Where(it => it.Id == gid) .ExecuteCommand(); }); } } StandardInterface/MES.Service/service/QcIssueResult/QcIssueResultManager.cs
@@ -213,7 +213,7 @@ * @return string 返回启动结果 */ public string GetProcessNo(GetQcIssueResultDetail getQcIssueResultDetail) public string GetProcessNo(GetQcIssueResultDetail getQcIssueResultDetail, string processType = "LLJ") { var db = SqlSugarHelper.GetInstance(); @@ -247,6 +247,10 @@ AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceHeaders startProcessInstanceHeaders = new AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceHeaders(); Debug.WriteLine(accessToken); startProcessInstanceHeaders.XAcsDingtalkAccessToken = accessToken; // 使用获取到的 Access Token // 根据流程类型选择不同的ProcessCode string processCode = "PROC-4F4B3976-BA15-47EF-9A3C-3A07AC07DFEA"; // 统一使用来料检流程编码 AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceRequest.StartProcessInstanceRequestFormComponentValues formComponentValues1 = new AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceRequest.StartProcessInstanceRequestFormComponentValues { Name = "TextField_K5AVR59BD28", @@ -359,7 +363,7 @@ AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceRequest startProcessInstanceRequest = new AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceRequest { OriginatorUserId = UserId, ProcessCode = "PROC-4F4B3976-BA15-47EF-9A3C-3A07AC07DFEA", ProcessCode = processCode, DeptId = 1, FormComponentValues = new List<AlibabaCloud.SDK.Dingtalkworkflow_1_0.Models.StartProcessInstanceRequest.StartProcessInstanceRequestFormComponentValues> { StandardInterface/MESApplication/Controllers/QC/RKJController.cs
@@ -5,6 +5,7 @@ using MES.Service.util; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; using System.Web; namespace MESApplication.Controllers.QC; @@ -514,4 +515,197 @@ return ResponseResult.ResponseError(ex); } } /// <summary> /// 保存下拉框字段(不良原因、评审状态、所属车间) /// </summary> /// <param name="rkjDto">包含字段数据的DTO</param> /// <returns>保存结果</returns> [HttpPost("saveDropdownFields")] public ResponseResult SaveDropdownFields([FromBody] RKJDto rkjDto) { try { dynamic resultInfos = new ExpandoObject(); var tbBillList = new RKJService().SaveDropdownFields(rkjDto); resultInfos.tbBillList = tbBillList; return new ResponseResult { status = 0, message = "OK", data = resultInfos }; } catch (Exception ex) { return ResponseResult.ResponseError(ex); } } /// <summary> /// 保存不良描述 /// </summary> /// <param name="data">包含gid和fngDesc的JSON对象</param> /// <returns>保存结果</returns> [HttpPost("saveFngDesc")] public ResponseResult SaveFngDesc([FromBody] JObject data) { try { var gid = Convert.ToDecimal(data["gid"]?.ToString()); var fngDesc = data["fngDesc"]?.ToString(); dynamic resultInfos = new ExpandoObject(); var tbBillList = new RKJService().SaveFngDesc(gid, fngDesc); resultInfos.tbBillList = tbBillList; return new ResponseResult { status = 0, message = "OK", data = resultInfos }; } catch (Exception ex) { return ResponseResult.ResponseError(ex); } } /// <summary> /// 获取附件信息 /// </summary> /// <param name="data">包含itemNo的JSON对象</param> /// <returns>附件列表</returns> [HttpPost("getAttachments")] public ResponseResult GetAttachments([FromBody] JObject data) { var itemNo = data["itemNo"]?.ToString(); try { dynamic resultInfos = new System.Dynamic.ExpandoObject(); var tbBillList = new RKJService().GetAttachments(itemNo); if (tbBillList == null || tbBillList.Count == 0) { return new ResponseResult { status = 1, message = "该检验单未上传附件信息!", data = null }; } resultInfos.tbBillList = tbBillList; return new ResponseResult { status = 0, message = "OK", data = resultInfos }; } catch (Exception ex) { return ResponseResult.ResponseError(ex); } } /// <summary> /// 预览FTP文件 /// </summary> /// <param name="itemNo">物料编码</param> /// <param name="fileName">文件名</param> /// <param name="ftpServer">FTP服务器地址</param> /// <returns>文件内容</returns> [HttpGet("PreviewFtpFile")] public IActionResult PreviewFtpFile([FromQuery] string itemNo, [FromQuery] string fileName, [FromQuery] string ftpServer) { try { // 添加CORS响应头 Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-Control-Allow-Methods", "GET, OPTIONS"); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type"); Response.Headers.Add("Access-Control-Expose-Headers", "Content-Type, Content-Length"); var service = new RKJService(); var fileBytes = service.GetFtpFile(itemNo, fileName, ftpServer); if (fileBytes == null || fileBytes.Length == 0) { return NotFound("文件在FTP服务器上不存在"); } var contentType = service.GetContentType(fileName); fileName = fileName?.Trim().Replace("\r", "").Replace("\n", ""); return File(fileBytes, contentType); } catch (Exception ex) { return StatusCode(500, $"预览文件失败:{ex.Message}"); } } /// <summary> /// 下载FTP文件 /// </summary> /// <param name="itemNo">物料编码</param> /// <param name="fileName">文件名</param> /// <param name="ftpServer">FTP服务器地址</param> /// <returns>文件下载</returns> [HttpGet("DownloadFtpFile")] public IActionResult DownloadFtpFile([FromQuery] string itemNo, [FromQuery] string fileName, [FromQuery] string ftpServer) { try { // 添加CORS响应头 - 关键配置用于解决跨域问题 Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-Control-Allow-Methods", "GET, OPTIONS"); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization"); Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition, Content-Length, Content-Type"); var service = new RKJService(); var fileBytes = service.GetFtpFile(itemNo, fileName, ftpServer); if (fileBytes == null || fileBytes.Length == 0) { return NotFound("文件在FTP服务器上不存在"); } var contentType = service.GetContentType(fileName); fileName = fileName?.Trim().Replace("\r", "").Replace("\n", ""); // 设置正确的Content-Disposition响应头以支持文件下载 var result = File(fileBytes, "application/octet-stream", fileName); // 确保Content-Disposition头正确设置,支持中文文件名 if (!string.IsNullOrEmpty(fileName)) { var encodedFileName = System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8); Response.Headers.Add("Content-Disposition", $"attachment; filename*=UTF-8''{encodedFileName}"); } return result; } catch (Exception ex) { return StatusCode(500, $"下载文件失败:{ex.Message}"); } } /// <summary> /// 处理CORS预检请求 /// </summary> /// <returns>OK</returns> [HttpOptions("PreviewFtpFile")] [HttpOptions("DownloadFtpFile")] public IActionResult HandleOptions() { // 处理CORS预检请求 Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-Control-Allow-Methods", "GET, OPTIONS"); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization"); Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition, Content-Length, Content-Type"); Response.Headers.Add("Access-Control-Max-Age", "86400"); return Ok(); } } StandardInterface/MESApplication/Controllers/QcIssueResult/QcIssueResultController.cs
@@ -33,7 +33,7 @@ [HttpPost("GetProcessNo")] public IActionResult StartDingtalkProcess([FromBody] GetQcIssueResultDetail getQcIssueResultDetail) public IActionResult StartDingtalkProcess([FromBody] GetQcIssueResultDetail getQcIssueResultDetail, [FromQuery] string processType = "LLJ") { try @@ -42,7 +42,7 @@ DecodeUrlEncodedProperties(getQcIssueResultDetail); // 调用 QcIssueResultManager 中的 GetProcessNo 方法来启动钉钉工作流实例 string result = m.GetProcessNo(getQcIssueResultDetail); string result = m.GetProcessNo(getQcIssueResultDetail, processType); string getLaboratoryDetailsJson = JsonConvert.SerializeObject(getQcIssueResultDetail, Formatting.Indented); Debug.WriteLine("getQcIssueResultDetail: " + getLaboratoryDetailsJson); StandardInterface/MESApplication/appsettings.json
@@ -10,6 +10,6 @@ "AppSettings": { "TestErpUrl": "http://192.168.11.120:8098/WebService1.asmx/mesToErpinfo", "ProductionErpUrl": "http://192.168.11.120:8098/WebService1.asmx/mesToErpinfoFormal", "DataBaseConn": "Data Source = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.22)(PORT = 1521))(CONNECT_DATA = (SERVICE_NAME = ORCL))); Persist Security Info=True;User ID = hm_prd; Password=hmprd" "DataBaseConn": "Data Source = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.22)(PORT = 1521))(CONNECT_DATA = (SERVICE_NAME = ORCL))); Persist Security Info=True;User ID = test_dev; Password=hmprd" } }