【069】配置文件读取
侧边栏壁纸
  • 累计撰写 70 篇文章
  • 累计收到 2 条评论

【069】配置文件读取

竹秋廿九
2026-01-30 / 0 评论 / 1 阅读 / 正在检测是否收录...
支持热更新,更新时间相差3秒以上的会自动重新加载
修改配置文件不用重启IIS服务

OpenApiConfig

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace UFIDA.U9.SH.OPENAPISV.Common
{
    /// <summary>
    /// 接口配置信息
    /// </summary>
    public class OpenApiConfig
    {
        private static Dictionary<string, string> _configCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private static DateTime _lastWriteTime;
        private static DateTime _lastCheckTime;
        private static readonly object _lock = new object();
        private const int CheckIntervalSeconds = 3;
        private const string ConfigName = "SH_OpenApiConfig.config";
        private static readonly string FilePath;

        static OpenApiConfig()
        {
            string basePath = AppDomain.CurrentDomain.BaseDirectory;

            // 核心逻辑:智能定位 Portal 目录(保证U9主服务、调度服务、审批流服务都统一读取Portal目录下的配置文件)
            // 兼容:Portal, Portal/AppServer, MailService, MailService/Libs
            string portalPath = FindPortalDirectory(basePath);

            FilePath = Path.Combine(portalPath, ConfigName);
        }

        /// <summary>
        /// 智能查找 Portal 目录(纯字符串操作优化版)
        /// </summary>
        private static string FindPortalDirectory(string startPath)
        {
            string currentPath = startPath;

            // 循环直到根目录(GetDirectoryName 返回 null 表示到达根目录)
            while (!string.IsNullOrEmpty(currentPath))
            {
                // 1. 获取当前文件夹名称
                // 注意:Path.GetFileName 对文件夹路径使用时,返回的是最后一级文件夹的名字
                string dirName = Path.GetFileName(currentPath.TrimEnd(Path.DirectorySeparatorChar));

                // 2. 【自身匹配】如果当前就是 Portal
                if (string.Equals(dirName, "Portal", StringComparison.OrdinalIgnoreCase))
                {
                    return currentPath;
                }

                // 3. 【子目录匹配】检查当前目录下是否有 Portal 子文件夹
                // 场景:在 C:\yonyou\U9CE 下发现了 Portal 子目录
                string potentialPortal = Path.Combine(currentPath, "Portal");
                if (Directory.Exists(potentialPortal))
                {
                    return potentialPortal;
                }

                // 4. 向上移动一级
                currentPath = Path.GetDirectoryName(currentPath);
            }

            // 如果没找到,返回原始路径
            return startPath;
        }

        private static void EnsureConfigLoaded()
        {
            if ((DateTime.Now - _lastCheckTime).TotalSeconds < CheckIntervalSeconds && _configCache.Count > 0)
                return;

            _lastCheckTime = DateTime.Now;

            if (!File.Exists(FilePath))
            {
                if (_configCache.Count == 0)
                    throw new FileNotFoundException($"配置文件【{ConfigName}】未找到。\n目标路径: {FilePath}\n当前上下文: {AppDomain.CurrentDomain.BaseDirectory}");
                return;
            }

            DateTime currentWriteTime = File.GetLastWriteTime(FilePath);
            if (_lastWriteTime >= currentWriteTime && _configCache.Count > 0) return;

            lock (_lock)
            {
                if (_lastWriteTime >= currentWriteTime && _configCache.Count > 0) return;
                LoadConfigFromXml();
                _lastWriteTime = currentWriteTime;
            }
        }

        private static void LoadConfigFromXml()
        {
            try
            {
                // 使用 LINQ to XML 加载
                XElement root = XElement.Load(FilePath);

                // 解析 <add key="..." value="..." /> 格式
                var newCache = root.Descendants("add")
                                   .Where(x => x.Attribute("key") != null)
                                   .ToDictionary(
                                       x => x.Attribute("key").Value,
                                       x => x.Attribute("value")?.Value ?? "",
                                       StringComparer.OrdinalIgnoreCase
                                   );

                _configCache = newCache;
            }
            catch (Exception ex)
            {
                throw new Exception($"加载配置文件【{ConfigName}】失败。\n路径: {FilePath}\n错误: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 获取配置值
        /// </summary>
        public static string Get(string key, string orgCode = null)
        {
            EnsureConfigLoaded();
            string value;
            // 优先匹配 Key_OrgCode
            if (!string.IsNullOrEmpty(orgCode) && _configCache.TryGetValue($"{key}_{orgCode}", out value)) return value;
            // 次优先匹配原始 Key
            if (_configCache.TryGetValue(key, out value)) return value;
            return null;
        }

        /// <summary>
        /// 获取整数配置值
        /// </summary>
        public static int GetInt(string key, int defaultValue)
        {
            string val = Get(key);
            return int.TryParse(val, out int result) ? result : defaultValue;
        }

        /// <summary>
        /// 获取长整数配置值
        /// </summary>
        public static long GetInt64(string key, long defaultValue)
        {
            string val = Get(key);
            return long.TryParse(val, out long result) ? result : defaultValue;
        }
    }
}

示例配置文件SH_OpenApiConfig.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
  
    <!-- 通用配置 -->
    <add key="Idempotency_Timeout" value="30" desc="幂等性僵尸进程超时时间 (单位: 分钟)" />
    <add key="HttpReplay_Timeout" value="10" desc="日志列表重发请求,Http超时时间(分钟)" />
    <add key="CenterOrg" value="1002412044341256" desc="数据中心组织ID,实体扩展段查询使用,如供应商可能在数据中心而不是当前组织" />
    
    <!-- 业务配置 -->
    <add key="AutoDistributeOrgs" value="10" desc="自动下发组织(多个使用英文,号隔开)" />
    <add key="PurPriceList_Create_DefCurrency" value="C001" desc="厂商价目表,默认币种" />
    <add key="PurPriceAdjustment_DefDocumentType" value="PMTJ0001" desc="厂商价目表,默认调整单单据类型" />
    <add key="SOLineThirdField" value="DescFlexField.PrivateDescSeg2" desc="NC销售订单行ID(销售订单行私有段2)" />
    
  </appSettings>
</configuration>

PushConfig

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UFIDA.U9.Base.Profile;
using UFSoft.UBF.PL;

namespace UFIDA.U9.SH.HER.QXPubBP.Common
{
    /// <summary>
    /// 推送配置管理类 (JSON版)
    /// 特性:支持 // 注释、线程安全、热更新、字典缓存
    /// </summary>
    public class PushConfig
    {
        /// <summary>
        /// 根据组织判断是否需要推送到NC
        /// </summary>
        /// <param name="orgCode">组织编码</param>
        /// <returns></returns>
        public static bool ShouldPushToNCByOrg(string orgCode)
        {
            ProfileValue pv = ProfileValue.Finder.Find("Profile=202601200001 and Organization.Code=@OrgCode", new OqlParam(orgCode));
            if (pv == null) return false;
            bool.TryParse(pv.Value, out bool isPush);
            return isPush;
        }

        #region 内部成员

        private static Dictionary<string, string> _configCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private static DateTime _lastWriteTime;
        private static DateTime _lastCheckTime;
        private static readonly object _lock = new object();
        private const int CheckIntervalSeconds = 3;
        private const string ConfigName = "SH_PushConfig.config";
        private static readonly string FilePath;

        #endregion

        static PushConfig()
        {
            string basePath = AppDomain.CurrentDomain.BaseDirectory;

            // 核心逻辑:智能定位 Portal 目录(保证U9主服务、调度服务、审批流服务都统一读取Portal目录下的配置文件)
            // 兼容:Portal, Portal/AppServer, MailService, MailService/Libs
            string portalPath = FindPortalDirectory(basePath);

            FilePath = Path.Combine(portalPath, ConfigName);
        }

        /// <summary>
        /// 智能查找 Portal 目录(纯字符串操作优化版)
        /// </summary>
        private static string FindPortalDirectory(string startPath)
        {
            string currentPath = startPath;

            // 循环直到根目录(GetDirectoryName 返回 null 表示到达根目录)
            while (!string.IsNullOrEmpty(currentPath))
            {
                // 1. 获取当前文件夹名称
                // 注意:Path.GetFileName 对文件夹路径使用时,返回的是最后一级文件夹的名字
                string dirName = Path.GetFileName(currentPath.TrimEnd(Path.DirectorySeparatorChar));

                // 2. 【自身匹配】如果当前就是 Portal
                if (string.Equals(dirName, "Portal", StringComparison.OrdinalIgnoreCase))
                {
                    return currentPath;
                }

                // 3. 【子目录匹配】检查当前目录下是否有 Portal 子文件夹
                // 场景:在 C:\yonyou\U9CE 下发现了 Portal 子目录
                string potentialPortal = Path.Combine(currentPath, "Portal");
                if (Directory.Exists(potentialPortal))
                {
                    return potentialPortal;
                }

                // 4. 向上移动一级
                currentPath = Path.GetDirectoryName(currentPath);
            }

            // 如果没找到,返回原始路径
            return startPath;
        }

        private static void EnsureConfigLoaded()
        {
            if ((DateTime.Now - _lastCheckTime).TotalSeconds < CheckIntervalSeconds && _configCache.Count > 0)
                return;

            _lastCheckTime = DateTime.Now;

            if (!File.Exists(FilePath))
            {
                if (_configCache.Count == 0)
                    throw new FileNotFoundException($"配置文件【{ConfigName}】未找到。\n目标路径: {FilePath}\n当前上下文: {AppDomain.CurrentDomain.BaseDirectory}");
                return;
            }

            DateTime currentWriteTime = File.GetLastWriteTime(FilePath);
            if (_lastWriteTime >= currentWriteTime && _configCache.Count > 0) return;

            lock (_lock)
            {
                if (_lastWriteTime >= currentWriteTime && _configCache.Count > 0) return;
                LoadConfigFromXml();
                _lastWriteTime = currentWriteTime;
            }
        }

        private static void LoadConfigFromXml()
        {
            try
            {
                // 使用 LINQ to XML 加载
                XElement root = XElement.Load(FilePath);

                // 解析 <add key="..." value="..." /> 格式
                var newCache = root.Descendants("add")
                                   .Where(x => x.Attribute("key") != null)
                                   .ToDictionary(
                                       x => x.Attribute("key").Value,
                                       x => x.Attribute("value")?.Value ?? "",
                                       StringComparer.OrdinalIgnoreCase
                                   );

                _configCache = newCache;
            }
            catch (Exception ex)
            {
                throw new Exception($"加载配置文件【{ConfigName}】失败。\n路径: {FilePath}\n错误: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 获取配置值
        /// </summary>
        public static string Get(string key, string orgCode = null)
        {
            EnsureConfigLoaded();
            string value;
            // 优先匹配 Key_OrgCode
            if (!string.IsNullOrEmpty(orgCode) && _configCache.TryGetValue($"{key}_{orgCode}", out value)) return value;
            // 次优先匹配原始 Key
            if (_configCache.TryGetValue(key, out value)) return value;
            return null;
        }

        /// <summary>
        /// 获取整数配置值
        /// </summary>
        public static int GetInt(string key, int defaultValue)
        {
            string val = Get(key);
            return int.TryParse(val, out int result) ? result : defaultValue;
        }

        /// <summary>
        /// 获取长整数配置值
        /// </summary>
        public static long GetInt64(string key, long defaultValue)
        {
            string val = Get(key);
            return long.TryParse(val, out long result) ? result : defaultValue;
        }
    }
}

示例配置文件SH_PushConfig.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
  
    <add key="HttpPush_Timeout" value="10" desc="HTTP请求超时时间(分钟)" />
    <add key="NCSync_BaseUrl" value="http://172.16.4.12:8054" desc="NC接口地址" />
    <add key="NCSync_ItemMaster" value="/uapws/rest/service/u9/synMaterial" desc="NC料品同步接口路由" />
    <add key="NCSync_ItemMaster_PushOrg" value="10" desc="NC料品同步的组织信息" />
    <add key="NCSync_ItemMaster_DefMattaxes" value="CN001" desc="NC料品同步的默认税组合编码" />
    
  </appSettings>
</configuration>
0

评论 (0)

取消