using System.Collections; using System.Data; using System.Data.SqlClient; using System.Dynamic; using Gs.Toolbox; using Gs.Toolbox.ApiCore.Abstract.Mvc; using Gs.Toolbox.ApiCore.Common.Mvc; using Gs.Toolbox.ApiCore.Group; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using static Gs.Toolbox.UtilityHelper; namespace Gs.Sys.Services { /// /// 表单管理控制器 /// 负责管理前端表单的UI布局配置(标准版/个人版)、ERP数据同步推送、动态查询配置等功能 /// [ApiGroup(ApiGroupNames.FM)] public class FmController : IRomteService { private readonly IHttpContextAccessor _http; /// /// 当前用户编码 /// private readonly string _userCode; /// /// 当前用户GUID /// private readonly string _userGuid; /// /// 当前用户所属组织FID路径 /// private readonly string _orgFids; /// /// 构造函数,从HTTP上下文中提取当前用户信息 /// /// HTTP上下文访问器 public FmController(IHttpContextAccessor httpContextAccessor) { _http = httpContextAccessor; (_userCode, _userGuid, _orgFids) = GetUserGuidAndOrgGuid(_http); } #region 布局配置 /// /// 保存或清空表单布局配置 /// /// 包含以下字段的动态对象: /// - formPath: 表单路径标识 /// - intType: 操作类型(1=保存标准版,2=保存个人版,3=清空标准版,4=清空个人版) /// - xmlList: UI控件配置列表(JArray格式),每个包含idName、idXml、idType、splitterPosition /// /// 返回操作结果,包含outMsg消息字段 /// /// 标准版布局:所有用户共享,需要管理员权限操作 /// 个人版布局:用户私有,优先级高于标准版 /// 布局数据存储在FM_LAYOUT表,通过groupGuid进行批量关联 /// [RequestMethod(RequestMethods.POST)] public ReturnDto EditModel([FromBody] dynamic model) { string applyUserGuid = ""; string formPath = model.formPath; int intType = model.intType; // intType含义:1=保存标准版,2=保存个人版,3=清空标准版,4=清空个人版 dynamic m = new ExpandoObject(); m.outMsg = ""; // 收集批量SQL语句,用于事务性执行保存布局操作 Hashtable SQLStringList = new Hashtable(); string _groupGuid = Guid.NewGuid().ToString(); // 标准版布局操作需要超级管理员权限验证 if (intType == 1 || intType == 3) { // 验证当前用户是否具有管理员权限,以操作标准版布局 int? isAdmin = 0; try { isAdmin = chkAdmin(); if (isAdmin <= 0) { m.outMsg = "你不是管理员,操作失败!"; return ReturnDto.QuickReturn(m, ReturnCode.Default, "操作成功!"); } } catch (Exception ex) { Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error:" + ex.Message); } } // 保存标准版布局:清除旧的标准版数据,applyUserGuid为null表示全局共享 if (intType == 1) { // 保留新批次布局配置,删除同一表单路径下的旧标准版布局 applyUserGuid = null; Gs.Toolbox.DbHelperSQL.ExecuteSql( "delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) }); // SQLStringList.Add("delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) }); } // 保存个人版布局:清除当前用户旧的个人版数据,applyUserGuid为用户GUID if (intType == 2) { // 保存调用者的个人布局副本,通过用户GUID限定作用域 applyUserGuid = _userGuid; Gs.Toolbox.DbHelperSQL.ExecuteSql( "delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) }); //SQLStringList.Add("delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) }); } // 清空标准版布局:管理员可以完全清除共享的标准版布局 if (intType == 3) { // 管理员可以完全删除共享的标准版布局,恢复到未配置状态 applyUserGuid = null; SQLStringList.Add( "delete from [FM_LAYOUT] where [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) }); Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList); m.outMsg = "清空标准版配置成功!"; return ReturnDto.QuickReturn(m, ReturnCode.Success, "操作成功!"); } // 清空个人版布局:删除当前用户的个人版布局,保留标准版不受影响 if (intType == 4) { // 移除调用者的个人布局,保留共享标准版不变 applyUserGuid = _userGuid; SQLStringList.Add( "delete from [FM_LAYOUT] where [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) }); Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList); m.outMsg = "清空个人版配置成功!"; return ReturnDto.QuickReturn(m, ReturnCode.Success, "操作成功!"); } // 遍历所有UI控件配置(gridview、layout、xml等),批量插入数据库 JArray jArray = model.xmlList; try { foreach (var jsonitem in jArray) { JObject job = (JObject)jsonitem; if (job["idName"] != null) { // 为每个UI控件(网格、布局面板、分割器等)构建插入语句 string idName = job["idName"].ToString(); string idXml = job["idXml"].ToString(); string idType = job["idType"].ToString(); string _splitterPosition = job["splitterPosition"].ToString(); string splitterPosition = string.IsNullOrEmpty(_splitterPosition) ? "0" : _splitterPosition; System.Text.StringBuilder _sql = new System.Text.StringBuilder(); _sql.Append( " INSERT INTO [dbo].[FM_LAYOUT] ([guid] ,[applyUserGuid] ,[formPath] ,[controlId],[controlHeight],[lastUpdateBy],[lastUpdateDate],controlXml,controlType,groupGuid,splitterPosition)"); _sql.Append( "values(newid(),@applyUserGuid,@formPath,@controlId,@controlHeight,@lastUpdateBy,getdate(),@controlXml,'" + idType + "','" + _groupGuid + "'," + splitterPosition + ")"); SQLStringList.Add(_sql, new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@controlId", idName), new SqlParameter("@controlHeight", "0"), new SqlParameter("@lastUpdateBy", _userCode), new SqlParameter("@applyUserGuid", applyUserGuid), new SqlParameter("@controlXml", idXml) }); } } // 事务性执行所有SQL语句,确保数据一致性 Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList); m.outMsg = "保存" + (intType == 1 ? "标准版" : "个人版") + "配置成功!"; return ReturnDto.QuickReturn(m, ReturnCode.Success, "操作成功!"); } catch (Exception ex) { // 捕获保存布局配置时的异常,并将信息返回给前端 m.outMsg = "操作失败:" + ex.Message; Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel error:" + ex.Message); } return ReturnDto.QuickReturn(m, ReturnCode.Default, "操作成功!"); } /// /// 读取表单布局配置(标准版+个人版) /// /// 包含formPath字段的动态对象 /// 返回两个列表:list为标准版布局数据,list2为当前用户的个人版布局数据 /// /// 通过存储过程fm_get_layout同时返回共享布局和用户个人布局覆盖 /// 前端优先使用个人版布局,如无则使用标准版 /// [RequestMethod(RequestMethods.POST)] public ReturnDto GetModel([FromBody] dynamic model) { string formPath = model.formPath.ToString(); dynamic m = new ExpandoObject(); m.list = new List(); m.list2 = new List(); SqlParameter[] parameters = { new("@formPath", formPath), new("@userGuid", _userGuid), }; var dset = new DataSet(); try { // 存储过程返回标准版布局数据(Table[0])和用户个人布局快照(Table[1]) dset = DbHelperSQL.RunProcedure("[fm_get_layout]", parameters, "0"); if (dset != null && dset.Tables.Count > 0 ) { // Table[0]表示标准版定义;Table[1]保存用户的个人版布局快照 var _tb = dset.Tables[0].TableToDynamicList(); m.list = _tb; var _tb2 = dset.Tables[1].TableToDynamicList(); m.list2 = _tb2; } } catch (Exception ex) { // 记录读取失败但继续返回默认结果给调用者 LogHelper.Debug(ToString(), ex.Message); } if (m != null) return ReturnDto.QuickReturn(m, ReturnCode.Success, "读取成功!"); return ReturnDto.QuickReturn(m, ReturnCode.Default, "读取失败!"); } /// /// 根据版本读取序列化的布局字符串 /// /// 包含formPath字段的动态对象 /// 返回最新保存版本(标准版或个人版)的序列化布局字符串 /// /// 通过存储过程fm_get_layout_ver获取最新布局快照 /// 根据formPath和用户作用域返回序列化的布局字符串 /// [RequestMethod(RequestMethods.POST)] public ReturnDto GetModelByVersion([FromBody] dynamic model) { string formPath = model.formPath.ToString(); string strMsg = ""; SqlParameter[] parameters = { new("@formPath", formPath), new("@userGuid", _userGuid), }; var dset = new DataSet(); try { // 存储过程暴露基于formPath和用户作用域的最新序列化布局快照 dset = DbHelperSQL.RunProcedure("[fm_get_layout_ver]", parameters, "0"); if (dset != null && dset.Tables.Count > 0 ) { strMsg = dset.Tables[0].Rows[0][0].ToString(); } } catch (Exception ex) { // 捕获读取布局版本失败的上下文信息,帮助诊断环境特定问题 LogHelper.Debug(ToString(), ex.Message + ",formPath:" + formPath + ",_userGuid:" + _userGuid); } return ReturnDto.QuickReturn(strMsg, ReturnCode.Success, "读取成功!"); } #endregion /// /// 检查当前用户是否为管理员 /// /// 管理员返回1,非管理员返回0 /// 使用SYS_USER.IS_SYS标志判断调用者是否拥有管理员权限 private int? chkAdmin() { int? isAdmin = 0; System.Text.StringBuilder _sb = new System.Text.StringBuilder(); // 通过SYS_USER.IS_SYS标志判断调用者是否拥有管理员权限 _sb.Append("select count(1) from [dbo].[SYS_USER] where GUID='" + _userGuid + "' and IS_SYS=1"); object _obj = Gs.Toolbox.DbHelperSQL.GetSingle(_sb.ToString()); if (_obj == null) { isAdmin = 0; } else isAdmin = Gs.Toolbox.UtilityHelper.ToInt(_obj.ToString()); return isAdmin; } #region 发送erp新版本 /// /// 发送数据到ERP系统 /// /// 包含以下字段的动态对象: /// - keyGuid: 原生主键 /// - keyUserGuid: 操作用户GUID /// - keyProduce: 存储过程名 /// - keyTaskName: 任务名 /// - keyChild: 任务子节点名 /// - keyMeth: 方法名(如add、update、delete、toclose等) /// - keyNo: 单据编号 /// - keyUrl: 接口地址 /// - idtype: 特殊操作类型标识(如工单状态更新) /// - keyType: 操作类型(1=审核,0=反审核) /// /// 成功返回ERP响应消息,失败返回错误描述 /// /// 将MES数据打包成ERP载荷并根据请求的操作类型推送 /// 通过存储过程转换业务数据,调用InterfaceUtil推送到ERP /// [RequestMethod(RequestMethods.POST)] public string SendErp([FromBody] dynamic model) { int _rtnInt = 0; string _rtnStr = ""; try { // 构建ERP请求参数 string _erpJson = GetErpParam(model); if (_erpJson.Length <= 0) return "-1读取erp参数失败!"; string keyUserGuid = model.keyUserGuid; string keyGuid = model.keyGuid; string keyNo = model.keyNo; string idtype = model.idtype; // 仅在更新工单状态时使用 string keyUrl = model.keyUrl; if (string.IsNullOrEmpty(idtype)) { // 常规接口:按操作类型推送单条业务数据 (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo, 0, keyUrl); } else { // 带idtype的请求用于特殊流程(如关闭、反关闭),ERP需要额外的状态标记 (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo, 2, keyUrl); } } catch (Exception ex) { // 记录ERP数据转换异常,便于定位存储过程或序列化问题 Gs.Toolbox.LogHelper.Debug(this.ToString(), "Fm SendErp:" + ex.Message); return "发送erp失败:" + ex.Message; } if (_rtnInt <= 0) { return "发送erp失败:" + _rtnStr; } return _rtnStr; } /// /// 构建ERP参数(内部方法) /// /// 包含业务参数的动态对象 /// JSON格式的ERP参数字符串 /// /// 调用业务定义的存储过程将MES数据打包给ERP /// 根据keyMeth(操作方法)和idtype决定返回数据结构 /// private string GetErpParam(dynamic model) { string keyGuid = model.keyGuid; string keyUserGuid = model.keyUserGuid; string keyProduce = model.keyProduce; string keyTaskName = model.keyTaskName; string keyChild = model.keyChild; string keyMeth = model.keyMeth; string keyNo = model.keyNo; string idtype = model.idtype; // 仅在更新工单状态时使用 if (keyMeth.ToUpper() == "delete".ToUpper()) // 删除操作无需向ERP推送数据,只需返回空串 return ""; try { System.Data.DataSet dset = new System.Data.DataSet(); SqlParameter[] parameters = { new("@inOrderGuid", keyGuid), new("@inEdtUserGuid", keyUserGuid), new("@keyMeth", keyMeth.ToLower()), }; // 调用业务定义的存储过程,将MES数据打包给ERP dset = DbHelperSQL.RunProcedure(keyProduce, parameters, "0"); if (dset == null) return ""; if (dset.Tables.Count <= 0) return ""; if (dset.Tables[0].Rows.Count <= 0) return ""; // 常规接口处理逻辑 if (string.IsNullOrEmpty(idtype)) { // 常规出参:第一张表是主数据,第二张表(若存在)是子表集合 string _mesGuid = dset.Tables[0].Rows[0][0].ToString(); dynamic _datajson = new ExpandoObject(); if (dset.Tables.Count > 1) { // 多表返回时,需要把子表集合挂到datajson中 // 结案操作的结构与其他不一样,特殊处理 if (keyMeth.ToLower() == "toclose".ToLower() || keyMeth.ToLower() == "closure".ToLower() || keyMeth.ToLower() == "unfinish") { _datajson = dset.Tables[1].Rows[0].RowToDynamic(); } else { _datajson = dset.Tables[0].Rows[0].RowToDynamic(); List _lst = dset.Tables[1].TableToDynamicList(); ((IDictionary)_datajson)[keyChild] = _lst; } } else if (dset.Tables.Count == 1) { _datajson = dset.Tables[0].Rows[0].RowToDynamic(); } // var _obj = new // { // mesid = _mesGuid, // taskname = keyTaskName, // optype = keyMeth, // datajson = JsonConvert.SerializeObject(_datajson), // }; // return JsonConvert.SerializeObject(_obj); return JsonConvert.SerializeObject(_datajson); } // 订单回传标识处理逻辑(带idtype) List _datajson22 = new List(); dynamic _ob = new ExpandoObject(); _ob.ENTRY = dset.Tables[0].TableToDynamicList(); _datajson22.Add(_ob); // var _obj22 = new // { // taskname = keyTaskName, // idtype = idtype, // datajson = JsonConvert.SerializeObject(_datajson22), // }; // return JsonConvert.SerializeObject(_obj22); return JsonConvert.SerializeObject(_datajson22); } catch (Exception ex) { // 记录ERP数据转换异常,便于定位存储过程或序列化问题 Gs.Toolbox.LogHelper.Debug(this.ToString(), ex.Message); throw ex; } } #endregion #region 查询 /// /// 读取查询配置 /// /// 包含formPath和list(列定义数组)的动态对象 /// 返回4个列表:list=查询条件配置,list2=结果字段配置,list3=排序配置,list4=其他配置 /// /// 将列名和显示标题拼成"~"分隔的参数,传给存储过程fm_set_query生成查询配置 /// 存储过程返回多张配置表用于动态查询构建 /// [RequestMethod(RequestMethods.POST)] public ReturnDto GetQuery([FromBody] dynamic model) { dynamic m = new ExpandoObject(); m.list = new List(); m.list2 = new List(); m.list3 = new List(); m.list4 = new List(); var _split = "|"; string formPath = model.formPath.ToString(); System.Text.StringBuilder _sb = new System.Text.StringBuilder(); foreach (var _obj in model.list) { var _line = _obj.colName + _split + _obj.colCap + _split ; if (_sb.Length > 0) _sb.Append("~"); _sb.Append(_line); } ; var lst = new List(); // 将列名和显示标题拼成"~"分隔的参数,传给存储过程生成查询配置 SqlParameter[] parameters = { new("@formPath", formPath), new("@colArray", _sb.ToString()), }; var dset = new DataSet(); try { // fm_set_query会返回查询条件、结果字段、排序等多张配置表 dset = DbHelperSQL.RunProcedure("[fm_set_query]", parameters, "0"); if (dset != null && dset.Tables.Count > 0) { m.list = dset.Tables[0].TableToDynamicList(); m.list2 = dset.Tables[1].TableToDynamicList(); m.list3 = dset.Tables[2].TableToDynamicList(); m.list4 = dset.Tables[3].TableToDynamicList(); } } catch (Exception ex) { // 记录查询配置读取异常 LogHelper.Debug(ToString(), ex.Message); } return ReturnDto.QuickReturn(m, ReturnCode.Success, "读取成功!"); } /// /// 编辑查询数据源表配置 /// /// 包含formPath和list(表名数组)的动态对象 /// 操作结果,包含outMsg消息 /// /// 仅管理员可操作,用于配置查询的数据来源表 /// 先清空原有配置,再批量插入最新表配置到FM_QUERY_TABLE /// [RequestMethod(RequestMethods.POST)] public ReturnDto EditQuery([FromBody] dynamic model) { dynamic m = new ExpandoObject(); m.outMsg = ""; string formPath = model.formPath; ArrayList arrayList = new ArrayList(); string _groupGuid = Guid.NewGuid().ToString(); int? isAdmin = 0; try { isAdmin = chkAdmin(); if (isAdmin <= 0) { // 查询配置仅限管理员操作,以保护共享元数据 m.outMsg = "你不是管理员,操作失败!"; return ReturnDto.QuickReturn(m, ReturnCode.Default, "操作成功!"); } } catch (Exception ex) { // 记录管理员权限检查异常 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error:" + ex.Message); } try { // 先清空原有查询来源表,再批量插入最新配置 Gs.Toolbox.DbHelperSQL.ExecuteSql( "delete from [FM_QUERY_TABLE] where formPath=@formPath ", new SqlParameter[] { new SqlParameter("@formPath", formPath) }); foreach (var _obj in model.list) { System.Text.StringBuilder _sb = new System.Text.StringBuilder(); _sb.Append( "INSERT INTO [dbo].[FM_QUERY_TABLE]([guid],[formPath] ,[tableName] ,[lastUpdateBy] ,[lastUpdateDate],[tableOtherName])"); _sb.Append(" values(newid(),'" + formPath + "','" + _obj.tableName + "','',getdate(),'" + _obj.tableOtherName + "')"); arrayList.Add(_sb.ToString()); } Gs.Toolbox.DbHelperSQL.ExecuteSqlTran(arrayList); } catch (Exception ex) { // 捕获保存查询配置时的异常,并将信息返回给前端 m.outMsg = ex.Message; return ReturnDto.QuickReturn(m, ReturnCode.Default, ex.Message); } m.outMsg = "操作成功!"; return ReturnDto.QuickReturn(m, ReturnCode.Default, "操作成功!"); } /// /// 删除查询数据源表配置 /// /// 包含guid字段的动态对象 /// 操作结果码 /// 仅管理员可操作,采用GUID精确删除指定的查询数据源记录 [RequestMethod(RequestMethods.POST)] public ReturnDto DeleteQuery([FromBody] dynamic model) { int? rtnInt = (int)ReturnCode.Default; int? isAdmin = 0; try { isAdmin = chkAdmin(); if (isAdmin <= 0) { // 删除查询配置同样需要管理员权限 return ReturnDto.QuickReturn(rtnInt, ReturnCode.Default, "你不是管理员,操作失败!"); } } catch (Exception ex) { // 记录管理员权限检查异常 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error:" + ex.Message); } Guid? guid = model.guid; System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); // 采用GUID精确删除指定的查询数据源记录 stringBuilder.Append("delete from FM_QUERY_TABLE where guid='" + guid + "'"); rtnInt = Gs.Toolbox.DbHelperSQL.ExecuteSql(stringBuilder.ToString()); if (rtnInt <= 0) return ReturnDto.QuickReturn(rtnInt, ReturnCode.Exception, "操作失败!"); return ReturnDto.QuickReturn(rtnInt, ReturnCode.Success, "操作成功!"); } /// /// 编辑查询列字段配置 /// /// 包含以下字段的动态对象: /// - guid: 记录GUID /// - sqlField: SQL字段名 /// - sqlFieldType: SQL字段类型 /// - fType: 操作类型(1=更新字段类型,其他=更新字段名) /// /// 操作结果码 /// 仅管理员可操作,用于调整查询列字段映射 [RequestMethod(RequestMethods.POST)] public ReturnDto EditCol([FromBody] dynamic model) { int? rtnInt = (int)ReturnCode.Default; int? isAdmin = 0; try { isAdmin = chkAdmin(); if (isAdmin <= 0) { // 只有管理员才能调整查询列字段映射 return ReturnDto.QuickReturn(rtnInt, ReturnCode.Default, "你不是管理员,操作失败!"); } } catch (Exception ex) { // 记录管理员权限检查异常 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error:" + ex.Message); } Guid? guid = model.guid; string sqlField = model.sqlField; string sqlFieldType = model.sqlFieldType; string fType = model.fType; System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); // 根据fType决定更新字段类型还是字段名 if (fType == "1") stringBuilder.Append("update FM_QUERY set sqlFieldType='" + sqlFieldType + "', lastUpdateDate=getdate() where guid='" + guid + "'"); else stringBuilder.Append("update FM_QUERY set sqlField='" + sqlField + "', lastUpdateDate=getdate() where guid='" + guid + "'"); rtnInt = Gs.Toolbox.DbHelperSQL.ExecuteSql(stringBuilder.ToString()); if (rtnInt <= 0) return ReturnDto.QuickReturn(rtnInt, ReturnCode.Exception, "操作成功!"); return ReturnDto.QuickReturn(rtnInt, ReturnCode.Success, "操作失败!"); } #endregion } }