commits列表 30 项
提交内容 8/8
redis appcache daterange等的更新
framework/Directory.Build.props
diff --git a/framework/Directory.Build.props b/framework/Directory.Build.props
index 98abd60..5fb4b6c 100644
--- a/framework/Directory.Build.props
+++ b/framework/Directory.Build.props
@@ -1,7 +1,7 @@
 <Project>
   <PropertyGroup>
-    <Version>26.9.17</Version>
-    <FileVersion>26.9.17</FileVersion>
-    <AssemblyVersion>26.9.17</AssemblyVersion>
+    <Version>26.9.19</Version>
+    <FileVersion>26.9.19</FileVersion>
+    <AssemblyVersion>26.9.19</AssemblyVersion>
   </PropertyGroup>
 </Project>
\ No newline at end of file
framework/PasteApeart.Application.Contracts/eventmodels/IEventModel.cs
diff --git a/framework/PasteApeart.Application.Contracts/eventmodels/IEventModel.cs b/framework/PasteApeart.Application.Contracts/eventmodels/IEventModel.cs
index 1b86f53..8283996 100644
--- a/framework/PasteApeart.Application.Contracts/eventmodels/IEventModel.cs
+++ b/framework/PasteApeart.Application.Contracts/eventmodels/IEventModel.cs
@@ -11,14 +11,19 @@ namespace PasteApeart.Application.Contracts
         long ExpireSecond { get; set; }
 
         /// <summary>
-        /// 截止 时间戳秒
+        /// 截止 时间戳秒,用于后续的,比如超过多久的消息丢弃,感觉和MutexKey MutexVal互斥了
         /// </summary>
         long ExpireTime { get; set; }
 
         /// <summary>
-        /// 对象名称
+        /// 对象名称 也就是Model的名称,大小写一致
         /// </summary>
         string EventName { get; set; }
+
+        /// <summary>
+        /// 广播模式 是否是广播模式的消息,默认false,false为队列消息
+        /// </summary>
+        bool IsSubscribe { get; set; }
     }
 
 
framework/PasteApeart.Handler/redis/IAppCache.cs
diff --git a/framework/PasteApeart.Handler/redis/IAppCache.cs b/framework/PasteApeart.Handler/redis/IAppCache.cs
index cdf6632..ab93a2e 100644
--- a/framework/PasteApeart.Handler/redis/IAppCache.cs
+++ b/framework/PasteApeart.Handler/redis/IAppCache.cs
@@ -305,6 +305,346 @@ namespace PasteApeart.Handler.redis
         /// <returns></returns>
         Task<bool> SortedSetAddAsync(string key, string field, long value);
         #endregion
+
+
+        //--------------------------------------------------------------------------------
+
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        //bool IsRedis { get; }
+
+        /// <summary>
+        /// 添加一个字符串对象。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <param name="value">值。</param>
+        /// <param name="expiry">过期时间(时间间隔)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        bool Set(string key, string value, TimeSpan? expiry = null);
+
+        /// <summary>
+        /// 添加一个字符串对象。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <param name="value">值。</param>
+        /// <param name="seconds">过期时间(秒)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        bool Set(string key, string value, int seconds);
+
+        /// <summary>
+        /// 添加一个对象。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <typeparam name="T">对象的类型。</typeparam>
+        /// <param name="value">值。</param>
+        /// <param name="expiry">过期时间(时间间隔)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        bool SetObject<T>(string key, T value, TimeSpan? expiry = null);
+
+        /// <summary>
+        /// 添加一个对象。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <typeparam name="T">对象的类型。</typeparam>
+        /// <param name="value">值。</param>
+        /// <param name="seconds">过期时间(秒)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        bool SetObject<T>(string key, T value, int seconds);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="val"></param>
+        /// <param name="second"></param>
+        /// <returns></returns>
+        bool SetValue(string key, string val, int second);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="val"></param>
+        /// <returns></returns>
+        bool SetValue(string key, string val);
+
+        /// <summary>
+        /// 获取一个对象。
+        /// </summary>
+        /// <param name="key">值。</param>
+        /// <returns>返回对象的值。</returns>
+        T GetObject<T>(string key);
+
+        /// <summary>
+        /// 获取一个字符串对象。
+        /// </summary>
+        /// <param name="key">值。</param>
+        /// <returns>返回对象的值。</returns>
+        [Obsolete]
+        string Get(string key);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        string GetStringValue(string key);
+
+        /// <summary>
+        /// 删除一个对象。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <returns>返回是否执行成功。</returns>
+        [Obsolete]
+        bool Delete(string key);
+
+        /// <summary>
+        /// 返回键是否存在。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <returns>返回键是否存在。</returns>
+        [Obsolete]
+        bool Exists(string key);
+
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        ///// <param name="key"></param>
+        ///// <returns></returns>
+        //Task<bool> ExistsAsync(string key);
+
+        /// <summary>
+        /// 设置一个键的过期时间。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <param name="expiry">过期时间(时间间隔)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        [Obsolete]
+        bool SetExpire(string key, TimeSpan? expiry);
+
+        /// <summary>
+        /// 设置一个键的过期时间。
+        /// </summary>
+        /// <param name="key">键。</param>
+        /// <param name="seconds">过期时间(秒)。</param>
+        /// <returns>返回是否执行成功。</returns>
+        [Obsolete]
+        bool SetExpire(string key, int seconds);
+
+        ///// <summary>
+        ///// 异步添加一个字符串对象。
+        ///// </summary>
+        ///// <param name="key">键。</param>
+        ///// <param name="value">值。</param>
+        ///// <param name="expiry">过期时间(时间间隔)。</param>
+        ///// <returns>返回是否执行成功。</returns>
+        //Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null);
+
+        ///// <summary>
+        ///// 异步添加一个字符串对象。
+        ///// </summary>
+        ///// <param name="key">键。</param>
+        ///// <param name="value">值。</param>
+        ///// <param name="seconds">过期时间(秒)。</param>
+        ///// <returns>返回是否执行成功。</returns>
+        //Task<bool> SetAsync(string key, string value, int seconds);
+
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        ///// <typeparam name="T"></typeparam>
+        ///// <param name="key"></param>
+        ///// <param name="value"></param>
+        ///// <param name="seconds"></param>
+        ///// <returns></returns>
+        //Task<bool> SetObjectAsync<T>(string key, T value, int seconds = 300);
+
+        ///// <summary>
+        ///// 异步获取一个对象。
+        ///// </summary>
+        ///// <typeparam name="T">对象的类型。</typeparam>
+        ///// <param name="key">值。</param>
+        ///// <returns>返回对象的值。</returns>
+        //Task<T> GetObjectAsync<T>(string key);
+
+        ///// <summary>
+        ///// 异步获取一个字符串对象。
+        ///// </summary>
+        ///// <param name="key">值。</param>
+        ///// <returns>返回对象的值。</returns>
+        //Task<string> GetAsync(string key);
+
+        ///// <summary>
+        ///// 异步删除一个对象。
+        ///// </summary>
+        ///// <param name="key">键。</param>
+        ///// <returns>返回是否执行成功。</returns>
+        //Task<bool> DeleteAsync(string key);
+
+        /// <summary>
+        /// 批量删除
+        /// </summary>
+        /// <param name="keys"></param>
+        /// <returns></returns>
+        Task BatchRemoveAsync(string[] keys);
+
+        ///// <summary>
+        ///// 异步设置一个键的过期时间。
+        ///// </summary>
+        ///// <param name="key">键。</param>
+        ///// <param name="seconds">过期时间(秒)。</param>
+        ///// <returns>返回是否执行成功。</returns>
+        //Task<bool> SetExpireAsync(string key, int seconds);
+
+        ///// <summary>
+        ///// 异步设置一个键的过期时间。
+        ///// </summary>
+        ///// <param name="key">键。</param>
+        ///// <param name="expiry">过期时间(时间间隔)。</param>
+        ///// <returns>返回是否执行成功。</returns>
+        //Task<bool> SetExpireAsync(string key, TimeSpan? expiry);
+
+        #region 分布式锁...
+
+        /// <summary>
+        /// 获取锁。
+        /// </summary>
+        /// <param name="key">锁名称。</param>
+        /// <param name="seconds">过期时间(秒)。</param>
+        /// <returns>是否已锁。</returns>
+        bool Lock(string key, int seconds);
+
+        /// <summary>
+        /// 释放锁。
+        /// </summary>
+        /// <param name="key">锁名称。</param>
+        /// <returns>是否成功。</returns>
+        bool UnLock(string key);
+
+        ///// <summary>
+        ///// 异步获取锁。
+        ///// </summary>
+        ///// <param name="key">锁名称。</param>
+        ///// <param name="seconds">过期时间(秒)。</param>
+        ///// <returns>是否成功。</returns>
+        //Task<bool> LockAsync(string key, int seconds);
+
+        ///// <summary>
+        ///// 异步释放锁。
+        ///// </summary>
+        ///// <param name="key">锁名称。</param>
+        ///// <returns>是否成功。</returns>
+        //Task<bool> UnLockAsync(string key);
+
+        #endregion
+
+
+        #region hashtable
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        List<HashKeyValue> HashGetAll(string key);
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        ///// <param name="key"></param>
+        ///// <returns></returns>
+        //Task<List<HashKeyValue>> HashGetAllAsync(string key);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="num"></param>
+        /// <returns></returns>
+        Task<long> HashIncrementAsync(string key, RedisValue field, long num = 1);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="num"></param>
+        /// <returns></returns>
+        Task<long> HashDecrementAsync(string key, RedisValue field, long num = 1);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="vals"></param>
+        void HashSet(string key, HashEntry[] vals);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="vals"></param>
+        Task HashSetAsync(string key, HashEntry[] vals);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="vals"></param>
+        void HashSet(string key, System.Collections.Generic.List<HashKeyValue> vals);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="vals"></param>
+        Task HashSetAsync(string key, System.Collections.Generic.List<HashKeyValue> vals);
+
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        ///// <param name="key"></param>
+        ///// <param name="field"></param>
+        ///// <returns></returns>
+        //Task<bool> HashDeleteAsync(string key, string field);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="pattern"></param>
+        /// <param name="size"></param>
+        /// <param name="cursor"></param>
+        /// <param name="offset"></param>
+        /// <returns></returns>
+        Task<HashEntry[]> HashScanAsync(string key, string pattern, int size = 1, int cursor = 0, int offset = 0);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="sunkey"></param>
+        /// <returns></returns>
+        string HashGet(string key, string sunkey);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="sunkey"></param>
+        /// <returns></returns>
+        Task<string> HashGetAsync(string key, string sunkey);
+
+        /// <summary>
+        /// 随机从Hash中获取一个
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        Task<HashEntry> HashRandomFieldsWithValuesAsync(string key);
+        #endregion
+
+
+
     }
 
 }
framework/PasteApeart.Handler/redis/MemoryClient.cs
diff --git a/framework/PasteApeart.Handler/redis/MemoryClient.cs b/framework/PasteApeart.Handler/redis/MemoryClient.cs
index de2b30a..48a088e 100644
--- a/framework/PasteApeart.Handler/redis/MemoryClient.cs
+++ b/framework/PasteApeart.Handler/redis/MemoryClient.cs
@@ -1084,6 +1084,372 @@ namespace PasteApeart.Handler.redis
 
             return sb.ToString();
         }
+
+
+        //------------------------------------------------
+
+
+        //private readonly IMemoryCache _memoryCache;
+        private static readonly Random _random = new();
+
+        //public MemoryClient(IMemoryCache memoryCache)
+        //{
+        //    _memoryCache = memoryCache;
+        //}
+
+        //public bool IsRedis => false;
+
+        #region 同步方法
+
+        //public bool Delete(string key)
+        //{
+        //    _memoryCache.Remove(key);
+        //    return true;
+        //}
+
+        //public bool Exists(string key)
+        //{
+        //    return _memoryCache.TryGetValue(key, out _);
+        //}
+
+        //public string Get(string key)
+        //{
+        //    return _memoryCache.TryGetValue(key, out string? val) ? val ?? string.Empty : string.Empty;
+        //}
+
+        //public T GetObject<T>(string key)
+        //{
+        //    if (_memoryCache.TryGetValue(key, out string? val) && !string.IsNullOrEmpty(val))
+        //    {
+        //        return JsonConvert.DeserializeObject<T>(val);
+        //    }
+        //    return default;
+        //}
+
+        //public string GetStringValue(string key)
+        //{
+        //    return Get(key);
+        //}
+
+        public object TryGetValue(string key)
+        {
+            return _memoryCache.TryGetValue(key, out var val) ? val : null;
+        }
+
+        public T GetOrCreate<T>(string key, Func<ICacheEntry, T> factory, TimeSpan? expiration = null)
+        {
+            return _memoryCache.GetOrCreate(key, entry =>
+            {
+                SetCacheEntryOptions(entry, expiration);
+                return factory(entry);
+            });
+        }
+
+        //public bool Set(string key, string value, TimeSpan? expiry = null)
+        //{
+        //    using var entry = _memoryCache.CreateEntry(key);
+        //    entry.Value = value;
+        //    SetCacheEntryOptions(entry, expiry);
+        //    return true;
+        //}
+
+        //public bool Set(string key, string value, int seconds)
+        //{
+        //    return Set(key, value, TimeSpan.FromSeconds(seconds));
+        //}
+
+        //public bool Set(string key, string value)
+        //{
+        //    using var entry = _memoryCache.CreateEntry(key);
+        //    entry.Value = value;
+        //    return true;
+        //}
+
+        //public bool SetObject<T>(string key, T value, TimeSpan? expiry = null)
+        //{
+        //    return Set(key, JsonConvert.SerializeObject(value), expiry);
+        //}
+
+        //public bool SetObject<T>(string key, T value, int seconds)
+        //{
+        //    return Set(key, JsonConvert.SerializeObject(value), TimeSpan.FromSeconds(seconds));
+        //}
+
+        public bool CreateEntry(string key, object value, TimeSpan? expiry = null)
+        {
+            using var entry = _memoryCache.CreateEntry(key);
+            entry.Value = value;
+            SetCacheEntryOptions(entry, expiry);
+            return true;
+        }
+
+        //public bool SetValue(string key, string val, int second)
+        //{
+        //    return Set(key, val, second);
+        //}
+
+        //public bool SetValue(string key, string val)
+        //{
+        //    return Set(key, val);
+        //}
+
+        //public bool SetExpire(string key, TimeSpan? expiry)
+        //{
+        //    if (_memoryCache.TryGetValue(key, out string? val))
+        //    {
+        //        return Set(key, val ?? string.Empty, expiry);
+        //    }
+        //    return false;
+        //}
+
+        //public bool SetExpire(string key, int seconds)
+        //{
+        //    return SetExpire(key, TimeSpan.FromSeconds(seconds));
+        //}
+
+        //public bool Lock(string key, int seconds)
+        //{
+        //    // MemoryCache 不支持分布式锁,直接返回 true
+        //    return true;
+        //}
+
+        //public bool UnLock(string key)
+        //{
+        //    return true;
+        //}
+
+        #endregion
+
+        #region 异步方法(同步包装,返回 Task)
+
+        //public Task<bool> DeleteAsync(string key)
+        //{
+        //    Delete(key);
+        //    return Task.FromResult(true);
+        //}
+
+        public Task BatchRemoveAsync(string[] keys)
+        {
+            if (keys != null)
+            {
+                foreach (var key in keys)
+                {
+                    if (!string.IsNullOrEmpty(key))
+                    {
+                        _memoryCache.Remove(key);
+                    }
+                }
+            }
+            return Task.CompletedTask;
+        }
+
+        //public Task<bool> ExistsAsync(string key)
+        //{
+        //    return Task.FromResult(Exists(key));
+        //}
+
+        //public Task<string> GetAsync(string key)
+        //{
+        //    return Task.FromResult(Get(key));
+        //}
+
+        //public Task<T?> GetObjectAsync<T>(string key)
+        //{
+        //    return Task.FromResult(GetObject<T>(key));
+        //}
+
+        public Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> factory, TimeSpan? expiration = null)
+        {
+            return _memoryCache.GetOrCreateAsync(key, async entry =>
+            {
+                SetCacheEntryOptions(entry, expiration);
+                return await factory(entry);
+            });
+        }
+
+        //public Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null)
+        //{
+        //    return Task.FromResult(Set(key, value, expiry));
+        //}
+
+        //public Task<bool> SetAsync(string key, string value, int seconds)
+        //{
+        //    return Task.FromResult(Set(key, value, seconds));
+        //}
+
+        //public Task<bool> SetObjectAsync<T>(string key, T value, int second)
+        //{
+        //    return Task.FromResult(SetObject(key, value, second));
+        //}
+
+        //public Task<bool> SetExpireAsync(string key, int seconds)
+        //{
+        //    return Task.FromResult(SetExpire(key, seconds));
+        //}
+
+        //public Task<bool> SetExpireAsync(string key, TimeSpan? expiry)
+        //{
+        //    return Task.FromResult(SetExpire(key, expiry));
+        //}
+
+        //public Task<bool> LockAsync(string key, int seconds)
+        //{
+        //    return Task.FromResult(Lock(key, seconds));
+        //}
+
+        //public Task<bool> UnLockAsync(string key)
+        //{
+        //    return Task.FromResult(UnLock(key));
+        //}
+
+        #endregion
+
+        #region Hash 操作
+
+        //public string HashGet(string key, string subKey)
+        //{
+        //    if (_memoryCache.TryGetValue(key, out var obj) && obj is Dictionary<string, string> dict)
+        //    {
+        //        return dict.TryGetValue(subKey, out var val) ? val : string.Empty;
+        //    }
+        //    return string.Empty;
+        //}
+
+        //public HashEntry[] HashGetAll(string key)
+        //{
+        //    if (_memoryCache.TryGetValue(key, out var obj) && obj is Dictionary<string, string> dict)
+        //    {
+        //        return dict.Select(x => new HashEntry(x.Key, x.Value)).ToArray();
+        //    }
+        //    return Array.Empty<HashEntry>();
+        //}
+
+        //public void HashSet(string key, HashEntry[] vals)
+        //{
+        //    var dict = _memoryCache.TryGetValue(key, out var obj) && obj is Dictionary<string, string> existing
+        //        ? existing
+        //        : new Dictionary<string, string>();
+
+        //    foreach (var entry in vals)
+        //    {
+        //        dict[entry.Name.ToString()] = entry.Value.ToString();
+        //    }
+
+        //    _memoryCache.Set(key, dict);
+        //}
+
+        public Task<string> HashGetAsync(string key, string subKey)
+        {
+            return Task.FromResult(HashGet(key, subKey));
+        }
+
+        //public Task<HashEntry[]> HashGetAllAsync(string key)
+        //{
+        //    return Task.FromResult(HashGetAll(key));
+        //}
+
+        public Task HashSetAsync(string key, HashEntry[] vals)
+        {
+            HashSet(key, vals);
+            return Task.CompletedTask;
+        }
+
+        ///// <summary>
+        ///// 
+        ///// </summary>
+        ///// <param name="key"></param>
+        ///// <param name="field"></param>
+        ///// <returns></returns>
+        //public Task<bool> HashDeleteAsync(string key, string field)
+        //{
+        //    if (_memoryCache.TryGetValue(key, out var obj) && obj is Dictionary<string, string> dict)
+        //    {
+        //        if (dict.ContainsKey(field))
+        //        {
+        //            dict.Remove(field);
+        //        }
+        //    }
+        //    return Task.FromResult(true);
+        //}
+
+        public Task<HashEntry> HashRandomFieldsWithValuesAsync(string key)
+        {
+            if (_memoryCache.TryGetValue(key, out var obj) && obj is Dictionary<string, string> dict && dict.Count > 0)
+            {
+                var keys = dict.Keys.ToArray();
+                var randomKey = keys[_random.Next(keys.Length)];
+                return Task.FromResult(new HashEntry(randomKey, dict[randomKey]));
+            }
+            return Task.FromResult(new HashEntry(string.Empty, string.Empty));
+        }
+
+        public Task<long> HashIncrementAsync(string key, RedisValue field, long num = 1)
+        {
+            // MemoryCache 不支持原子增减,简单实现
+            var current = HashGet(key, field.ToString());
+            if (long.TryParse(current, out var value))
+            {
+                var newValue = value + num;
+                HashSet(key, new[] { new HashEntry(field, newValue.ToString()) });
+                return Task.FromResult(newValue);
+            }
+            HashSet(key, new[] { new HashEntry(field, num.ToString()) });
+            return Task.FromResult(num);
+        }
+
+        public Task<long> HashDecrementAsync(string key, RedisValue field, long num = 1)
+        {
+            return HashIncrementAsync(key, field, -num);
+        }
+
+        #endregion
+
+        #region 未实现的方法(抛出明确异常)
+
+        //public void HashSet(string key, List<HashKeyValue> vals)
+        //{
+        //    throw new NotImplementedException("MemoryCache 不支持此操作");
+        //}
+
+        public Task HashSetAsync(string key, List<HashKeyValue> vals)
+        {
+            throw new NotImplementedException("MemoryCache 不支持此操作");
+        }
+
+        List<HashKeyValue> IAppCache.HashGetAll(string key)
+        {
+            throw new NotImplementedException("MemoryCache 不支持此操作");
+        }
+
+        Task<List<HashKeyValue>> IAppCache.HashGetAllAsync(string key)
+        {
+            throw new NotImplementedException("MemoryCache 不支持此操作");
+        }
+
+        public Task<HashEntry[]> HashScanAsync(string key, string pattern, int size = 1, int cursor = 0, int offset = 0)
+        {
+            throw new NotImplementedException("MemoryCache 不支持 HashScan");
+        }
+
+        #endregion
+
+        #region 私有辅助方法
+
+        private static void SetCacheEntryOptions(ICacheEntry entry, TimeSpan? expiration)
+        {
+            if (expiration.HasValue)
+            {
+                entry.AbsoluteExpirationRelativeToNow = expiration;
+            }
+            else
+            {
+                entry.SlidingExpiration = TimeSpan.FromMinutes(5);
+            }
+        }
+
+        #endregion
+
+
     }
 
 }
framework/PasteApeart.Handler/redis/RedisClient.cs
diff --git a/framework/PasteApeart.Handler/redis/RedisClient.cs b/framework/PasteApeart.Handler/redis/RedisClient.cs
index 42e019b..65faf29 100644
--- a/framework/PasteApeart.Handler/redis/RedisClient.cs
+++ b/framework/PasteApeart.Handler/redis/RedisClient.cs
@@ -948,6 +948,267 @@ namespace PasteApeart.Handler.redis
         {
             return "is redis!";
         }
+
+
+
+        //--------------------------------------------------------------------------------
+
+
+
+        #region 同步方法...
+
+
+
+        #endregion
+
+        #region 异步方法...
+
+
+        /// <summary>
+        /// 批量删除
+        /// </summary>
+        /// <param name="keys"></param>
+        /// <returns></returns>
+        public async Task BatchRemoveAsync(string[] keys)
+        {
+            var batchCommand = _database.CreateBatch();
+            var redisKeys = keys.Select(x => new RedisKey(setKey(x))).ToArray();
+            await batchCommand.KeyDeleteAsync(redisKeys);
+        }
+
+
+
+        #endregion
+
+        #region 分布式锁...
+
+
+        #endregion
+
+        #region Hash
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public async Task<HashEntry[]> HashReadAllAsync(string key)
+        {
+            return await _database.HashGetAllAsync(setKey(key));
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="vals"></param>
+        public async Task HashSetAsync(string key, HashEntry[] vals)
+        {
+            await _database.HashSetAsync(setKey(key), vals);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="num"></param>
+        /// <returns></returns>
+        public async Task<long> HashIncrementAsync(string key, RedisValue field, long num = 1)
+        {
+            return await _database.HashIncrementAsync(setKey(key), field, num);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="num"></param>
+        /// <returns></returns>
+        public async Task<long> HashDecrementAsync(string key, RedisValue field, long num = 1)
+        {
+            return await _database.HashDecrementAsync(setKey(key), field, num);
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="sunkey"></param>
+        /// <returns></returns>
+        public async Task<string> HashGetAsync(string key, string sunkey)
+        {
+            return await _database.HashGetAsync(setKey(key), sunkey);
+        }
+
+        #endregion
+
+
+
+        public async Task HashSetAsync(string key, List<HashKeyValue> vals)
+        {
+            var list = new List<HashEntry>();
+            foreach (var item in vals)
+            {
+                list.Add(new HashEntry(item.Key, item.Value) { });
+            }
+
+            await _database.HashSetAsync(setKey(key), list.ToArray());
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="num"></param>
+        /// <returns></returns>
+        public async Task HashIncrementAsync(string key, string field, int num)
+        {
+            await _database.HashIncrementAsync(setKey(key), field, num);
+        }
+
+
+        /// <summary>
+        /// 写入有序排序
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="field"></param>
+        /// <param name="score"></param>
+        public async Task SortedSetAddAsync(string key, RedisValue field, int score)
+        {
+            await _database.SortedSetAddAsync(setKey(key), field, score);
+        }
+
+        /// <summary>
+        /// 批量写入有序排序
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="fields"></param>
+        public async Task SortedSetAddAsync(string key, SortedSetEntry[] fields)
+        {
+            await _database.SortedSetAddAsync(setKey(key), fields);
+        }
+
+
+        /// <summary>
+        /// 从hash中获取随机一个!!!
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="count"></param>
+        /// <returns></returns>
+        public async Task<HashEntry> HashRandomFieldsWithValuesAsync(string key)
+        {
+            var reads = await _database.HashRandomFieldsWithValuesAsync(setKey(key), 1);
+            if (reads?.Length > 0)
+            {
+                return reads.First();
+            }
+            return default;
+        }
+
+        /// <summary>
+        /// 查询符合条件的
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="pattern">*XXX*</param>
+        /// <param name="size"></param>
+        /// <returns></returns>
+        public async Task<HashEntry[]> HashScanAsync(string key, string pattern, int size = 1, int cursor = 0, int offset = 0)
+        {
+            var results = new List<HashEntry>();
+            var reads = _database.HashScanAsync(setKey(key), pattern, size, cursor, offset);
+            await foreach (var item in reads)
+            {
+                results.Add(item);
+                if (results.Count >= size)
+                {
+                    break;
+                }
+            }
+            return results.ToArray();
+        }
+
+
+        /// <summary>
+        /// 按照分数升序,获取第一个分数 >= 指定分数的数据并删除
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="minScore">最低分数</param>
+        /// <returns></returns>
+        public async Task<SortedSetEntry?> SortedSetStartPopScoreAsync(string key, int minScore)
+        {
+            var luaScript = @"
+                    local members = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], '+inf', 'WITHSCORES', 'LIMIT', 0, 1)
+                    if #members == 0 then
+                        return nil
+                    end
+                    redis.call('ZREM', KEYS[1], members[1])
+                    return {members[1], members[2]}
+                ";
+            var result = await _database.ScriptEvaluateAsync(luaScript,
+                new RedisKey[] { setKey(key) },
+                new RedisValue[] { minScore });
+            if (result.IsNull)
+            {
+                return null;
+            }
+            var resultArray = (RedisResult[])result;
+            if (resultArray.Length >= 2)
+            {
+                string element = (string)resultArray[0];
+                double redisScore = (double)resultArray[1];
+                // 将double转换为int(根据您的需求)
+                int scoreValue = (int)redisScore;
+                return new SortedSetEntry(element, scoreValue);
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 返回分数符合的第一个元素,返回并删除这个元素
+        /// </summary>
+        public async Task<SortedSetEntry?> SortedSetPopExactScoreAsync(string key, int targetScore)
+        {
+            var luaScript = @"
+                    local members = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[1], 'WITHSCORES', 'LIMIT', 0, 1)
+                    if #members == 0 then
+                        return nil
+                    end
+                    redis.call('ZREM', KEYS[1], members[1])
+                    return {members[1], members[2]}
+                ";
+
+            var result = await _database.ScriptEvaluateAsync(luaScript,
+                new RedisKey[] { setKey(key) },
+                new RedisValue[] { targetScore });
+
+            if (result.IsNull)
+            {
+                return null;
+            }
+
+            var resultArray = (RedisResult[])result;
+            if (resultArray.Length >= 2)
+            {
+                string element = (string)resultArray[0];
+                double redisScore = (double)resultArray[1];
+                int scoreValue = (int)redisScore;
+
+                return new SortedSetEntry(element, scoreValue);
+            }
+
+            return null;
+        }
+
+
+
+
+
+
     }
 
 
manage/page/pasteform/lib/My97DatePicker/4.8/WdatePicker.js
diff --git a/manage/page/pasteform/lib/My97DatePicker/4.8/WdatePicker.js b/manage/page/pasteform/lib/My97DatePicker/4.8/WdatePicker.js
index 851604a..53c5d4e 100644
--- a/manage/page/pasteform/lib/My97DatePicker/4.8/WdatePicker.js
+++ b/manage/page/pasteform/lib/My97DatePicker/4.8/WdatePicker.js
@@ -54,4 +54,4 @@ opposite:false,
 hmsMenuCfg:{H:[1,6],m:[5,6],s:[15,4]},
 opposite:false,
 
-specialDates:null,specialDays:null,disabledDates:null,disabledDays:null,onpicking:null,onpicked:null,onclearing:null,oncleared:null,ychanging:null,ychanged:null,Mchanging:null,Mchanged:null,dchanging:null,dchanged:null,Hchanging:null,Hchanged:null,mchanging:null,mchanged:null,schanging:null,schanged:null,eCont:null,vel:null,elProp:"",errMsg:"",quickSel:[],has:{},getRealLang:function(){var _=$.$langList;for(var A=0;A<_.length;A++)if(_[A].name==this.lang)return _[A];return _[0]}};WdatePicker=U;var Y=window,T={innerHTML:""},N="document",H="documentElement",C="getElementsByTagName",V,A,S,G,c,X=navigator.appName;if(X=="Microsoft Internet Explorer")S=true;else if(X=="Opera")c=true;else G=true;A=$.$dpPath||J();if($.$wdate)K(A+"skin/WdatePicker.css");V=Y;if($.$crossFrame){try{while(V.parent!=V&&V.parent[N][C]("frameset").length==0)V=V.parent}catch(O){}}if(!V.$dp)V.$dp={ff:G,ie:S,opera:c,status:0,defMinDate:$.minDate,defMaxDate:$.maxDate};B();if($.$preLoad&&$dp.status==0)E(Y,"onload",function(){U(null,true)});if(!Y[N].docMD){E(Y[N],"onmousedown",D,true);Y[N].docMD=true}if(!V[N].docMD){E(V[N],"onmousedown",D,true);V[N].docMD=true}E(Y,"onunload",function(){if($dp.dd)P($dp.dd,"none")});function B(){try{V[N],V.$dp=V.$dp||{}}catch($){V=Y;$dp=$dp||{}}var A={win:Y,$:function($){return(typeof $=="string")?Y[N].getElementById($):$},$D:function($,_){return this.$DV(this.$($).value,_)},$DV:function(_,$){if(_!=""){this.dt=$dp.cal.splitDate(_,$dp.cal.dateFmt);if($)for(var B in $)if(this.dt[B]===undefined)this.errMsg="invalid property:"+B;else{this.dt[B]+=$[B];if(B=="M"){var C=$["M"]>0?1:0,A=new Date(this.dt["y"],this.dt["M"],0).getDate();this.dt["d"]=Math.min(A+C,this.dt["d"])}}if(this.dt.refresh())return this.dt}return""},show:function(){var A=V[N].getElementsByTagName("div"),$=100000;for(var B=0;B<A.length;B++){var _=parseInt(A[B].style.zIndex);if(_>$)$=_}this.dd.style.zIndex=$+2;P(this.dd,"block");P(this.dd.firstChild,"")},unbind:function($){$=this.$($);if($.initcfg){L($,"onclick",function(){U($.initcfg)});L($,"onfocus",function(){U($.initcfg)})}},hide:function(){P(this.dd,"none")},attachEvent:E};for(var _ in A)V.$dp[_]=A[_];$dp=V.$dp}function E(B,_,A,$){if(B.addEventListener){var C=_.replace(/on/,"");A._ieEmuEventHandler=function($){return A($)};B.addEventListener(C,A._ieEmuEventHandler,$)}else B.attachEvent(_,A)}function L(A,$,_){if(A.removeEventListener){var B=$.replace(/on/,"");_._ieEmuEventHandler=function($){return _($)};A.removeEventListener(B,_._ieEmuEventHandler,false)}else A.detachEvent($,_)}function a(_,$,A){if(typeof _!=typeof $)return false;if(typeof _=="object"){if(!A)for(var B in _){if(typeof $[B]=="undefined")return false;if(!a(_[B],$[B],true))return false}return true}else if(typeof _=="function"&&typeof $=="function")return _.toString()==$.toString();else return _==$}function J(){var _,A,$=Y[N][C]("script");for(var B=0;B<$.length;B++){_=$[B].getAttribute("src")||"";_=_.substr(0,_.toLowerCase().indexOf("wdatepicker.js"));A=_.lastIndexOf("/");if(A>0)_=_.substring(0,A+1);if(_)break}return _}function K(A,$,B){var D=Y[N][C]("HEAD").item(0),_=Y[N].createElement("link");if(D){_.href=A;_.rel="stylesheet";_.type="text/css";if($)_.title=$;if(B)_.charset=B;D.appendChild(_)}}function F($){$=$||V;var A=0,_=0;while($!=V){var D=$.parent[N][C]("iframe");for(var F=0;F<D.length;F++){try{if(D[F].contentWindow==$){var E=W(D[F]);A+=E.left;_+=E.top;break}}catch(B){}}$=$.parent}return{"leftM":A,"topM":_}}function W(G,F){if(G.getBoundingClientRect)return G.getBoundingClientRect();else{var A={ROOT_TAG:/^body|html$/i,OP_SCROLL:/^(?:inline|table-row)$/i},E=false,I=null,_=G.offsetTop,H=G.offsetLeft,D=G.offsetWidth,B=G.offsetHeight,C=G.offsetParent;if(C!=G)while(C){H+=C.offsetLeft;_+=C.offsetTop;if(R(C,"position").toLowerCase()=="fixed")E=true;else if(C.tagName.toLowerCase()=="body")I=C.ownerDocument.defaultView;C=C.offsetParent}C=G.parentNode;while(C.tagName&&!A.ROOT_TAG.test(C.tagName)){if(C.scrollTop||C.scrollLeft)if(!A.OP_SCROLL.test(P(C)))if(!c||C.style.overflow!=="visible"){H-=C.scrollLeft;_-=C.scrollTop}C=C.parentNode}if(!E){var $=b(I);H-=$.left;_-=$.top}D+=H;B+=_;return{"left":H,"top":_,"right":D,"bottom":B}}}function M($){$=$||V;var B=$[N],A=($.innerWidth)?$.innerWidth:(B[H]&&B[H].clientWidth)?B[H].clientWidth:B.body.offsetWidth,_=($.innerHeight)?$.innerHeight:(B[H]&&B[H].clientHeight)?B[H].clientHeight:B.body.offsetHeight;return{"width":A,"height":_}}function b($){$=$||V;var B=$[N],A=B[H],_=B.body;B=(A&&A.scrollTop!=null&&(A.scrollTop>_.scrollTop||A.scrollLeft>_.scrollLeft))?A:_;return{"top":B.scrollTop,"left":B.scrollLeft}}function D($){try{var _=$?($.srcElement||$.target):null;if($dp.cal&&!$dp.eCont&&$dp.dd&&_!=$dp.el&&$dp.dd.style.display=="block")$dp.cal.close()}catch($){}}function Z(){$dp.status=2}var Q,_;function U(K,C){if(!$dp)return;B();var L={};for(var H in K)L[H]=K[H];for(H in $)if(H.substring(0,1)!="$"&&L[H]===undefined)L[H]=$[H];if(C){if(!J()){_=_||setInterval(function(){if(V[N].readyState=="complete")clearInterval(_);U(null,true)},50);return}if($dp.status==0){$dp.status=1;L.el=T;I(L,true)}else return}else if(L.eCont){L.eCont=$dp.$(L.eCont);L.el=T;L.autoPickDate=true;L.qsEnabled=false;I(L)}else{if($.$preLoad&&$dp.status!=2)return;var F=D();if(Y.event===F||F){L.srcEl=F.srcElement||F.target;F.cancelBubble=true}L.el=L.el=$dp.$(L.el||L.srcEl);if(!L.el||L.el["My97Mark"]===true||L.el.disabled||($dp.dd&&P($dp.dd)!="none"&&$dp.dd.style.left!="-970px")){try{if(L.el["My97Mark"])L.el["My97Mark"]=false}catch(A){}return}if(F&&L.el.nodeType==1&&!a(L.el.initcfg,K)){$dp.unbind(L.el);E(L.el,F.type=="focus"?"onclick":"onfocus",function(){U(K)});L.el.initcfg=K}I(L)}function J(){if(S&&V!=Y&&V[N].readyState!="complete")return false;return true}function D(){if(G){func=D.caller;while(func!=null){var $=func.arguments[0];if($&&($+"").indexOf("Event")>=0)return $;func=func.caller}return null}return event}}function R(_,$){return _.currentStyle?_.currentStyle[$]:document.defaultView.getComputedStyle(_,false)[$]}function P(_,$){if(_)if($!=null)_.style.display=$;else return R(_,"display")}function I(G,_){var D=G.el?G.el.nodeName:"INPUT";if(_||G.eCont||new RegExp(/input|textarea|div|span|p|a/ig).test(D))G.elProp=D=="INPUT"?"value":"innerHTML";else return;if(G.lang=="auto")G.lang=S?navigator.browserLanguage.toLowerCase():navigator.language.toLowerCase();if(!G.eCont)for(var C in G)$dp[C]=G[C];if(!$dp.dd||G.eCont||($dp.dd&&(G.getRealLang().name!=$dp.dd.lang||G.skin!=$dp.dd.skin))){if(G.eCont)E(G.eCont,G);else{$dp.dd=V[N].createElement("DIV");$dp.dd.style.cssText="position:absolute";V[N].body.appendChild($dp.dd);E($dp.dd,G);if(_)$dp.dd.style.left=$dp.dd.style.top="-970px";else{$dp.show();B($dp)}}}else if($dp.cal){$dp.show();$dp.cal.init();if(!$dp.eCont)B($dp)}function E(K,J){var I=V[N].domain,F=false,G="<iframe hideFocus=true width=9 height=7 frameborder=0 border=0 scrolling=no src=\"about:blank\"></iframe>";K.innerHTML=G;var _=$.$langList,D=$.$skinList,H;try{H=K.lastChild.contentWindow[N]}catch(E){F=true;K.removeChild(K.lastChild);var L=V[N].createElement("iframe");L.hideFocus=true;L.frameBorder=0;L.scrolling="no";L.src="javascript:(function(){var d=document;d.open();d.domain='"+I+"';})()";K.appendChild(L);setTimeout(function(){H=K.lastChild.contentWindow[N];C()},97);return}C();function C(){var _=J.getRealLang();K.lang=_.name;K.skin=J.skin;var $=["<head><script>","","var doc=document, $d, $dp, $cfg=doc.cfg, $pdp = parent.$dp, $dt, $tdt, $sdt, $lastInput, $IE=$pdp.ie, $FF = $pdp.ff,$OPERA=$pdp.opera, $ny, $cMark = false;","if($cfg.eCont){$dp = {};for(var p in $pdp)$dp[p]=$pdp[p];}else{$dp=$pdp;};for(var p in $cfg){$dp[p]=$cfg[p];}","doc.oncontextmenu=function(){try{$c._fillQS(!$dp.has.d,1);showB($d.qsDivSel);}catch(e){};return false;};","</script><script src=",A,"lang/",_.name,".js charset=",_.charset,"></script>"];if(F)$[1]="document.domain=\""+I+"\";";for(var C=0;C<D.length;C++)if(D[C].name==J.skin)$.push("<link rel=\"stylesheet\" type=\"text/css\" href=\""+A+"skin/"+D[C].name+"/datepicker.css\" charset=\""+D[C].charset+"\"/>");$.push("<script src=\""+A+"calendar.js\"></script>");$.push("</head><body leftmargin=\"0\" topmargin=\"0\" tabindex=0></body></html>");$.push("<script>var t;t=t||setInterval(function(){if(doc.ready){new My97DP();$cfg.onload();$c.autoSize();$cfg.setPos($dp);clearInterval(t);}},20);</script>");J.setPos=B;J.onload=Z;H.write("<html>");H.cfg=J;H.write($.join(""));H.close()}}function B(J){var H=J.position.left,C=J.position.top,D=J.el;if(D==T)return;if(D!=J.srcEl&&(P(D)=="none"||D.type=="hidden"))D=J.srcEl;var I=W(D),$=F(Y),E=M(V),B=b(V),G=$dp.dd.offsetHeight,A=$dp.dd.offsetWidth;if(isNaN(C))C=0;if(($.topM+I.bottom+G>E.height)&&($.topM+I.top-G>0))C+=B.top+$.topM+I.top-G-2;else{C+=B.top+$.topM+I.bottom;var _=C-B.top+G-E.height;if(_>0)C-=_}if(isNaN(H))H=0;H+=B.left+Math.min($.leftM+I.left,E.width-A-5)-(S?2:0);J.dd.style.top=C+"px";J.dd.style.left=H+"px"}}})()
\ No newline at end of file
+specialDates:null,specialDays:null,disabledDates:null,disabledDays:null,onpicking:null,onpicked:null,onclearing:null,oncleared:null,ychanging:null,ychanged:null,Mchanging:null,Mchanged:null,dchanging:null,dchanged:null,Hchanging:null,Hchanged:null,mchanging:null,mchanged:null,schanging:null,schanged:null,eCont:null,vel:null,elProp:"",errMsg:"",quickSel:[],has:{},getRealLang:function(){var _=$.$langList;for(var A=0;A<_.length;A++)if(_[A].name==this.lang)return _[A];return _[0]}};WdatePicker=U;var Y=window,T={innerHTML:""},N="document",H="documentElement",C="getElementsByTagName",V,A,S,G,c,X=navigator.appName;if(X=="Microsoft Internet Explorer")S=true;else if(X=="Opera")c=true;else G=true;A=$.$dpPath||J();if($.$wdate)K(A+"skin/WdatePicker.css");V=Y;if($.$crossFrame){try{while(V.parent!=V&&V.parent[N][C]("frameset").length==0)V=V.parent}catch(O){}}if(!V.$dp)V.$dp={ff:G,ie:S,opera:c,status:0,defMinDate:$.minDate,defMaxDate:$.maxDate};B();if($.$preLoad&&$dp.status==0)E(Y,"onload",function(){U(null,true)});if(!Y[N].docMD){E(Y[N],"onmousedown",D,true);Y[N].docMD=true}if(!V[N].docMD){E(V[N],"onmousedown",D,true);V[N].docMD=true}E(Y,"pagehide",function(){if($dp.dd)P($dp.dd,"none")});function B(){try{V[N],V.$dp=V.$dp||{}}catch($){V=Y;$dp=$dp||{}}var A={win:Y,$:function($){return(typeof $=="string")?Y[N].getElementById($):$},$D:function($,_){return this.$DV(this.$($).value,_)},$DV:function(_,$){if(_!=""){this.dt=$dp.cal.splitDate(_,$dp.cal.dateFmt);if($)for(var B in $)if(this.dt[B]===undefined)this.errMsg="invalid property:"+B;else{this.dt[B]+=$[B];if(B=="M"){var C=$["M"]>0?1:0,A=new Date(this.dt["y"],this.dt["M"],0).getDate();this.dt["d"]=Math.min(A+C,this.dt["d"])}}if(this.dt.refresh())return this.dt}return""},show:function(){var A=V[N].getElementsByTagName("div"),$=100000;for(var B=0;B<A.length;B++){var _=parseInt(A[B].style.zIndex);if(_>$)$=_}this.dd.style.zIndex=$+2;P(this.dd,"block");P(this.dd.firstChild,"")},unbind:function($){$=this.$($);if($.initcfg){L($,"onclick",function(){U($.initcfg)});L($,"onfocus",function(){U($.initcfg)})}},hide:function(){P(this.dd,"none")},attachEvent:E};for(var _ in A)V.$dp[_]=A[_];$dp=V.$dp}function E(B,_,A,$){if(B.addEventListener){var C=_.replace(/on/,"");A._ieEmuEventHandler=function($){return A($)};B.addEventListener(C,A._ieEmuEventHandler,$)}else B.attachEvent(_,A)}function L(A,$,_){if(A.removeEventListener){var B=$.replace(/on/,"");_._ieEmuEventHandler=function($){return _($)};A.removeEventListener(B,_._ieEmuEventHandler,false)}else A.detachEvent($,_)}function a(_,$,A){if(typeof _!=typeof $)return false;if(typeof _=="object"){if(!A)for(var B in _){if(typeof $[B]=="undefined")return false;if(!a(_[B],$[B],true))return false}return true}else if(typeof _=="function"&&typeof $=="function")return _.toString()==$.toString();else return _==$}function J(){var _,A,$=Y[N][C]("script");for(var B=0;B<$.length;B++){_=$[B].getAttribute("src")||"";_=_.substr(0,_.toLowerCase().indexOf("wdatepicker.js"));A=_.lastIndexOf("/");if(A>0)_=_.substring(0,A+1);if(_)break}return _}function K(A,$,B){var D=Y[N][C]("HEAD").item(0),_=Y[N].createElement("link");if(D){_.href=A;_.rel="stylesheet";_.type="text/css";if($)_.title=$;if(B)_.charset=B;D.appendChild(_)}}function F($){$=$||V;var A=0,_=0;while($!=V){var D=$.parent[N][C]("iframe");for(var F=0;F<D.length;F++){try{if(D[F].contentWindow==$){var E=W(D[F]);A+=E.left;_+=E.top;break}}catch(B){}}$=$.parent}return{"leftM":A,"topM":_}}function W(G,F){if(G.getBoundingClientRect)return G.getBoundingClientRect();else{var A={ROOT_TAG:/^body|html$/i,OP_SCROLL:/^(?:inline|table-row)$/i},E=false,I=null,_=G.offsetTop,H=G.offsetLeft,D=G.offsetWidth,B=G.offsetHeight,C=G.offsetParent;if(C!=G)while(C){H+=C.offsetLeft;_+=C.offsetTop;if(R(C,"position").toLowerCase()=="fixed")E=true;else if(C.tagName.toLowerCase()=="body")I=C.ownerDocument.defaultView;C=C.offsetParent}C=G.parentNode;while(C.tagName&&!A.ROOT_TAG.test(C.tagName)){if(C.scrollTop||C.scrollLeft)if(!A.OP_SCROLL.test(P(C)))if(!c||C.style.overflow!=="visible"){H-=C.scrollLeft;_-=C.scrollTop}C=C.parentNode}if(!E){var $=b(I);H-=$.left;_-=$.top}D+=H;B+=_;return{"left":H,"top":_,"right":D,"bottom":B}}}function M($){$=$||V;var B=$[N],A=($.innerWidth)?$.innerWidth:(B[H]&&B[H].clientWidth)?B[H].clientWidth:B.body.offsetWidth,_=($.innerHeight)?$.innerHeight:(B[H]&&B[H].clientHeight)?B[H].clientHeight:B.body.offsetHeight;return{"width":A,"height":_}}function b($){$=$||V;var B=$[N],A=B[H],_=B.body;B=(A&&A.scrollTop!=null&&(A.scrollTop>_.scrollTop||A.scrollLeft>_.scrollLeft))?A:_;return{"top":B.scrollTop,"left":B.scrollLeft}}function D($){try{var _=$?($.srcElement||$.target):null;if($dp.cal&&!$dp.eCont&&$dp.dd&&_!=$dp.el&&$dp.dd.style.display=="block")$dp.cal.close()}catch($){}}function Z(){$dp.status=2}var Q,_;function U(K,C){if(!$dp)return;B();var L={};for(var H in K)L[H]=K[H];for(H in $)if(H.substring(0,1)!="$"&&L[H]===undefined)L[H]=$[H];if(C){if(!J()){_=_||setInterval(function(){if(V[N].readyState=="complete")clearInterval(_);U(null,true)},50);return}if($dp.status==0){$dp.status=1;L.el=T;I(L,true)}else return}else if(L.eCont){L.eCont=$dp.$(L.eCont);L.el=T;L.autoPickDate=true;L.qsEnabled=false;I(L)}else{if($.$preLoad&&$dp.status!=2)return;var F=D();if(Y.event===F||F){L.srcEl=F.srcElement||F.target;F.cancelBubble=true}L.el=L.el=$dp.$(L.el||L.srcEl);if(!L.el||L.el["My97Mark"]===true||L.el.disabled||($dp.dd&&P($dp.dd)!="none"&&$dp.dd.style.left!="-970px")){try{if(L.el["My97Mark"])L.el["My97Mark"]=false}catch(A){}return}if(F&&L.el.nodeType==1&&!a(L.el.initcfg,K)){$dp.unbind(L.el);E(L.el,F.type=="focus"?"onclick":"onfocus",function(){U(K)});L.el.initcfg=K}I(L)}function J(){if(S&&V!=Y&&V[N].readyState!="complete")return false;return true}function D(){if(G){func=D.caller;while(func!=null){var $=func.arguments[0];if($&&($+"").indexOf("Event")>=0)return $;func=func.caller}return null}return event}}function R(_,$){return _.currentStyle?_.currentStyle[$]:document.defaultView.getComputedStyle(_,false)[$]}function P(_,$){if(_)if($!=null)_.style.display=$;else return R(_,"display")}function I(G,_){var D=G.el?G.el.nodeName:"INPUT";if(_||G.eCont||new RegExp(/input|textarea|div|span|p|a/ig).test(D))G.elProp=D=="INPUT"?"value":"innerHTML";else return;if(G.lang=="auto")G.lang=S?navigator.browserLanguage.toLowerCase():navigator.language.toLowerCase();if(!G.eCont)for(var C in G)$dp[C]=G[C];if(!$dp.dd||G.eCont||($dp.dd&&(G.getRealLang().name!=$dp.dd.lang||G.skin!=$dp.dd.skin))){if(G.eCont)E(G.eCont,G);else{$dp.dd=V[N].createElement("DIV");$dp.dd.style.cssText="position:absolute";V[N].body.appendChild($dp.dd);E($dp.dd,G);if(_)$dp.dd.style.left=$dp.dd.style.top="-970px";else{$dp.show();B($dp)}}}else if($dp.cal){$dp.show();$dp.cal.init();if(!$dp.eCont)B($dp)}function E(K,J){var I=V[N].domain,F=false,G="<iframe hideFocus=true width=9 height=7 frameborder=0 border=0 scrolling=no src=\"about:blank\"></iframe>";K.innerHTML=G;var _=$.$langList,D=$.$skinList,H;try{H=K.lastChild.contentWindow[N]}catch(E){F=true;K.removeChild(K.lastChild);var L=V[N].createElement("iframe");L.hideFocus=true;L.frameBorder=0;L.scrolling="no";L.src="javascript:(function(){var d=document;d.open();d.domain='"+I+"';})()";K.appendChild(L);setTimeout(function(){H=K.lastChild.contentWindow[N];C()},97);return}C();function C(){var _=J.getRealLang();K.lang=_.name;K.skin=J.skin;var $=["<head><script>","","var doc=document, $d, $dp, $cfg=doc.cfg, $pdp = parent.$dp, $dt, $tdt, $sdt, $lastInput, $IE=$pdp.ie, $FF = $pdp.ff,$OPERA=$pdp.opera, $ny, $cMark = false;","if($cfg.eCont){$dp = {};for(var p in $pdp)$dp[p]=$pdp[p];}else{$dp=$pdp;};for(var p in $cfg){$dp[p]=$cfg[p];}","doc.oncontextmenu=function(){try{$c._fillQS(!$dp.has.d,1);showB($d.qsDivSel);}catch(e){};return false;};","</script><script src=",A,"lang/",_.name,".js charset=",_.charset,"></script>"];if(F)$[1]="document.domain=\""+I+"\";";for(var C=0;C<D.length;C++)if(D[C].name==J.skin)$.push("<link rel=\"stylesheet\" type=\"text/css\" href=\""+A+"skin/"+D[C].name+"/datepicker.css\" charset=\""+D[C].charset+"\"/>");$.push("<script src=\""+A+"calendar.js\"></script>");$.push("</head><body leftmargin=\"0\" topmargin=\"0\" tabindex=0></body></html>");$.push("<script>var t;t=t||setInterval(function(){if(doc.ready){new My97DP();$cfg.onload();$c.autoSize();$cfg.setPos($dp);clearInterval(t);}},20);</script>");J.setPos=B;J.onload=Z;H.write("<html>");H.cfg=J;H.write($.join(""));H.close()}}function B(J){var H=J.position.left,C=J.position.top,D=J.el;if(D==T)return;if(D!=J.srcEl&&(P(D)=="none"||D.type=="hidden"))D=J.srcEl;var I=W(D),$=F(Y),E=M(V),B=b(V),G=$dp.dd.offsetHeight,A=$dp.dd.offsetWidth;if(isNaN(C))C=0;if(($.topM+I.bottom+G>E.height)&&($.topM+I.top-G>0))C+=B.top+$.topM+I.top-G-2;else{C+=B.top+$.topM+I.bottom;var _=C-B.top+G-E.height;if(_>0)C-=_}if(isNaN(H))H=0;H+=B.left+Math.min($.leftM+I.left,E.width-A-5)-(S?2:0);J.dd.style.top=C+"px";J.dd.style.left=H+"px"}}})()
\ No newline at end of file
manage/page/pasteform/lib/pickerdaterange/dateRange.css
diff --git a/manage/page/pasteform/lib/pickerdaterange/dateRange.css b/manage/page/pasteform/lib/pickerdaterange/dateRange.css
index bfd5fc7..71d61bc 100644
--- a/manage/page/pasteform/lib/pickerdaterange/dateRange.css
+++ b/manage/page/pasteform/lib/pickerdaterange/dateRange.css
@@ -111,6 +111,7 @@ input{padding:2px 0 1px;*padding:4px 0 0;_padding:4px 0 0;_height:21px;} */
 	text-align: right;
 	cursor: pointer;
 	padding: 1px 2px;
+	user-select: none;
 }
 
 .gri_dateRangeDateTable th {
@@ -260,7 +261,6 @@ input{padding:2px 0 1px;*padding:4px 0 0;_padding:4px 0 0;_height:21px;} */
  	z-index:100;
  }
  .i_pre,.i_next,.ta_calendar td.ta_dateRangeSelected,.ta_calendar td.first,.ta_calendar td.last,.ta_calendar td.today{
- 	/*background:url(http://imgcache.qq.com/bossweb/mta/images/calendar_all.png) no-repeat;*/
  	background:url(./images/calendar_all.png) no-repeat;
  	cursor:pointer;
  }
@@ -341,7 +341,7 @@ input{padding:2px 0 1px;*padding:4px 0 0;_padding:4px 0 0;_height:21px;} */
  	padding: 4px 8px;
  	text-align:center;
  	white-space:nowrap;
- 	/* font-family:"΢���ź�"; */
+	user-select: none;
  	cursor:pointer;
  }
  .calendar-month td.hover,.calendar-month td:hover,.calendar-month caption span:hover{
manage/page/pasteform/lib/pickerdaterange/dateRange.js
diff --git a/manage/page/pasteform/lib/pickerdaterange/dateRange.js b/manage/page/pasteform/lib/pickerdaterange/dateRange.js
index 3c3ddfd..4ad04d8 100644
--- a/manage/page/pasteform/lib/pickerdaterange/dateRange.js
+++ b/manage/page/pasteform/lib/pickerdaterange/dateRange.js
@@ -83,6 +83,12 @@ function pickerDateRange(inputId, options) {
 	this.inputId = inputId;
 	this.inputCompareId = inputId + 'Compare';
 	this.compareInputDiv = 'div_compare_' + inputId;
+
+		    // ===== 新增:注册实例 =====
+    window.__pickerRegistry = window.__pickerRegistry || {};
+    window.__pickerRegistry[inputId] = this;
+    // =========================
+
 	// 配置参数
 	this.mOpts = $.extend({}, defaults, options);
 	//默认日历参数最大是3
@@ -466,9 +472,43 @@ function pickerDateRange(inputId, options) {
 	$(document).bind('click', function () {
 		__method.close();
 	});
+
+
+
 };
 
 /**
+ * 
+ * @param {*} inputId 
+ * @param {*} start 
+ * @param {*} end 
+ * @returns 
+ */
+function setPickerDate(inputId, start, end) {
+    $('#' + inputId).val(start + '  至  ' + end);
+    $('#' + inputId).attr('datas', start);
+    $('#' + inputId).attr('datae', end);
+
+    var picker = window.__pickerRegistry && window.__pickerRegistry[inputId];
+    if (!picker) return;
+
+    // 用实例上真实的 id,不靠拼
+    $('#' + picker.startDateId).val(start);
+    $('#' + picker.endDateId).val(end);
+
+    // 提交隐藏域
+    $('#' + picker.mOpts.startDateId).val(start);
+    $('#' + picker.mOpts.endDateId).val(end);
+
+    // 同步 mOpts
+    picker.mOpts.startDate = start;
+    picker.mOpts.endDate   = end;
+}
+
+// // 调用
+// setPickerDate('sdate', '2024-03-01', '2024-03-15');
+
+/**
  * @description 日期选择器的初始化方法,对象原型扩展
  * @param {Boolean} isCompare 标识当前初始化选择面板是否是对比日期
  */
​ ​