tjx
2025-10-11 cdd53f350537ee64fa7c9c375f1abdeb51a7294f
FmController类更新详细注释
已修改2个文件
252 ■■■■■ 文件已修改
WebApi/CLAUDE.md 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WebApi/Gs.Sys/Services/FmController.cs 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WebApi/CLAUDE.md
@@ -23,6 +23,7 @@
  - **Gs.QiTaCk** / **Gs.QiTaRk**: Miscellaneous in/out operations (其它出库/其它入库)
  - **Gs.Ww**: Outsourcing management (委外)
  - **Gs.JJGZ**: Piece-rate wage management (计件工资)
  - **Gs.OpenApi**: External API integration and open interfaces
## Development Commands
@@ -39,9 +40,15 @@
```
### Development Server
- API runs on `http://localhost:5263` (Project profile) or `http://localhost:37005` (IIS Express)
- Swagger UI: `http://localhost:5263/swagger`
- CORS configured to allow all origins in development
- API runs on `http://localhost:5263` (default Project profile) or `http://localhost:37005` (IIS Express)
- Ports configured in `Gs.HostIIS/Properties/launchSettings.json`
- Swagger UI: `http://localhost:5263/swagger` (always enabled, not just in Development)
- CORS configured to allow all origins via "AllowAll" policy
### Testing
- No automated tests currently exist in the solution
- Manual testing via Swagger UI at `/swagger` endpoint
- Test ERP integration endpoints configured in appsettings.json (`TestErpUrl`, `TestErpUrl2`)
## Architecture Details
WebApi/Gs.Sys/Services/FmController.cs
@@ -14,12 +14,31 @@
namespace Gs.Sys.Services
{
    /// <summary>
    /// 表单管理控制器
    /// 负责管理前端表单的UI布局配置(标准版/个人版)、ERP数据同步推送、动态查询配置等功能
    /// </summary>
    [ApiGroup(ApiGroupNames.FM)]
    public class FmController : IRomteService
    {
        private readonly IHttpContextAccessor _http;
        private readonly string _userCode, _userGuid, _orgFids;
        /// <summary>
        /// 当前用户编码
        /// </summary>
        private readonly string _userCode;
        /// <summary>
        /// 当前用户GUID
        /// </summary>
        private readonly string _userGuid;
        /// <summary>
        /// 当前用户所属组织FID路径
        /// </summary>
        private readonly string _orgFids;
        /// <summary>
        /// 构造函数,从HTTP上下文中提取当前用户信息
        /// </summary>
        /// <param name="httpContextAccessor">HTTP上下文访问器</param>
        public FmController(IHttpContextAccessor httpContextAccessor)
        {
            _http = httpContextAccessor;
@@ -30,27 +49,35 @@
        #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>
        /// <param name="model">包含以下字段的动态对象:
        /// - formPath: 表单路径标识
        /// - intType: 操作类型(1=保存标准版,2=保存个人版,3=清空标准版,4=清空个人版)
        /// - xmlList: UI控件配置列表(JArray格式),每个包含idName、idXml、idType、splitterPosition
        /// </param>
        /// <returns>返回操作结果,包含outMsg消息字段</returns>
        /// <remarks>
        /// 标准版布局:所有用户共享,需要管理员权限操作
        /// 个人版布局:用户私有,优先级高于标准版
        /// 布局数据存储在FM_LAYOUT表,通过groupGuid进行批量关联
        /// </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.
            // intType含义:1=保存标准版,2=保存个人版,3=清空标准版,4=清空个人版
            dynamic m = new ExpandoObject();
            m.outMsg = "";
            // Gather batched SQL statements so they can be executed transactionally when saving layouts.
            // 收集批量SQL语句,用于事务性执行保存布局操作
            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
                {
@@ -69,10 +96,10 @@
                }
            }
            //保存标准版
            // 保存标准版布局:清除旧的标准版数据,applyUserGuid为null表示全局共享
            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 +
@@ -82,10 +109,10 @@
                // 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)
            {
                // Persist the caller's personal layout copy scoped to their user GUID.
                // 保存调用者的个人布局副本,通过用户GUID限定作用域
                applyUserGuid = _userGuid;
                Gs.Toolbox.DbHelperSQL.ExecuteSql(
                    "delete from [FM_LAYOUT] where  groupGuid<>'" + _groupGuid +
@@ -98,10 +125,10 @@
                //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)
            {
                // Administrators can wipe the shared standard layout entirely.
                // 管理员可以完全删除共享的标准版布局,恢复到未配置状态
                applyUserGuid = null;
                SQLStringList.Add(
                    "delete from [FM_LAYOUT] where [formPath]=@formPath and applyUserGuid is null",
@@ -113,10 +140,10 @@
                    "操作成功!");
            }
            //清空个人版本
            // 清空个人版布局:删除当前用户的个人版布局,保留标准版不受影响
            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",
@@ -131,7 +158,7 @@
                    "操作成功!");
            }
            //这是所有的gridview,lay xml
            // 遍历所有UI控件配置(gridview、layout、xml等),批量插入数据库
            JArray jArray = model.xmlList;
            try
            {
@@ -140,7 +167,7 @@
                    JObject job = (JObject)jsonitem;
                    if (job["idName"] != null)
                    {
                        // Compose an insert statement for each UI control (grid, layout panel, splitter, etc.).
                        // 为每个UI控件(网格、布局面板、分割器等)构建插入语句
                        string idName = job["idName"].ToString();
                        string idXml = job["idXml"].ToString();
                        string idType = job["idType"].ToString();
@@ -170,6 +197,7 @@
                    }
                }
                // 事务性执行所有SQL语句,确保数据一致性
                Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList);
                m.outMsg = "保存" + (intType == 1 ? "标准版" : "个人版") + "配置成功!";
                return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Success,
@@ -177,24 +205,26 @@
            }
            catch (Exception ex)
            {
                // 捕获保存查询配置时的异常,并将信息返回给前端。
                // 捕获保存布局配置时的异常,并将信息返回给前端
                m.outMsg = "操作失败:" + ex.Message;
                Gs.Toolbox.LogHelper.Debug(this.ToString(),
                    "EditModel error:" + ex.Message);
            }
            //这是所有的
            return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default,
                "操作成功!");
        }
        /// <summary>
        ///     读取
        /// 读取表单布局配置(标准版+个人版)
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        /// <remarks>Combines the shared layout (list) with the current user's override (list2).</remarks>
        /// <param name="model">包含formPath字段的动态对象</param>
        /// <returns>返回两个列表:list为标准版布局数据,list2为当前用户的个人版布局数据</returns>
        /// <remarks>
        /// 通过存储过程fm_get_layout同时返回共享布局和用户个人布局覆盖
        /// 前端优先使用个人版布局,如无则使用标准版
        /// </remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<ExpandoObject> GetModel([FromBody] dynamic model)
        {
@@ -210,13 +240,13 @@
            var dset = new DataSet();
            try
            {
                // Stored procedure returns both standard layout data and any personal override for the current user.
                // 存储过程返回标准版布局数据(Table[0])和用户个人布局快照(Table[1])
                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.
                    // Table[0]表示标准版定义;Table[1]保存用户的个人版布局快照
                    var _tb = dset.Tables[0].TableToDynamicList();
                    m.list = _tb;
                    var _tb2 = dset.Tables[1].TableToDynamicList();
@@ -225,7 +255,7 @@
            }
            catch (Exception ex)
            {
                // Log retrieval failure but continue returning default result to caller.
                // 记录读取失败但继续返回默认结果给调用者
                LogHelper.Debug(ToString(), ex.Message);
            }
@@ -238,11 +268,14 @@
        /// <summary>
        ///     读取
        /// 根据版本读取序列化的布局字符串
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        /// <remarks>Retrieves the serialized layout string for the latest saved version (standard or personal).</remarks>
        /// <param name="model">包含formPath字段的动态对象</param>
        /// <returns>返回最新保存版本(标准版或个人版)的序列化布局字符串</returns>
        /// <remarks>
        /// 通过存储过程fm_get_layout_ver获取最新布局快照
        /// 根据formPath和用户作用域返回序列化的布局字符串
        /// </remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<string> GetModelByVersion([FromBody] dynamic model)
        {
@@ -256,7 +289,7 @@
            var dset = new DataSet();
            try
            {
                // Stored procedure exposes the latest serialized layout snapshot based on formPath and user scope.
                // 存储过程暴露基于formPath和用户作用域的最新序列化布局快照
                dset = DbHelperSQL.RunProcedure("[fm_get_layout_ver]",
                    parameters, "0");
                if (dset != null && dset.Tables.Count > 0
@@ -267,7 +300,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);
@@ -279,11 +312,16 @@
        #endregion
        /// <summary>
        /// 检查当前用户是否为管理员
        /// </summary>
        /// <returns>管理员返回1,非管理员返回0</returns>
        /// <remarks>使用SYS_USER.IS_SYS标志判断调用者是否拥有管理员权限</remarks>
        private int? chkAdmin()
        {
            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.
            // 通过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());
@@ -300,50 +338,57 @@
        #region 发送erp新版本
        /// <summary>
        ///
        /// 发送数据到ERP系统
        /// </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>
        /// <param name="model">包含以下字段的动态对象:
        /// - keyGuid: 原生主键
        /// - keyUserGuid: 操作用户GUID
        /// - keyProduce: 存储过程名
        /// - keyTaskName: 任务名
        /// - keyChild: 任务子节点名
        /// - keyMeth: 方法名(如add、update、delete、toclose等)
        /// - keyNo: 单据编号
        /// - keyUrl: 接口地址
        /// - idtype: 特殊操作类型标识(如工单状态更新)
        /// - keyType: 操作类型(1=审核,0=反审核)
        /// </param>
        /// <returns>成功返回ERP响应消息,失败返回错误描述</returns>
        /// <remarks>
        /// 将MES数据打包成ERP载荷并根据请求的操作类型推送
        /// 通过存储过程转换业务数据,调用InterfaceUtil推送到ERP
        /// </remarks>
        [RequestMethod(RequestMethods.POST)]
        public string SendErp([FromBody] 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 keyUrl = model.keyUrl;接口地址
            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 idtype = model.idtype; // 仅在更新工单状态时使用
                string keyUrl = model.keyUrl;
                if (string.IsNullOrEmpty(idtype))
                {
                    // 常规接口:按操作类型推送单条业务数据。
                    // 常规接口:按操作类型推送单条业务数据
                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson,
                        keyUserGuid, keyGuid, keyNo, 0, keyUrl);
                }
                else
                {
                    // 带 idtype 的请求用于特殊流程(如关闭、反关闭),ERP 需要额外的状态标记。
                    // 带idtype的请求用于特殊流程(如关闭、反关闭),ERP需要额外的状态标记
                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson,
                        keyUserGuid, keyGuid, keyNo, 2, keyUrl);
                }
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                // 记录ERP数据转换异常,便于定位存储过程或序列化问题
                Gs.Toolbox.LogHelper.Debug(this.ToString(),
                    "Fm SendErp:" + ex.Message);
                return "发送erp失败:" + ex.Message;
@@ -358,10 +403,14 @@
        }
        /// <summary>
        /// 构建erp参数
        /// 构建ERP参数(内部方法)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <param name="model">包含业务参数的动态对象</param>
        /// <returns>JSON格式的ERP参数字符串</returns>
        /// <remarks>
        /// 调用业务定义的存储过程将MES数据打包给ERP
        /// 根据keyMeth(操作方法)和idtype决定返回数据结构
        /// </remarks>
        private string GetErpParam(dynamic model)
        {
            string keyGuid = model.keyGuid;
@@ -371,9 +420,9 @@
            string keyChild = model.keyChild;
            string keyMeth = model.keyMeth;
            string keyNo = model.keyNo;
            string idtype = model.idtype; //这个仅仅是更新工单状态的时候有
            string idtype = model.idtype; // 仅在更新工单状态时使用
            if (keyMeth.ToUpper() == "delete".ToUpper())
                // 删除操作无需向 ERP 推送数据,只需返回空串。
                // 删除操作无需向ERP推送数据,只需返回空串
                return "";
            try
            {
@@ -384,7 +433,7 @@
                    new("@inEdtUserGuid", keyUserGuid),
                    new("@keyMeth", keyMeth.ToLower()),
                };
                // 调用业务定义的存储过程,将 MES 数据打包给 ERP。
                // 调用业务定义的存储过程,将MES数据打包给ERP
                dset = DbHelperSQL.RunProcedure(keyProduce, parameters, "0");
                if (dset == null)
                    return "";
@@ -392,16 +441,16 @@
                    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 中。
                        //这是这是普通的接口里的结案,结构和其它不一样
                        // 多表返回时,需要把子表集合挂到datajson中
                        // 结案操作的结构与其他不一样,特殊处理
                        if (keyMeth.ToLower() == "toclose".ToLower() ||
                            keyMeth.ToLower() == "closure".ToLower() ||
                            keyMeth.ToLower() == "unfinish")
@@ -433,7 +482,7 @@
                    return JsonConvert.SerializeObject(_datajson);
                }
                //这是订单回传标识
                // 订单回传标识处理逻辑(带idtype)
                List<dynamic> _datajson22 = new List<dynamic>();
                dynamic _ob = new ExpandoObject();
                _ob.ENTRY = dset.Tables[0].TableToDynamicList();
@@ -449,7 +498,7 @@
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                // 记录ERP数据转换异常,便于定位存储过程或序列化问题
                Gs.Toolbox.LogHelper.Debug(this.ToString(), ex.Message);
                throw ex;
            }
@@ -461,10 +510,14 @@
        #region 查询
        /// <summary>
        /// 读取
        /// 读取查询配置
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <param name="model">包含formPath和list(列定义数组)的动态对象</param>
        /// <returns>返回4个列表:list=查询条件配置,list2=结果字段配置,list3=排序配置,list4=其他配置</returns>
        /// <remarks>
        /// 将列名和显示标题拼成"~"分隔的参数,传给存储过程fm_set_query生成查询配置
        /// 存储过程返回多张配置表用于动态查询构建
        /// </remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<ExpandoObject> GetQuery([FromBody] dynamic model)
        {
@@ -489,7 +542,7 @@
            ;
            var lst = new List<dynamic>();
            // 将列名和显示标题拼成 "~" 分隔的参数,传给存储过程生成查询配置。
            // 将列名和显示标题拼成"~"分隔的参数,传给存储过程生成查询配置
            SqlParameter[] parameters =
            {
                new("@formPath", formPath),
@@ -498,7 +551,7 @@
            var dset = new DataSet();
            try
            {
                // fm_set_query 会返回查询条件、结果字段、排序等多张配置表。
                // fm_set_query会返回查询条件、结果字段、排序等多张配置表
                dset = DbHelperSQL.RunProcedure("[fm_set_query]", parameters,
                    "0");
                if (dset != null && dset.Tables.Count > 0)
@@ -511,7 +564,7 @@
            }
            catch (Exception ex)
            {
                // 记录 ERP 数据转换异常,便于定位存储过程或序列化问题。
                // 记录查询配置读取异常
                LogHelper.Debug(ToString(), ex.Message);
            }
@@ -520,10 +573,14 @@
        }
        /// <summary>
        /// 编辑表
        /// 编辑查询数据源表配置
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <param name="model">包含formPath和list(表名数组)的动态对象</param>
        /// <returns>操作结果,包含outMsg消息</returns>
        /// <remarks>
        /// 仅管理员可操作,用于配置查询的数据来源表
        /// 先清空原有配置,再批量插入最新表配置到FM_QUERY_TABLE
        /// </remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<ExpandoObject> EditQuery([FromBody] dynamic model)
        {
@@ -538,7 +595,7 @@
                isAdmin = chkAdmin();
                if (isAdmin <= 0)
                {
                    // Query configuration is restricted to administrators to protect shared metadata.
                    // 查询配置仅限管理员操作,以保护共享元数据
                    m.outMsg = "你不是管理员,操作失败!";
                    return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default,
                        "操作成功!");
@@ -546,14 +603,14 @@
            }
            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[]
@@ -574,7 +631,7 @@
            }
            catch (Exception ex)
            {
                // 捕获保存查询配置时的异常,并将信息返回给前端。
                // 捕获保存查询配置时的异常,并将信息返回给前端
                m.outMsg = ex.Message;
                return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default,
                    ex.Message);
@@ -586,10 +643,11 @@
        }
        /// <summary>
        ///   删除表
        /// 删除查询数据源表配置
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <param name="model">包含guid字段的动态对象</param>
        /// <returns>操作结果码</returns>
        /// <remarks>仅管理员可操作,采用GUID精确删除指定的查询数据源记录</remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<int?> DeleteQuery([FromBody] dynamic model)
        {
@@ -600,14 +658,14 @@
                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);
            }
@@ -615,7 +673,7 @@
            Guid? guid = model.guid;
            System.Text.StringBuilder stringBuilder =
                new System.Text.StringBuilder();
            // 采用 GUID 精确删除指定的查询数据源记录。
            // 采用GUID精确删除指定的查询数据源记录
            stringBuilder.Append("delete from FM_QUERY_TABLE where guid='" +
                                 guid + "'");
            rtnInt =
@@ -628,10 +686,16 @@
        }
        /// <summary>
        ///   编辑列
        /// 编辑查询列字段配置
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <param name="model">包含以下字段的动态对象:
        /// - guid: 记录GUID
        /// - sqlField: SQL字段名
        /// - sqlFieldType: SQL字段类型
        /// - fType: 操作类型(1=更新字段类型,其他=更新字段名)
        /// </param>
        /// <returns>操作结果码</returns>
        /// <remarks>仅管理员可操作,用于调整查询列字段映射</remarks>
        [RequestMethod(RequestMethods.POST)]
        public ReturnDto<int?> EditCol([FromBody] dynamic model)
        {
@@ -642,14 +706,14 @@
                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);
            }
@@ -660,6 +724,7 @@
            string fType = model.fType;
            System.Text.StringBuilder stringBuilder =
                new System.Text.StringBuilder();
            // 根据fType决定更新字段类型还是字段名
            if (fType == "1")
                stringBuilder.Append("update  FM_QUERY set sqlFieldType='" +
                                     sqlFieldType +