CrazyIter_Bin 3 jaren geleden
bovenliggende
commit
0c30bb8e68

+ 2 - 1
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -1016,7 +1016,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 {
                                 {
                                     HashSet<string> grades = new HashSet<string>();
                                     HashSet<string> grades = new HashSet<string>();
                                     List<GroupListDto> groups = await GroupListService.GetGroupListListids(client, _dingDing, lessonRecord.groupIds, lessonRecord.school);
                                     List<GroupListDto> groups = await GroupListService.GetGroupListListids(client, _dingDing, lessonRecord.groupIds, lessonRecord.school);
-                                    List<GroupListDto> groupLists = groups.FindAll(x => !string.IsNullOrEmpty(x.periodId) && x.year > 0 && !string.IsNullOrEmpty(x.school));
+                                     
+                                    List<GroupListDto> groupLists = groups?.FindAll(x => !string.IsNullOrEmpty(x.periodId) && x.year > 0 && !string.IsNullOrEmpty(x.school));
                                     if (groupLists.IsNotEmpty() && !string.IsNullOrEmpty(lessonRecord.periodId))
                                     if (groupLists.IsNotEmpty() && !string.IsNullOrEmpty(lessonRecord.periodId))
                                     {
                                     {
                                         School schoolObj = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(lessonRecord.school, new PartitionKey("Base"));
                                         School schoolObj = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(lessonRecord.school, new PartitionKey("Base"));

+ 23 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/IP2RegionExtensions.cs

@@ -0,0 +1,23 @@
+using IP2Region;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+
+namespace TEAMModelOS.SDK
+{
+    public static class IP2RegionExtensions
+    {
+        public static IServiceCollection AddIP2Region(this IServiceCollection services, string configPath)
+        {
+            if (services == null) throw new ArgumentNullException(nameof(services));
+            if (configPath == null) throw new ArgumentNullException(nameof(configPath));
+            services.TryAddSingleton(new IP2RegionFileSearcher($"{configPath}/ip2region.db"));
+            return services;
+        }
+    }
+}

+ 411 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/IP2RegionFileSearcher.cs

@@ -0,0 +1,411 @@
+using IP2Region;
+using IP2Region.Models;
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.DI
+{
+    /// <summary>
+    /// 使用文件流的形式进行IP检索
+    /// </summary>
+    public class IP2RegionFileSearcher : IDisposable
+    {
+        const int BTREE_ALGORITHM = 1;
+        const int BINARY_ALGORITHM = 2;
+        const int MEMORY_ALGORITYM = 3;
+
+        private DbConfig _dbConfig = null;
+
+        /// <summary>
+        /// db file access handler
+        /// </summary>
+        private Stream _raf = null;
+
+        /// <summary>
+        /// header blocks buffer
+        /// </summary>
+        private long[] _headerSip = null;
+        private int[] _headerPtr = null;
+        private int _headerLength;
+
+        /// <summary>
+        /// super blocks info 
+        /// </summary>
+        private long _firstIndexPtr = 0;
+        private long _lastIndexPtr = 0;
+        private int _totalIndexBlocks = 0;
+
+        /// <summary>
+        /// for memory mode
+        /// the original db binary string
+        /// </summary>
+        private byte[] _dbBinStr = null;
+
+        /// <summary>
+        /// Get by index ptr.
+        /// </summary>
+        private DataBlock GetByIndexPtr(long ptr)
+        {
+            _raf.Seek(ptr, SeekOrigin.Begin);
+            byte[] buffer = new byte[12];
+            _raf.Read(buffer, 0, buffer.Length);
+            long extra = Utils.GetIntLong(buffer, 8);
+            int dataLen = (int)((extra >> 24) & 0xFF);
+            int dataPtr = (int)((extra & 0x00FFFFFF));
+            _raf.Seek(dataPtr, SeekOrigin.Begin);
+            byte[] data = new byte[dataLen];
+            _raf.Read(data, 0, data.Length);
+            int city_id = (int)Utils.GetIntLong(data, 0);
+            string region = Encoding.UTF8.GetString(data, 4, data.Length - 4);
+            return new DataBlock(city_id, region, dataPtr);
+        }
+
+        public IP2RegionFileSearcher(DbConfig dbConfig, string dbFile)
+        {
+            if (_dbConfig == null)
+            {
+                _dbConfig = dbConfig;
+            }
+            _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read);
+        }
+
+        public IP2RegionFileSearcher(string dbFile) : this(null, dbFile) { }
+
+        public IP2RegionFileSearcher(DbConfig dbConfig, Stream dbFileStream)
+        {
+            if (_dbConfig == null)
+            {
+                _dbConfig = dbConfig;
+            }
+
+            _raf = dbFileStream;
+        }
+
+        public IP2RegionFileSearcher(Stream dbFileStream) : this(null, dbFileStream) { }
+
+        #region Sync Methods
+        /// <summary>
+        /// Get the region with a int ip address with memory binary search algorithm.
+        /// </summary>
+        private DataBlock MemorySearch(long ip)
+        {
+            int blen = IndexBlock.LENGTH;
+            if (_dbBinStr == null)
+            {
+                _dbBinStr = new byte[(int)_raf.Length];
+                _raf.Seek(0L, SeekOrigin.Begin);
+                _raf.Read(_dbBinStr, 0, _dbBinStr.Length);
+
+                //initialize the global vars
+                _firstIndexPtr = Utils.GetIntLong(_dbBinStr, 0);
+                _lastIndexPtr = Utils.GetIntLong(_dbBinStr, 4);
+                _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
+            }
+
+            //search the index blocks to define the data
+            int l = 0, h = _totalIndexBlocks;
+            long sip = 0;
+
+            while (l <= h)
+            {
+                int m = (l + h) >> 1;
+                int p = (int)(_firstIndexPtr + m * blen);
+
+                sip = Utils.GetIntLong(_dbBinStr, p);
+
+                if (ip < sip)
+                {
+                    h = m - 1;
+                }
+                else
+                {
+                    sip = Utils.GetIntLong(_dbBinStr, p + 4);
+                    if (ip > sip)
+                    {
+                        l = m + 1;
+                    }
+                    else
+                    {
+                        sip = Utils.GetIntLong(_dbBinStr, p + 8);
+                        break;
+                    }
+                }
+            }
+
+            //not matched
+            if (sip == 0) return null;
+
+            //get the data
+            int dataLen = (int)((sip >> 24) & 0xFF);
+            int dataPtr = (int)((sip & 0x00FFFFFF));
+            int city_id = (int)Utils.GetIntLong(_dbBinStr, dataPtr);
+            string region = Encoding.UTF8.GetString(_dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8);
+
+            return new DataBlock(city_id, region, dataPtr);
+        }
+
+        /// <summary>
+        /// Get the region throught the ip address with memory binary search algorithm.
+        /// </summary>
+        public DataBlock MemorySearch(string ip)
+        {
+            return MemorySearch(Utils.Ip2long(ip));
+        }
+
+        /// <summary>
+        /// Get the region with a int ip address with b-tree algorithm.
+        /// </summary>
+        private DataBlock BtreeSearch(long ip)
+        {
+            //check and load the header
+            if (_headerSip == null)
+            {
+                _raf.Seek(8L, SeekOrigin.Begin);    //pass the super block
+                byte[] b = new byte[4096];
+                _raf.Read(b, 0, b.Length);
+                //fill the header
+                int len = b.Length >> 3, idx = 0;  //b.lenght / 8
+                _headerSip = new long[len];
+                _headerPtr = new int[len];
+                long startIp, dataPtrTemp;
+                for (int i = 0; i < b.Length; i += 8)
+                {
+                    startIp = Utils.GetIntLong(b, i);
+                    dataPtrTemp = Utils.GetIntLong(b, i + 4);
+                    if (dataPtrTemp == 0) break;
+
+                    _headerSip[idx] = startIp;
+                    _headerPtr[idx] = (int)dataPtrTemp;
+                    idx++;
+                }
+                _headerLength = idx;
+            }
+
+            //1. define the index block with the binary search
+            if (ip == _headerSip[0])
+            {
+                return GetByIndexPtr(_headerPtr[0]);
+            }
+            else if (ip == _headerPtr[_headerLength - 1])
+            {
+                return GetByIndexPtr(_headerPtr[_headerLength - 1]);
+            }
+            int l = 0, h = _headerLength, sptr = 0, eptr = 0;
+            int m = 0;
+            while (l <= h)
+            {
+                m = (l + h) >> 1;
+                //perfectly matched, just return it
+                if (ip == _headerSip[m])
+                {
+                    if (m > 0)
+                    {
+                        sptr = _headerPtr[m - 1];
+                        eptr = _headerPtr[m];
+                    }
+                    else
+                    {
+                        sptr = _headerPtr[m];
+                        eptr = _headerPtr[m + 1];
+                    }
+                }
+                //less then the middle value
+                else if (ip < _headerSip[m])
+                {
+                    if (m == 0)
+                    {
+                        sptr = _headerPtr[m];
+                        eptr = _headerPtr[m + 1];
+                        break;
+                    }
+                    else if (ip > _headerSip[m - 1])
+                    {
+                        sptr = _headerPtr[m - 1];
+                        eptr = _headerPtr[m];
+                        break;
+                    }
+                    h = m - 1;
+                }
+                else
+                {
+                    if (m == _headerLength - 1)
+                    {
+                        sptr = _headerPtr[m - 1];
+                        eptr = _headerPtr[m];
+                        break;
+                    }
+                    else if (ip <= _headerSip[m + 1])
+                    {
+                        sptr = _headerPtr[m];
+                        eptr = _headerPtr[m + 1];
+                        break;
+                    }
+                    l = m + 1;
+                }
+            }
+            //match nothing just stop it
+            if (sptr == 0) return null;
+            //2. search the index blocks to define the data
+            int blockLen = eptr - sptr, blen = IndexBlock.LENGTH;
+            byte[] iBuffer = new byte[blockLen + blen];    //include the right border block
+            _raf.Seek(sptr, SeekOrigin.Begin);
+            _raf.Read(iBuffer, 0, iBuffer.Length);
+            l = 0; h = blockLen / blen;
+            long sip = 0;
+            int p = 0;
+            while (l <= h)
+            {
+                m = (l + h) >> 1;
+                p = m * blen;
+                sip = Utils.GetIntLong(iBuffer, p);
+                if (ip < sip)
+                {
+                    h = m - 1;
+                }
+                else
+                {
+                    sip = Utils.GetIntLong(iBuffer, p + 4);
+                    if (ip > sip)
+                    {
+                        l = m + 1;
+                    }
+                    else
+                    {
+                        sip = Utils.GetIntLong(iBuffer, p + 8);
+                        break;
+                    }
+                }
+            }
+            //not matched
+            if (sip == 0) return null;
+            //3. get the data
+            int dataLen = (int)((sip >> 24) & 0xFF);
+            int dataPtr = (int)((sip & 0x00FFFFFF));
+            _raf.Seek(dataPtr, SeekOrigin.Begin);
+            byte[] data = new byte[dataLen];
+            _raf.Read(data, 0, data.Length);
+            int city_id = (int)Utils.GetIntLong(data, 0);
+            String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);// new String(data, 4, data.Length - 4, "UTF-8");
+            return new DataBlock(city_id, region, dataPtr);
+        }
+
+        /// <summary>
+        /// Get the region throught the ip address with b-tree search algorithm.
+        /// </summary>
+        public DataBlock BtreeSearch(string ip)
+        {
+            return BtreeSearch(Utils.Ip2long(ip));
+        }
+
+        /// <summary>
+        /// Get the region with a int ip address with binary search algorithm.
+        /// </summary>
+        private DataBlock BinarySearch(long ip)
+        {
+            int blen = IndexBlock.LENGTH;
+            if (_totalIndexBlocks == 0)
+            {
+                _raf.Seek(0L, SeekOrigin.Begin);
+                byte[] superBytes = new byte[8];
+                _raf.Read(superBytes, 0, superBytes.Length);
+                //initialize the global vars
+                _firstIndexPtr = Utils.GetIntLong(superBytes, 0);
+                _lastIndexPtr = Utils.GetIntLong(superBytes, 4);
+                _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
+            }
+
+            //search the index blocks to define the data
+            int l = 0, h = _totalIndexBlocks;
+            byte[] buffer = new byte[blen];
+            long sip = 0;
+            while (l <= h)
+            {
+                int m = (l + h) >> 1;
+                _raf.Seek(_firstIndexPtr + m * blen, SeekOrigin.Begin);    //set the file pointer
+                _raf.Read(buffer, 0, buffer.Length);
+                sip = Utils.GetIntLong(buffer, 0);
+                if (ip < sip)
+                {
+                    h = m - 1;
+                }
+                else
+                {
+                    sip = Utils.GetIntLong(buffer, 4);
+                    if (ip > sip)
+                    {
+                        l = m + 1;
+                    }
+                    else
+                    {
+                        sip = Utils.GetIntLong(buffer, 8);
+                        break;
+                    }
+                }
+            }
+            //not matched
+            if (sip == 0) return null;
+            //get the data
+            int dataLen = (int)((sip >> 24) & 0xFF);
+            int dataPtr = (int)((sip & 0x00FFFFFF));
+            _raf.Seek(dataPtr, SeekOrigin.Begin);
+            byte[] data = new byte[dataLen];
+            _raf.Read(data, 0, data.Length);
+            int city_id = (int)Utils.GetIntLong(data, 0);
+            String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);//new String(data, 4, data.Length - 4, "UTF-8");
+            return new DataBlock(city_id, region, dataPtr);
+        }
+
+        /// <summary>
+        /// Get the region throught the ip address with binary search algorithm.
+        /// </summary>
+        public DataBlock BinarySearch(String ip)
+        {
+            return BinarySearch(Utils.Ip2long(ip));
+        }
+        #endregion
+
+        #region Async Methods
+        /// <summary>
+        /// Get the region throught the ip address with memory binary search algorithm.
+        /// </summary>
+        public Task<DataBlock> MemorySearchAsync(string ip)
+        {
+            return Task.FromResult(MemorySearch(ip));
+        }
+        /// <summary>
+        /// Get the region throught the ip address with b-tree search algorithm.
+        /// </summary>
+        public Task<DataBlock> BtreeSearchAsync(string ip)
+        {
+            return Task.FromResult(BtreeSearch(ip));
+        }
+        /// <summary>
+        /// Get the region throught the ip address with binary search algorithm.
+        /// </summary>
+        public Task<DataBlock> BinarySearchAsync(string ip)
+        {
+            return Task.FromResult(BinarySearch(ip));
+        }
+        #endregion
+
+        /// <summary>
+        /// Close the db.
+        /// </summary>
+        public void Close()
+        {
+            _headerSip = null;
+            _headerPtr = null;
+            _dbBinStr = null;
+            _raf.Close();
+        }
+
+        public void Dispose()
+        {
+            Close();
+        }
+    }
+}

+ 13 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/BlockSize.cs

@@ -0,0 +1,13 @@
+using System.Runtime.CompilerServices;
+
+namespace IP2Region.Models
+{
+    internal class BlockSize
+    {
+        internal readonly static int SuperBlockSize = Unsafe.SizeOf<HeaderBlock>();
+
+        internal readonly static int HeaderBlockSize = Unsafe.SizeOf<HeaderBlock>();
+
+        internal readonly static int IndexBlockSize = Unsafe.SizeOf<IndexBlock>();
+    }
+}

+ 50 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/DataBlock.cs

@@ -0,0 +1,50 @@
+//*******************************
+// Created By Rocher Kong 
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+namespace IP2Region.Models
+{
+    public class DataBlock
+    {
+        #region Private Properties
+        public int CityID
+        {
+            get;
+            private set;
+        }
+
+        public string Region
+        {
+            get;
+            private set;
+        }
+
+        public int DataPtr
+        {
+            get;
+            private set;
+        }
+        #endregion
+
+        #region Constructor
+        public DataBlock(int city_id, string region, int dataPtr = 0)
+        {
+            CityID = city_id;
+            Region = region;
+            DataPtr = dataPtr;
+        }
+
+        public DataBlock(int city_id, string region):this(city_id,region,0)
+        {
+        }
+        #endregion
+
+        public override string ToString()
+        {
+            return $"{CityID}|{Region}|{DataPtr}";
+        }
+
+    }
+
+}

+ 49 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/DbConfig.cs

@@ -0,0 +1,49 @@
+//*******************************
+// Created By Rocher Kong 
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+using System;
+
+namespace IP2Region.Models
+{
+    public class DbMakerConfigException : Exception
+    {
+        public string ErrMsg { get; private set; }
+        public DbMakerConfigException(string errMsg)
+        {
+            ErrMsg = errMsg;
+        }
+    }
+
+    public class DbConfig
+    {
+        public int TotalHeaderSize
+        {
+            get;
+            private set;
+        }
+
+        public int indexBlockSize
+        {
+            get;
+            private set;
+        }
+
+        public DbConfig(int totalHeaderSize)
+        {
+            if ((totalHeaderSize % 8) != 0)
+            {
+                throw new DbMakerConfigException("totalHeaderSize must be times of 8");
+            }
+            TotalHeaderSize = totalHeaderSize;
+            //4 * 2048
+            indexBlockSize = 8192; 
+        }
+
+        public DbConfig():this(8 * 2048)
+        {
+        }
+    }
+
+}

+ 49 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/HeaderBlock.cs

@@ -0,0 +1,49 @@
+//*******************************
+// Created By Rocher Kong 
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+
+namespace IP2Region.Models
+{
+    internal class HeaderBlock
+    {
+        public long IndexStartIp
+        {
+            get;
+            private set;
+        }
+
+        public int IndexPtr
+        {
+            get;
+            private set;
+        }
+
+        public HeaderBlock(long indexStartIp, int indexPtr)
+        {
+            IndexStartIp = indexStartIp;
+            IndexPtr = indexPtr;
+        }
+
+        /// <summary>
+        /// Get the bytes for total storage
+        /// </summary>
+        /// <returns>
+        /// Bytes gotten.
+        /// </returns>
+        public byte[] GetBytes()
+        {
+            /*
+             * +------------+-----------+
+             * | 4bytes     | 4bytes    |
+             * +------------+-----------+
+             *  start ip      index ptr
+            */
+            byte[] b = new byte[8];
+            Utils.WriteIntLong(b, 0, IndexStartIp);
+            Utils.WriteIntLong(b, 4, IndexPtr);
+            return b;
+        }
+    }
+}

+ 65 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/IndexBlock.cs

@@ -0,0 +1,65 @@
+//*******************************
+// Created By Rocher Kong 
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+
+namespace IP2Region
+{
+    internal class IndexBlock
+    {
+        public const int LENGTH = 12;
+
+        public long StartIP
+        {
+            get;
+            private set;
+        }
+
+        public long EndIp
+        {
+            get;
+            private set;
+        }
+
+        public uint DataPtr
+        {
+            get;
+            private set;
+        }
+
+        public int DataLen
+        {
+            get;
+            private set;
+        }
+
+        public IndexBlock(long startIp, long endIp, uint dataPtr, int dataLen)
+        {
+            StartIP = startIp;
+            EndIp = endIp;
+            DataPtr = dataPtr;
+            DataLen = dataLen;
+        }
+
+        public byte[] GetBytes()
+        {
+            /*
+             * +------------+-----------+-----------+
+             * | 4bytes     | 4bytes    | 4bytes    |
+             * +------------+-----------+-----------+
+             *  start ip      end ip      data ptr + len 
+            */
+            byte[] b = new byte[12];
+
+            Utils.WriteIntLong(b, 0, StartIP);    //start ip
+            Utils.WriteIntLong(b, 4, EndIp);        //end ip
+
+            //write the data ptr and the length
+            long mix = DataPtr | ((DataLen << 24) & 0xFF000000L);
+            Utils.WriteIntLong(b, 8, mix);
+
+            return b;
+        }
+    }
+}

+ 100 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/Int24.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace IP2Region.Models
+{
+    internal struct Int24
+    {
+        private byte Byte1;
+
+        private byte Byte2;
+
+        private byte Byte3;
+
+        internal Int24(int value)
+        {
+            if (value > 0x00FFFFFF)
+            {
+                throw new ArgumentException("Int24 max value must be smaller than 0x00FFFFFF");
+            }
+            ref byte ptr = ref Unsafe.As<int, byte>(ref value);
+            Byte1 = ptr;
+            Byte2 = Unsafe.Add(ref ptr, 1);
+            Byte3 = Unsafe.Add(ref ptr, 2);
+        }
+
+        internal void SetValue(int value)
+        {
+            if (value > 0x00FFFFFF)
+            {
+                throw new ArgumentException("Int24 max value must be smaller than 0x00FFFFFF");
+            }
+            ref byte ptr = ref Unsafe.As<int, byte>(ref value);
+            Byte1 = ptr;
+            Byte2 = Unsafe.Add(ref ptr, 1);
+            Byte3 = Unsafe.Add(ref ptr, 2);
+        }
+
+        internal int GetValue()
+        {
+            int value = 0;
+            ref byte ptr = ref Unsafe.As<int, byte>(ref value);
+            ptr = Byte1;
+            Unsafe.Add(ref ptr, 1) = Byte2;
+            Unsafe.Add(ref ptr, 2) = Byte3;
+            return value;
+        }
+
+        public static int operator +(Int24 a, Int24 b)
+        {
+            return a.GetValue() + b.GetValue();
+        }
+
+        public static Int24 operator -(Int24 a, Int24 b)
+        {
+            return new Int24(a.GetValue() - b.GetValue());
+        }
+
+        public static int operator *(Int24 a, Int24 b)
+        {
+            return a.GetValue() * b.GetValue();
+        }
+
+        public static int operator /(Int24 a, Int24 b)
+        {
+            return new Int24(a.GetValue() / b.GetValue());
+        }
+
+        public static bool operator ==(int a, Int24 b)
+        {
+            return a == b.GetValue();
+        }
+
+        public static bool operator !=(int a, Int24 b)
+        {
+            return a == b.GetValue();
+        }
+
+        public static implicit operator int (Int24 operand)
+        {
+            return operand.GetValue();
+        }
+
+        public static explicit operator Int24(int operand)
+        {
+            return new Int24(operand);
+        }
+
+        public override bool Equals(object obj)
+        {
+            int value = (int)obj;
+            return value == GetValue();
+        }
+
+        public override int GetHashCode()
+        {
+            return GetValue();
+        }
+    }
+}

+ 15 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/SuperBlock.cs

@@ -0,0 +1,15 @@
+namespace IP2Region.Models
+{
+    internal struct SuperBlock
+    {
+        /// <summary>
+        /// 指向INDEX起始位置的index block
+        /// </summary>
+        internal int FirstIndexPtr;
+
+        /// <summary>
+        /// 指向最后一个index block的地址
+        /// </summary>
+        internal int LastIndexPtr;
+    }
+}

+ 122 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/Utils.cs

@@ -0,0 +1,122 @@
+//*******************************
+// Created By Rocher Kong 
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+
+using System;
+
+namespace IP2Region
+{
+    public class IPInValidException : Exception
+    {
+        const string ERROR_MSG = "IP Illigel. Please input a valid IP.";
+        public IPInValidException() : base(ERROR_MSG) { }
+    }
+    internal static class Utils
+    {
+        /// <summary>
+        /// Write specfield bytes to a byte array start from offset.
+        /// </summary>
+        public static void Write(byte[] b, int offset, ulong v, int bytes)
+        {
+            for (int i = 0; i < bytes; i++)
+            {
+                b[offset++] = (byte)((v >> (8 * i)) & 0xFF);
+            }
+        }
+
+        /// <summary>
+        /// Write a int to a byte array.
+        /// </summary>
+        public static void WriteIntLong(byte[] b, int offset, long v)
+        {
+            b[offset++] = (byte)((v >> 0) & 0xFF);
+            b[offset++] = (byte)((v >> 8) & 0xFF);
+            b[offset++] = (byte)((v >> 16) & 0xFF);
+            b[offset] = (byte)((v >> 24) & 0xFF);
+        }
+
+        /// <summary>
+        /// Get a int from a byte array start from the specifiled offset.
+        /// </summary>
+        public static long GetIntLong(byte[] b, int offset)
+        {
+            return (
+                ((b[offset++] & 0x000000FFL)) |
+                ((b[offset++] << 8) & 0x0000FF00L) |
+                ((b[offset++] << 16) & 0x00FF0000L) |
+                ((b[offset] << 24) & 0xFF000000L)
+            );
+        }
+
+        /// <summary>
+        /// Get a int from a byte array start from the specifield offset.
+        /// </summary>
+        public static int GetInt3(byte[] b, int offset)
+        {
+            return (
+                (b[offset++] & 0x000000FF) |
+                (b[offset++] & 0x0000FF00) |
+                (b[offset] & 0x00FF0000)
+            );
+        }
+
+        public static int GetInt2(byte[] b, int offset)
+        {
+            return (
+                (b[offset++] & 0x000000FF) |
+                (b[offset] & 0x0000FF00)
+            );
+        }
+
+        public static int GetInt1(byte[] b, int offset)
+        {
+            return (
+                (b[offset] & 0x000000FF)
+            );
+        }
+        /// <summary>
+        /// String ip to long ip.
+        /// </summary>
+        public static long Ip2long(string ip)
+        {
+            string[] p = ip.Split('.');
+            if (p.Length != 4) throw new IPInValidException();
+
+            foreach (string pp in p)
+            {
+                if (pp.Length > 3) throw new IPInValidException();
+                if (!int.TryParse(pp, out int value) || value > 255)
+                {
+                    throw new IPInValidException();
+                }
+            }
+            var bip1 = long.TryParse(p[0], out long ip1);
+            var bip2 = long.TryParse(p[1], out long ip2);
+            var bip3 = long.TryParse(p[2], out long ip3);
+            var bip4 = long.TryParse(p[3], out long ip4);
+
+            if (!bip1 || !bip2 || !bip3 || !bip4
+                || ip4 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255
+                || ip4 < 0 || ip1 < 0 || ip2 < 0 || ip3 < 0)
+            {
+                throw new IPInValidException();
+            }
+            long p1 = ((ip1 << 24) & 0xFF000000);
+            long p2 = ((ip2 << 16) & 0x00FF0000);
+            long p3 = ((ip3 << 8) & 0x0000FF00);
+            long p4 = ((ip4 << 0) & 0x000000FF);
+            return ((p1 | p2 | p3 | p4) & 0xFFFFFFFFL);
+        }
+
+        /// <summary>
+        /// Int to ip string.
+        /// </summary>
+        public static string Long2ip(long ip)
+        {
+            return $"{(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}";
+        }
+    }
+
+}

+ 2 - 0
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -27,6 +27,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.1" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.1" />
+    <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.2" />
     <PackageReference Include="Microsoft.Identity.Client" Version="4.39.0" />
     <PackageReference Include="Microsoft.Identity.Client" Version="4.39.0" />
     <PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
     <PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
     <PackageReference Include="NUnit" Version="3.13.2" />
     <PackageReference Include="NUnit" Version="3.13.2" />
@@ -37,6 +38,7 @@
     <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="2.0.0-preview" />
     <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="2.0.0-preview" />
     <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
     <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
     <PackageReference Include="NPinyin.Core" Version="3.0.0" />
     <PackageReference Include="NPinyin.Core" Version="3.0.0" />
+    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
     <PackageReference Include="VueCliMiddleware" Version="6.0.0" />
     <PackageReference Include="VueCliMiddleware" Version="6.0.0" />
 
 
   </ItemGroup>
   </ItemGroup>

+ 9 - 8
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -377,6 +377,7 @@ namespace TEAMModelOS.Controllers.Core
                 if (!request.TryGetProperty("schoolId", out JsonElement _schoolId)) return BadRequest();
                 if (!request.TryGetProperty("schoolId", out JsonElement _schoolId)) return BadRequest();
                 if (!request.TryGetProperty("sheetNo", out JsonElement _sheetNo)) return BadRequest();
                 if (!request.TryGetProperty("sheetNo", out JsonElement _sheetNo)) return BadRequest();
                 if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
                 if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
+                if (!request.TryGetProperty("owner", out JsonElement _owner)) return BadRequest();
                 var client = _azureCosmos.GetCosmosClient();
                 var client = _azureCosmos.GetCosmosClient();
                 School school = null;
                 School school = null;
                 try
                 try
@@ -394,16 +395,16 @@ namespace TEAMModelOS.Controllers.Core
                
                
                 if ($"{_scope}".Equals("school", StringComparison.OrdinalIgnoreCase))
                 if ($"{_scope}".Equals("school", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    examData = await GetExamBySheet("school", $"{_schoolId}", $"{_schoolId}", client,  classesSet, $"{_sheetNo}");
+                    examData = await GetExamBySheet("school", $"{_schoolId}", $"{_schoolId}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
                     if (response.Status == 200 && examData == null)
                     if (response.Status == 200 && examData == null)
                     {
                     {
                         //获取scope=school
                         //获取scope=school
-                        examData = await GetExamBySheet("school", $"{_schoolId}", $"{id}", client,  classesSet, $"{_sheetNo}");
+                        examData = await GetExamBySheet("school", $"{_schoolId}", $"{id}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
                     }
                     }
                 }
                 }
                 else {
                 else {
                     //获取scope=private
                     //获取scope=private
-                    examData = await GetExamBySheet("private", null, $"{id}", client,  classesSet, $"{_sheetNo}");
+                    examData = await GetExamBySheet("private", null, $"{id}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
                 }
                 }
 
 
                 (List<RMember> tmdIds, List<RGroupList> classInfo) = await GroupListService.GetStutmdidListids(client, _dingDing, classesSet.ToList(), $"{_schoolId}");
                 (List<RMember> tmdIds, List<RGroupList> classInfo) = await GroupListService.GetStutmdidListids(client, _dingDing, classesSet.ToList(), $"{_schoolId}");
@@ -480,11 +481,11 @@ namespace TEAMModelOS.Controllers.Core
             }
             }
         }
         }
 
 
-        private async Task<ExamData> GetExamBySheet(string scope, string school, string code, CosmosClient client,HashSet<string> classesSet, string sheetNo)
+        private async Task<ExamData> GetExamBySheet(string scope, string school, string code, CosmosClient client,HashSet<string> classesSet, string sheetNo,string owner)
         {
         {
             ExamData examRcds = null;
             ExamData examRcds = null;
-            int i = 1;
-            StringBuilder sql = new StringBuilder($"SELECT  value(c) FROM c  join papers in c.papers where c.scope='{scope}' and papers.sheetNo='{sheetNo}' ");
+    
+            StringBuilder sql = new StringBuilder($"SELECT  value(c) FROM c  join papers in c.papers where  c.owner='{owner}' and  c.scope='{scope}' and papers.sheetNo='{sheetNo}' ");
             if (!string.IsNullOrEmpty(school))
             if (!string.IsNullOrEmpty(school))
             {
             {
                 sql.Append($"  and c.school='{school}' ");
                 sql.Append($"  and c.school='{school}' ");
@@ -515,12 +516,12 @@ namespace TEAMModelOS.Controllers.Core
                 {
                 {
                     if (exam.scope.Equals("school"))
                     if (exam.scope.Equals("school"))
                     {
                     {
-                        config = new SheetConfig { id = pap.sheet, scope = exam.scope, code = $"SheetConfig-{exam.school}" };
+                        config = new SheetConfig { id = pap.sheet, scope = pap.scope, code = $"SheetConfig-{exam.school}" };
                         paper=new PaperRcdData { name = pap.name, answers = pap.answers, point = pap.point };
                         paper=new PaperRcdData { name = pap.name, answers = pap.answers, point = pap.point };
                     }
                     }
                     else
                     else
                     {
                     {
-                        config = new SheetConfig { id = pap.sheet, scope = exam.scope, code = $"SheetConfig-{code}" };
+                        config = new SheetConfig { id = pap.sheet, scope = pap.scope, code = $"SheetConfig-{code}" };
                         paper = new PaperRcdData { name = pap.name, answers = pap.answers, point = pap.point };
                         paper = new PaperRcdData { name = pap.name, answers = pap.answers, point = pap.point };
                     }
                     }
                 }
                 }

+ 46 - 8
TEAMModelOS/Controllers/Teacher/InitController.cs

@@ -26,6 +26,7 @@ using TEAMModelOS.SDK;
 using static TEAMModelOS.SDK.Models.Teacher;
 using static TEAMModelOS.SDK.Models.Teacher;
 using TEAMModelOS.Services;
 using TEAMModelOS.Services;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Authorization;
+using System.Diagnostics;
 
 
 namespace TEAMModelOS.Controllers
 namespace TEAMModelOS.Controllers
 {
 {
@@ -43,8 +44,10 @@ namespace TEAMModelOS.Controllers
         private readonly IConfiguration _configuration;
         private readonly IConfiguration _configuration;
         private readonly NotificationService _notificationService;
         private readonly NotificationService _notificationService;
         private readonly CoreAPIHttpService _coreAPIHttpService;
         private readonly CoreAPIHttpService _coreAPIHttpService;
-        public InitController(CoreAPIHttpService coreAPIHttpService,AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
+        private readonly IP2RegionFileSearcher _iP2Region;
+        public InitController(IP2RegionFileSearcher iP2Region, CoreAPIHttpService coreAPIHttpService,AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
         {
         {
+            _iP2Region = iP2Region;
             _azureCosmos = azureCosmos;
             _azureCosmos = azureCosmos;
             _azureStorage = azureStorage;
             _azureStorage = azureStorage;
             _dingDing = dingDing;
             _dingDing = dingDing;
@@ -231,16 +234,19 @@ namespace TEAMModelOS.Controllers
         [HttpPost("get-teacher-info")]
         [HttpPost("get-teacher-info")]
         public async Task<IActionResult> GetTeacherInfo(JsonElement request)
         public async Task<IActionResult> GetTeacherInfo(JsonElement request)
         {
         {
+            if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
+            var jwt = new JwtSecurityToken(id_token.GetString());
+            //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
+            var id = jwt.Payload.Sub;
+            jwt.Payload.TryGetValue("name", out object name);
+            jwt.Payload.TryGetValue("picture", out object picture);
             try
             try
             {
             {
-                if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
-                var jwt = new JwtSecurityToken(id_token.GetString());
-                //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
-                var id = jwt.Payload.Sub;
-                jwt.Payload.TryGetValue("name", out object name);
-                jwt.Payload.TryGetValue("picture", out object picture);
+               
                 Teacher teacher = null;
                 Teacher teacher = null;
                 TeacherInfo teacherInfo= await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{name}", $"{picture}", id, _azureStorage, _option);
                 TeacherInfo teacherInfo= await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{name}", $"{picture}", id, _azureStorage, _option);
+
+                LoginLog(id, $"{name}", 200);
                 return Ok(new { location = _option.Location, teacherInfo. auth_token, teacherInfo. blob_uri, teacherInfo.blob_sas, teacherInfo.schools, teacherInfo.defaultschool, teacherInfo. courses,
                 return Ok(new { location = _option.Location, teacherInfo. auth_token, teacherInfo. blob_uri, teacherInfo.blob_sas, teacherInfo.schools, teacherInfo.defaultschool, teacherInfo. courses,
                     teacherInfo.total,
                     teacherInfo.total,
                     teacherInfo.osblob_uri,
                     teacherInfo.osblob_uri,
@@ -250,16 +256,48 @@ namespace TEAMModelOS.Controllers
             }
             }
             catch (CosmosException ex)
             catch (CosmosException ex)
             {
             {
+                LoginLog(id, $"{name}", 500);
                 await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
                 return BadRequest();
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                LoginLog(id, $"{name}", 500);
+                _ = _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
                 return BadRequest();
             }
             }
         }
         }
 
 
+
+        private async void LoginLog(string id, string name, int status) {
+            HttpContext.Request.Headers.TryGetValue("referer", out var referer);
+            HttpContext.Request.Headers.TryGetValue("sec-ch-ua", out var chua);
+            HttpContext.Request.Headers.TryGetValue("sec-ch-ua-platform", out var platform);
+            HttpContext.Request.Headers.TryGetValue("user-agent", out var useragent);
+       
+            var userHostAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
+            if (string.IsNullOrEmpty(userHostAddress))
+            {
+                userHostAddress = HttpContext.Connection.RemoteIpAddress.ToString();
+            }
+            if (userHostAddress.Contains("::")) 
+            {
+                userHostAddress = "127.0.0.1";
+            }
+           var a=  userHostAddress.Split("[:]")[0];
+            var ipinfo= _iP2Region.BtreeSearch(a);
+            if (status == 200)
+            {
+                Trace.TraceInformation($"{name}({id}):Login Succeed! --Time:{DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")} " +
+                    $"--Browser Info( --referer:{referer}  --sec-ch-ua:{chua}  --sec-ch-ua-platform:{platform}  --user-agent:{useragent}  --ip:{userHostAddress} --ipinfo:{ipinfo.ToJsonString()})");
+            }
+            else
+            {
+                Trace.TraceError($"{name}({id}):Login Failed! --Time:{DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")} " +
+                      $"--Browser Info( --referer:{referer}  --sec-ch-ua:{chua}  --sec-ch-ua-platform:{platform}  --user-agent:{useragent}  --ip:{userHostAddress}--ipinfo:{ipinfo.ToJsonString()})");
+            }
+            
+        }
         //TODO 此API需處理對應前端返回的相關數據
         //TODO 此API需處理對應前端返回的相關數據
         [ProducesDefaultResponseType]
         [ProducesDefaultResponseType]
         [Authorize(Roles = "IES")]
         [Authorize(Roles = "IES")]

File diff suppressed because it is too large
+ 683591 - 0
TEAMModelOS/JsonFile/Core/ip.merge.txt


BIN
TEAMModelOS/JsonFile/Core/ip2region.db


+ 1 - 0
TEAMModelOS/Startup.cs

@@ -118,6 +118,7 @@ namespace TEAMModelOS
             //注入word 標籤解析
             //注入word 標籤解析
             string path = $"{ environment.ContentRootPath}/JsonFile/Core";
             string path = $"{ environment.ContentRootPath}/JsonFile/Core";
             services.AddHtexTranslator(path);
             services.AddHtexTranslator(path);
+            services.AddIP2Region(path);
             services.AddServerSentEvents(o =>
             services.AddServerSentEvents(o =>
             {
             {
                 o.KeepaliveMode = ServerSentEventsKeepaliveMode.Always;
                 o.KeepaliveMode = ServerSentEventsKeepaliveMode.Always;