tjx
2025-10-11 75ff22c91d7710573231ba3ed75259f3d1477cc8
WebApi/Gs.Sys/Services/FmController.cs
@@ -28,25 +28,29 @@
                GetUserGuidAndOrgGuid(_http);
        }
        #region 版面
        #region 布局配置
        /// <summary>
        ///     增加
        ///     保存或清空表单布局
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <remarks>Saves layouts: intType 1=standard save, 2=personal save, 3=clear standard, 4=clear personal.</remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<ExpandoObject> EditModel([FromBody] dynamic model)
        {
            string applyUserGuid = "";
            string formPath = model.formPath;
            int intType = model.intType;
            // intType: 1 = save standard layout, 2 = save personal layout, 3 = clear standard layout, 4 = clear personal layout.
            dynamic m = new ExpandoObject();
            m.outMsg = "";
            // Gather batched SQL statements so they can be executed transactionally when saving layouts.
            Hashtable SQLStringList = new Hashtable();
            string _groupGuid = Guid.NewGuid().ToString();
            //只有超级管理员权限
            if (intType == 1 || intType == 3)
            {
                // Validate the current user has administrator rights when touching standard layouts.
                int? isAdmin = 0;
                try
                {
@@ -65,6 +69,7 @@
            //保存标准版
            if (intType == 1)
            {
                // Persist a new standard layout definition shared by all users.
                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) });
@@ -72,6 +77,7 @@
            //保存个人版本
            if (intType == 2)
            {
                // Persist the caller's personal layout copy scoped to their user 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) });
@@ -79,6 +85,7 @@
            //清空标准版本
            if (intType == 3)
            {
                // Administrators can wipe the shared standard layout entirely.
                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);
@@ -88,6 +95,7 @@
            //清空个人版本
            if (intType == 4)
            {
                // Remove the caller's personal layout while keeping the shared standard version intact.
                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);
@@ -103,6 +111,7 @@
                    JObject job = (JObject)jsonitem;
                    if (job["idName"] != null)
                    {
                        // Compose an insert statement for each UI control (grid, layout panel, splitter, etc.).
                        string idName = job["idName"].ToString();
                        string idXml = job["idXml"].ToString();
                        string idType = job["idType"].ToString();
@@ -127,6 +136,7 @@
            }
            catch (Exception ex)
            {
                // 捕获保存查询配置时的异常,并将信息返回给前端。
                m.outMsg = "操作失败:" + ex.Message;
                Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel error:" + ex.Message);
            }
@@ -140,6 +150,7 @@
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        /// <remarks>Combines the shared layout (list) with the current user's override (list2).</remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<ExpandoObject> GetModel([FromBody] dynamic model)
        {
@@ -155,10 +166,12 @@
            var dset = new DataSet();
            try
            {
                // Stored procedure returns both standard layout data and any personal override for the current user.
                dset = DbHelperSQL.RunProcedure("[fm_get_layout]", parameters, "0");
                if (dset != null && dset.Tables.Count > 0
                 )
                {
                    // Table[0] represents the standard definition; table[1] holds the user's personal layout snapshot.
                    var _tb = dset.Tables[0].TableToDynamicList();
                    m.list = _tb;
                    var _tb2 = dset.Tables[1].TableToDynamicList();
@@ -167,6 +180,7 @@
            }
            catch (Exception ex)
            {
                // Log retrieval failure but continue returning default result to caller.
                LogHelper.Debug(ToString(), ex.Message);
            }
            if (m != null)
@@ -182,6 +196,7 @@
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        /// <remarks>Retrieves the serialized layout string for the latest saved version (standard or personal).</remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<string> GetModelByVersion([FromBody] dynamic model)
        {
@@ -195,6 +210,7 @@
            var dset = new DataSet();
            try
            {
                // Stored procedure exposes the latest serialized layout snapshot based on formPath and user scope.
                dset = DbHelperSQL.RunProcedure("[fm_get_layout_ver]", parameters, "0");
                if (dset != null && dset.Tables.Count > 0
                 )
@@ -204,6 +220,7 @@
            }
            catch (Exception ex)
            {
                // Capture context when reading layout versions fails to help diagnose environment-specific issues.
                LogHelper.Debug(ToString(), ex.Message+ ",formPath:"+ formPath+ ",_userGuid:"+ _userGuid);
            }
@@ -217,6 +234,7 @@
        {
            int? isAdmin = 0;
            System.Text.StringBuilder _sb = new System.Text.StringBuilder();
            // Uses SYS_USER.IS_SYS flag to decide if the caller has elevated privileges.
            _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)
@@ -235,6 +253,7 @@
        /// </summary>
        /// <param name="model">keyType:1审核,0反审核</param>
        /// <returns></returns>
        /// <remarks>Packages MES data into ERP payloads and posts them according to the requested operation.</remarks>
        [RequestMethod(RequestMethods.POST)]
        public string SendErp([FromBody] dynamic model)
        {
@@ -245,6 +264,7 @@
            //string keyChild = model.keyChild;任务子节点名
            //string keyMeth = model.keyMeth;方法名
            //string keyNo = model.keyNo;单据编号
            //string keyUrl = model.keyUrl;接口地址
            int _rtnInt = 0;
            string _rtnStr = "";
            try
@@ -256,13 +276,21 @@
                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);
                {
                    // 常规接口:按操作类型推送单条业务数据。
                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo,0,keyUrl);
                }
                else
                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo, 2);
                {
                    // 带 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;
            }
@@ -289,6 +317,7 @@
            string keyNo = model.keyNo;
            string idtype = model.idtype;//这个仅仅是更新工单状态的时候有
            if (keyMeth.ToUpper() == "delete".ToUpper())
                // 删除操作无需向 ERP 推送数据,只需返回空串。
                return "";
            try
            {
@@ -299,6 +328,7 @@
                       new("@inEdtUserGuid", keyUserGuid),
                       new("@keyMeth", keyMeth.ToLower()),
                   };
                // 调用业务定义的存储过程,将 MES 数据打包给 ERP。
                dset = DbHelperSQL.RunProcedure(keyProduce, parameters, "0");
                if (dset == null)
                    return "";
@@ -309,10 +339,12 @@
                //这是普通的接口
                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")
                        {
@@ -325,30 +357,33 @@
                            ((IDictionary<string, object>)_datajson)[keyChild] = _lst;
                        }
                    }
                    var _obj = new
                    {
                        mesid = _mesGuid,
                        taskname = keyTaskName,
                        optype = keyMeth,
                        datajson = JsonConvert.SerializeObject(_datajson),
                    };
                    return JsonConvert.SerializeObject(_obj);
                    // var _obj = new
                    // {
                    //     mesid = _mesGuid,
                    //     taskname = keyTaskName,
                    //     optype = keyMeth,
                    //     datajson = JsonConvert.SerializeObject(_datajson),
                    // };
                    // return JsonConvert.SerializeObject(_obj);
                    return JsonConvert.SerializeObject(_datajson);
                }
                //这是订单回传标识
                List<dynamic> _datajson22 = new List<dynamic>();
                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);
                // 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;
            }
@@ -386,6 +421,7 @@
            }
            ;
            var lst = new List<dynamic>();
            // 将列名和显示标题拼成 "~" 分隔的参数,传给存储过程生成查询配置。
            SqlParameter[] parameters =
            {
                new("@formPath", formPath),
@@ -394,6 +430,7 @@
            var dset = new DataSet();
            try
            {
                // fm_set_query 会返回查询条件、结果字段、排序等多张配置表。
                dset = DbHelperSQL.RunProcedure("[fm_set_query]", parameters, "0");
                if (dset != null && dset.Tables.Count > 0)
                {
@@ -405,6 +442,7 @@
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                LogHelper.Debug(ToString(), ex.Message);
            }
            return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Success, "读取成功!");
@@ -429,16 +467,19 @@
                isAdmin = chkAdmin();
                if (isAdmin <= 0)
                {
                    // Query configuration is restricted to administrators to protect shared metadata.
                    m.outMsg = "你不是管理员,操作失败!";
                    return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default, "操作成功!");
                }
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                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)
                {
@@ -451,6 +492,7 @@
            }
            catch (Exception ex)
            {
                // 捕获保存查询配置时的异常,并将信息返回给前端。
                m.outMsg = ex.Message;
                return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default, ex.Message);
            }
@@ -473,15 +515,18 @@
                isAdmin = chkAdmin();
                if (isAdmin <= 0)
                {
                    // 删除查询配置同样需要管理员权限。
                    return ReturnDto<int>.QuickReturn(rtnInt, ReturnCode.Default, "你不是管理员,操作失败!");
                }
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                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)
@@ -504,11 +549,13 @@
                isAdmin = chkAdmin();
                if (isAdmin <= 0)
                {
                    // 只有管理员才能调整查询列字段映射。
                    return ReturnDto<int>.QuickReturn(rtnInt, ReturnCode.Default, "你不是管理员,操作失败!");
                }
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error:" + ex.Message);
            }
            Guid? guid = model.guid;