瀏覽代碼

检查订单状态增加查询个人卡云端没有消单,获取用户信息使用redis

chenwei 2 周之前
父節點
當前提交
ba92bf49a7

二進制
.vs/smartfueltool/DesignTimeBuild/.dtbcache.v2


二進制
.vs/smartfueltool/v17/.futdcache.v2


+ 7 - 0
UtilityCass/Class1.cs

@@ -0,0 +1,7 @@
+namespace UtilityCass
+{
+    public class Class1
+    {
+
+    }
+}

+ 9 - 0
UtilityCass/UtilityCass.csproj

@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>

+ 7 - 0
smartfueltool.sln

@@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MS.Services", "src\MS.Servi
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MS.WebApi", "src\MS.WebApi\MS.WebApi.csproj", "{3141D06A-A07B-4FA2-89E9-08A9D2C0F06F}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilityCass", "UtilityCass\UtilityCass.csproj", "{18E249EC-40C6-47C7-864B-AB5D7329DA42}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -85,6 +87,10 @@ Global
 		{3141D06A-A07B-4FA2-89E9-08A9D2C0F06F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3141D06A-A07B-4FA2-89E9-08A9D2C0F06F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3141D06A-A07B-4FA2-89E9-08A9D2C0F06F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{18E249EC-40C6-47C7-864B-AB5D7329DA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{18E249EC-40C6-47C7-864B-AB5D7329DA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{18E249EC-40C6-47C7-864B-AB5D7329DA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{18E249EC-40C6-47C7-864B-AB5D7329DA42}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -107,6 +113,7 @@ Global
 		{74B127A1-2427-42A2-AF94-881603FDBB1F} = {433E0684-F807-470D-8323-9D7613E97462}
 		{614D5F01-9D21-40B5-AE2E-8AFA23F8D72F} = {DAA14B58-0D03-4CBE-A236-6E030CF4862B}
 		{3141D06A-A07B-4FA2-89E9-08A9D2C0F06F} = {6E003534-115A-4186-855F-92CD9AD843A0}
+		{18E249EC-40C6-47C7-864B-AB5D7329DA42} = {99BBE9DB-016F-4795-AAD9-531D5F60A7D7}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {F2C6288D-0E94-4685-8AB6-D0E4FB1BBA93}

+ 1 - 1
src/MS.Common/MS.Common.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>

+ 17 - 0
src/MS.Models/IFuelSendSMS.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MS.Models
+{
+    public class IFuelSendSMSDto
+    {
+        public string BUID { get; set; }
+        public string SendType { get; set; }
+        public string cardNo { get; set; }
+        public string time { get; set; }
+        public string amount { get; set; }
+        public string balance { get; set; }
+        public string Phone { get; set; }
+    }
+}

+ 1 - 0
src/MS.Models/MS.Models.csproj

@@ -6,6 +6,7 @@
 
   <ItemGroup>
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
+    <PackageReference Include="Portable.BouncyCastle" Version="1.8.6" />
   </ItemGroup>
 
   <ItemGroup>

+ 458 - 0
src/MS.Models/SM4.cs

@@ -0,0 +1,458 @@
+using Org.BouncyCastle.Utilities.Encoders;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace MS.Models
+{
+    public class Sm4
+    {
+        public const int SM4_ENCRYPT = 1;
+        public const int SM4_DECRYPT = 0;
+
+        private long GET_ULONG_BE(byte[] b, int i)
+        {
+            long n = (long)(b[i] & 0xff) << 24 | (long)((b[i + 1] & 0xff) << 16) | (long)((b[i + 2] & 0xff) << 8) | (long)(b[i + 3] & 0xff) & 0xffffffffL;
+            return n;
+        }
+        private void PUT_ULONG_BE(long n, byte[] b, int i)
+        {
+            b[i] = (byte)(int)(0xFF & n >> 24);
+            b[i + 1] = (byte)(int)(0xFF & n >> 16);
+            b[i + 2] = (byte)(int)(0xFF & n >> 8);
+            b[i + 3] = (byte)(int)(0xFF & n);
+        }
+        private long SHL(long x, int n)
+        {
+            return (x & 0xFFFFFFFF) << n;
+        }
+
+        private long ROTL(long x, int n)
+        {
+            return SHL(x, n) | x >> (32 - n);
+        }
+
+        private void SWAP(long[] sk, int i)
+        {
+            long t = sk[i];
+            sk[i] = sk[(31 - i)];
+            sk[(31 - i)] = t;
+        }
+
+        public byte[] SboxTable = new byte[] { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
+            (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
+            0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
+            (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
+            (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
+            (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
+            (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
+            (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
+            (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
+            (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
+            0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
+            (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
+            0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
+            0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
+            (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
+            (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
+            (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
+            (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
+            0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
+            (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
+            (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
+            (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
+            (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
+            (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
+            0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
+            (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
+            0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
+            (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
+            (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
+            0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
+            (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
+            0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
+            (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
+            0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
+            (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
+            (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
+            (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
+            0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
+            (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 };
+        public uint[] FK = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
+        public uint[] CK = { 0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
+                                        0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
+                                        0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
+                                        0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
+                                        0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
+                                        0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
+                                        0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
+                                        0x10171e25,0x2c333a41,0x484f565d,0x646b7279 };
+        private byte sm4Sbox(byte inch)
+        {
+            int i = inch & 0xFF;
+            byte retVal = SboxTable[i];
+            return retVal;
+        }
+        private long sm4Lt(long ka)
+        {
+            long bb = 0L;
+            long c = 0L;
+            byte[] a = new byte[4];
+            byte[] b = new byte[4];
+            PUT_ULONG_BE(ka, a, 0);
+            b[0] = sm4Sbox(a[0]);
+            b[1] = sm4Sbox(a[1]);
+            b[2] = sm4Sbox(a[2]);
+            b[3] = sm4Sbox(a[3]);
+            bb = GET_ULONG_BE(b, 0);
+            c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24);
+            return c;
+        }
+        private long sm4F(long x0, long x1, long x2, long x3, long rk)
+        {
+            return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
+        }
+
+        private long sm4CalciRK(long ka)
+        {
+            long bb = 0L;
+            long rk = 0L;
+            byte[] a = new byte[4];
+            byte[] b = new byte[4];
+            PUT_ULONG_BE(ka, a, 0);
+            b[0] = sm4Sbox(a[0]);
+            b[1] = sm4Sbox(a[1]);
+            b[2] = sm4Sbox(a[2]);
+            b[3] = sm4Sbox(a[3]);
+            bb = GET_ULONG_BE(b, 0);
+            rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23);
+            return rk;
+        }
+
+        private void sm4_setkey(long[] SK, byte[] key)
+        {
+            long[] MK = new long[4];
+            long[] k = new long[36];
+            int i = 0;
+            MK[0] = GET_ULONG_BE(key, 0);
+            MK[1] = GET_ULONG_BE(key, 4);
+            MK[2] = GET_ULONG_BE(key, 8);
+            MK[3] = GET_ULONG_BE(key, 12);
+            k[0] = MK[0] ^ (long)FK[0];
+            k[1] = MK[1] ^ (long)FK[1];
+            k[2] = MK[2] ^ (long)FK[2];
+            k[3] = MK[3] ^ (long)FK[3];
+            for (; i < 32; i++)
+            {
+                k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long)CK[i]));
+                SK[i] = k[(i + 4)];
+            }
+        }
+
+        private void sm4_one_round(long[] sk, byte[] input, byte[] output)
+        {
+            int i = 0;
+            long[] ulbuf = new long[36];
+            ulbuf[0] = GET_ULONG_BE(input, 0);
+            ulbuf[1] = GET_ULONG_BE(input, 4);
+            ulbuf[2] = GET_ULONG_BE(input, 8);
+            ulbuf[3] = GET_ULONG_BE(input, 12);
+            while (i < 32)
+            {
+                ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]);
+                i++;
+            }
+            PUT_ULONG_BE(ulbuf[35], output, 0);
+            PUT_ULONG_BE(ulbuf[34], output, 4);
+            PUT_ULONG_BE(ulbuf[33], output, 8);
+            PUT_ULONG_BE(ulbuf[32], output, 12);
+        }
+
+        private byte[] padding(byte[] input, int mode)
+        {
+            if (input == null)
+            {
+                return null;
+            }
+
+            byte[] ret = (byte[])null;
+            if (mode == SM4_ENCRYPT)
+            {
+                int p = 16 - input.Length % 16;
+                ret = new byte[input.Length + p];
+                Array.Copy(input, 0, ret, 0, input.Length);
+                for (int i = 0; i < p; i++)
+                {
+                    ret[input.Length + i] = (byte)p;
+                }
+            }
+            else
+            {
+                int p = input[input.Length - 1];
+                ret = new byte[input.Length - p];
+                Array.Copy(input, 0, ret, 0, input.Length - p);
+            }
+            return ret;
+        }
+
+        public void sm4_setkey_enc(Sm4Context ctx, byte[] key)
+        {
+            ctx.mode = SM4_ENCRYPT;
+            sm4_setkey(ctx.sk, key);
+        }
+
+        public void sm4_setkey_dec(Sm4Context ctx, byte[] key)
+        {
+            int i = 0;
+            ctx.mode = SM4_DECRYPT;
+            sm4_setkey(ctx.sk, key);
+            for (i = 0; i < 16; i++)
+            {
+                SWAP(ctx.sk, i);
+            }
+        }
+
+        public byte[] sm4_crypt_ecb(Sm4Context ctx, byte[] input)
+        {
+            if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT))
+            {
+                input = padding(input, SM4_ENCRYPT);
+            }
+
+            int length = input.Length;
+            byte[] bins = new byte[length];
+            Array.Copy(input, 0, bins, 0, length);
+            byte[] bous = new byte[length];
+            for (int i = 0; length > 0; length -= 16, i++)
+            {
+                byte[] inBytes = new byte[16];
+                byte[] outBytes = new byte[16];
+                Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length);
+                sm4_one_round(ctx.sk, inBytes, outBytes);
+                Array.Copy(outBytes, 0, bous, i * 16, length > 16 ? 16 : length);
+            }
+
+            if (ctx.isPadding && ctx.mode == SM4_DECRYPT)
+            {
+                bous = padding(bous, SM4_DECRYPT);
+            }
+            return bous;
+        }
+
+        public byte[] sm4_crypt_cbc(Sm4Context ctx, byte[] iv, byte[] input)
+        {
+            if (ctx.isPadding && ctx.mode == SM4_ENCRYPT)
+            {
+                input = padding(input, SM4_ENCRYPT);
+            }
+
+            int i = 0;
+            int length = input.Length;
+            byte[] bins = new byte[length];
+            Array.Copy(input, 0, bins, 0, length);
+            byte[] bous = null;
+            List<byte> bousList = new List<byte>();
+            if (ctx.mode == SM4_ENCRYPT)
+            {
+                for (int j = 0; length > 0; length -= 16, j++)
+                {
+                    byte[] inBytes = new byte[16];
+                    byte[] outBytes = new byte[16];
+                    byte[] out1 = new byte[16];
+                    Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length);
+                    for (i = 0; i < 16; i++)
+                    {
+                        outBytes[i] = ((byte)(inBytes[i] ^ iv[i]));
+                    }
+                    sm4_one_round(ctx.sk, outBytes, out1);
+                    Array.Copy(out1, 0, iv, 0, 16);
+                    for (int k = 0; k < 16; k++)
+                    {
+                        bousList.Add(out1[k]);
+                    }
+                }
+            }
+            else
+            {
+                byte[] temp = new byte[16];
+                for (int j = 0; length > 0; length -= 16, j++)
+                {
+                    byte[] inBytes = new byte[16];
+                    byte[] outBytes = new byte[16];
+                    byte[] out1 = new byte[16];
+
+
+                    Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length);
+                    Array.Copy(inBytes, 0, temp, 0, 16);
+                    sm4_one_round(ctx.sk, inBytes, outBytes);
+                    for (i = 0; i < 16; i++)
+                    {
+                        out1[i] = ((byte)(outBytes[i] ^ iv[i]));
+                    }
+                    Array.Copy(temp, 0, iv, 0, 16);
+                    for (int k = 0; k < 16; k++)
+                    {
+                        bousList.Add(out1[k]);
+                    }
+                }
+            }
+
+            if (ctx.isPadding && ctx.mode == SM4_DECRYPT)
+            {
+                bous = padding(bousList.ToArray(), SM4_DECRYPT);
+                return bous;
+            }
+            else
+            {
+                return bousList.ToArray();
+            }
+        }
+    }
+
+    public class Sm4Context
+    {
+        public int mode;
+        public long[] sk;
+        public bool isPadding;
+        public Sm4Context()
+        {
+            this.mode = 1;
+            this.isPadding = true;
+            this.sk = new long[32];
+        }
+    }
+
+
+    /// <summary>
+    /// 国密SM4算法扩展
+    /// </summary>
+    public static class SM4Exten
+    {
+        /// <summary>
+        /// 加密ECB模式
+        /// </summary>
+        /// <param name="plainText">明文</param>
+        /// <param name="secretKey">密钥</param>
+        /// <param name="hexString">密钥是否是十六进制</param>
+        /// <returns>返回密文</returns>
+        public static string SM4Encrypt_ECB(this string plainText,string secretKey, bool hexString=false,bool isPadding=true)
+        {
+            Sm4Context ctx = new Sm4Context();
+            ctx.isPadding = isPadding;
+            ctx.mode = Sm4.SM4_ENCRYPT;
+            byte[] keyBytes;
+            if (hexString)
+            {
+                keyBytes = Hex.Decode(secretKey);
+            }
+            else
+            {
+                keyBytes = Encoding.Default.GetBytes(secretKey);
+            }
+            Sm4 sm4 = new Sm4();
+            sm4.sm4_setkey_enc(ctx, keyBytes);
+            byte[] encrypted = sm4.sm4_crypt_ecb(ctx, Encoding.Default.GetBytes(plainText));
+            String cipherText = Encoding.Default.GetString(Hex.Encode(encrypted));
+            return cipherText;
+        }
+
+        /// <summary>
+        /// 解密ECB模式
+        /// </summary>
+        /// <param name="secretKey">密钥</param>
+        /// <param name="hexString">密钥是否是十六进制</param>
+        /// <param name="cipherText">密文</param>
+        /// <param name="isPadding">TRUE</param>
+        /// <returns>返回明文</returns>
+        public static string SM4Decrypt_ECB(this string secretKey,  string cipherText, bool hexString=false,bool isPadding= true)
+        {
+            Sm4Context ctx = new Sm4Context();
+            ctx.isPadding = true;
+            ctx.mode = Sm4.SM4_DECRYPT;
+
+            byte[] keyBytes;
+            if (hexString)
+            {
+                keyBytes = Hex.Decode(secretKey);
+            }
+            else
+            {
+                keyBytes = Encoding.Default.GetBytes(secretKey);
+            }
+
+            Sm4 sm4 = new Sm4();
+            sm4.sm4_setkey_dec(ctx, keyBytes);
+            byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Hex.Decode(cipherText));
+            return Encoding.Default.GetString(decrypted);
+        }
+
+
+
+        /// <summary>
+        /// 加密CBC模式
+        /// </summary>
+        /// <param name="plainText">明文</param>
+        /// <param name="secretKey">密钥</param>
+        /// <param name="iv"></param>
+        /// <param name="hexString">明文是否是十六进制</param>
+        /// <returns>返回密文</returns>
+        public static string SM4Encrypt_CBC(this string plainText, string secretKey, string iv, bool hexString = false, bool isPadding = true)
+        {
+            Sm4Context ctx = new Sm4Context();
+            ctx.isPadding = true;
+            ctx.mode = Sm4.SM4_ENCRYPT;
+            byte[] keyBytes;
+            byte[] ivBytes;
+            if (hexString)
+            {
+                keyBytes = Hex.Decode(secretKey);
+                ivBytes = Hex.Decode(iv);
+            }
+            else
+            {
+                keyBytes = Encoding.Default.GetBytes(secretKey);
+                ivBytes = Encoding.Default.GetBytes(iv);
+            }
+            Sm4 sm4 = new Sm4();
+            sm4.sm4_setkey_enc(ctx, keyBytes);
+            byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Encoding.Default.GetBytes(plainText));
+            String cipherText = Encoding.Default.GetString(Hex.Encode(encrypted));
+            return cipherText;
+        }
+
+        /// <summary>
+        /// 解密CBC模式
+        /// </summary>
+        /// <param name="cipherText">密文</param>
+        /// <param name="secretKey">密钥</param>
+        /// <param name="iv"></param>
+        /// <param name="hexString">明文是否是十六进制</param>
+        /// <returns>返回明文</returns>
+        public static string SM4Decrypt_CBC(this string cipherText, string secretKey, string iv, bool hexString = false, bool isPadding = true)
+        {
+            Sm4Context ctx = new Sm4Context();
+            ctx.isPadding = true;
+            ctx.mode = Sm4.SM4_DECRYPT;
+            byte[] keyBytes;
+            byte[] ivBytes;
+            if (hexString)
+            {
+                keyBytes = Hex.Decode(secretKey);
+                ivBytes = Hex.Decode(iv);
+            }
+            else
+            {
+                keyBytes = Encoding.Default.GetBytes(secretKey);
+                ivBytes = Encoding.Default.GetBytes(iv);
+            }
+            Sm4 sm4 = new Sm4();
+            sm4.sm4_setkey_dec(ctx, keyBytes);
+            byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Hex.Decode(cipherText));
+            return Encoding.Default.GetString(decrypted);
+        }
+    }
+}

+ 84 - 20
src/MS.Services/Account/AccountService.cs

@@ -22,11 +22,14 @@ using MS.WebCore.Core;
 using Newtonsoft.Json;
 using NPOI.POIFS.Crypt.Dsig;
 using NPOI.SS.Formula.Functions;
+using StackExchange.Redis;
 using System;
 using System.Collections;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Data.SqlClient;
 using System.Linq;
+using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 
@@ -1421,7 +1424,7 @@ group by a.id,a.offline_phone_no ,b.card_no,d.invoice_balance,f.amount
         {
             try
             {
-                 DateTime now = DateTime.Now;
+                DateTime now = DateTime.Now;
                 string time = now.AddDays(7).ToString("yyyy-MM-dd 23:59:59");
                 string sixMonthsAgo = now.AddMonths(-6).ToString("yyyy-MM-dd HH:mm:ss");//半年前
                 string sixMonthsAndSevenDaysAgo = now.AddMonths(-6).AddDays(-7).ToString("yyyy-MM-dd HH:mm:ss");//半年前往前推7天
@@ -1500,7 +1503,8 @@ group by b.id,c.phone_no ,c.invoice_balance ,e.amount
                 if (userMessage.AccountType == "IC卡")
                 {
                     TemplateCode = "SMS_475590084";
-                } else  if (userMessage.AccountType == "电子账户")
+                }
+                else if (userMessage.AccountType == "电子账户")
                 {
                     TemplateCode = "SMS_475470121";
                 }
@@ -1646,22 +1650,81 @@ group by a.id,a.offline_phone_no ,b.card_no,d.invoice_balance,f.amount
             }
 
         }
-//        public async Task<List<UserIdAndName>> userIdAndName(string Buid)
-//        {
-//            var user = await _AccountingContext.GetRepository<UserIdAndName>().FromSql(
-//                        string.Format(@"SELECT CAST(ui.id AS TEXT) as id, ui.user_name as userName, ui.phone_no as phoneNo, at2.name as AccountType FROM public.user_info ui left join public.account_info ai 
-//on ui.id  = ai.user_info_id 
-//left join account_type at2  on ai.account_type_id  = at2.id  where ui.creation_date is not null and ui.business_unit_info_id IN (
-//select b.id from public.business_unit_info a left join  public.business_unit_info b on 
-//a.business_unit_info_id  = b.business_unit_info_id 
-//where a.id  = '{0}'
-//)", Buid)
-//                        )
-//                        .ToListAsync();
-//            return user;
-//        }
+        //        public async Task<List<UserIdAndName>> userIdAndName(string Buid)
+        //        {
+        //            var user = await _AccountingContext.GetRepository<UserIdAndName>().FromSql(
+        //                        string.Format(@"SELECT CAST(ui.id AS TEXT) as id, ui.user_name as userName, ui.phone_no as phoneNo, at2.name as AccountType FROM public.user_info ui left join public.account_info ai 
+        //on ui.id  = ai.user_info_id 
+        //left join account_type at2  on ai.account_type_id  = at2.id  where ui.creation_date is not null and ui.business_unit_info_id IN (
+        //select b.id from public.business_unit_info a left join  public.business_unit_info b on 
+        //a.business_unit_info_id  = b.business_unit_info_id 
+        //where a.id  = '{0}'
+        //)", Buid)
+        //                        )
+        //                        .ToListAsync();
+        //            return user;
+        //        }
+
+        /// <summary>
+        /// 添加redis
+        /// </summary>
+        /// <param name="Buid"></param>
+        /// <param name="page"></param>
+        /// <param name="pageSize"></param>
+        /// <returns></returns>
+        public async Task<List<UserIdAndName>> userIdAndNameRedis(string Buid, int page = 1, int pageSize = 1000)
+        {
+            string sql = string.Format(@"
+        SELECT 
+            CAST(ui.id AS TEXT) as id, 
+            ui.user_name as userName, 
+            ui.phone_no as phoneNo, 
+            at2.name as AccountType 
+        FROM public.user_info ui 
+        LEFT JOIN public.account_info ai ON ui.id = ai.user_info_id 
+        LEFT JOIN account_type at2 ON ai.account_type_id = at2.id 
+        WHERE ui.creation_date IS NOT NULL 
+          AND ui.business_unit_info_id IN (
+            SELECT b.id 
+            FROM public.business_unit_info a 
+            LEFT JOIN public.business_unit_info b ON a.business_unit_info_id = b.business_unit_info_id 
+            WHERE a.id = '{0}'
+          )
+        ORDER BY ui.id  
+        OFFSET {1} ROWS 
+        FETCH NEXT {2} ROWS ONLY", Buid, page, pageSize);
+
+            string buidsql = string.Format(@"select * FROM public.business_unit_info where id  = '{0}'", Buid);
+            var list = _AccountingContext.DbContext.BusinessUnitInfo
+            .FromSqlRaw(buidsql)
+              .ToList().FirstOrDefault();
+            var user = _AccountingContext.DbContext.UserIdAndNames
+      .FromSqlRaw(sql)
+        .ToList();
+            user.ForEach(_ =>
+            {
+                string json = JsonConvert.SerializeObject(_);
+                redisExample.AddToHash("UserInfo_" + list.BusinessUnitInfoId.ToString(), _.Id, json);
+            });
+            return user;
+        }
         public async Task<List<UserIdAndName>> userIdAndName(string Buid, int page = 1, int pageSize = 1000)
         {
+            List<UserIdAndName> userlist = new List<UserIdAndName>();
+            string buidsql = string.Format(@"select * FROM public.business_unit_info where id  = '{0}'", Buid);
+            var list = _AccountingContext.DbContext.BusinessUnitInfo
+          .FromSqlRaw(buidsql)
+            .ToList().FirstOrDefault();
+            var redislist = redisExample.GetAllUsersOptimized("UserInfo_" + list.BusinessUnitInfoId.ToString());
+            if (redislist != null && redislist.Count() > 0)
+            {
+                if (page != 0)
+                {
+                    return new List<UserIdAndName>();
+                }
+                return redislist;
+            }
+
             string sql = string.Format(@"
         SELECT 
             CAST(ui.id AS TEXT) as id, 
@@ -1680,22 +1743,23 @@ group by a.id,a.offline_phone_no ,b.card_no,d.invoice_balance,f.amount
           )
         ORDER BY ui.id  
         OFFSET {1} ROWS 
-        FETCH NEXT {2} ROWS ONLY",Buid,page, pageSize); 
+        FETCH NEXT {2} ROWS ONLY", Buid, page, pageSize);
 
 
             var user = _AccountingContext.DbContext.UserIdAndNames
       .FromSqlRaw(sql)
         .ToList();
-
             return user;
         }
+      
+
         /// <summary>
         /// 更新redis缓存,发券时获取用户信息
         /// </summary>
         /// <returns></returns>
         public async Task AddRedisuserIdAndName(string buid)
-        { 
-            
+        {
+
         }
         /// <summary>
         /// 获取卡片类型    4 cpu卡,1感应卡

+ 1 - 0
src/MS.Services/Account/IAccountService.cs

@@ -38,5 +38,6 @@ namespace MS.Services
         Task Upload(List<UploadTrx> uploadTrxes);
         Task SendSms();
         Task<List<UserIdAndName>> userIdAndName(string Buid, int page = 1, int pageSize = 1000);
+        Task<List<UserIdAndName>> userIdAndNameRedis(string Buid, int page = 1, int pageSize = 1000);
     }
 }

+ 51 - 6
src/MS.Services/PosTrxService/PosTrxService.cs

@@ -16,6 +16,7 @@ using MS.Models;
 using MS.Models.ViewModel;
 using MS.UnitOfWork;
 using MS.WebCore.Core;
+using Newtonsoft.Json;
 using NPOI.SS.UserModel;
 using NPOI.XSSF.UserModel;
 using System;
@@ -23,6 +24,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Runtime.Intrinsics.X86;
 using System.Runtime.Serialization.Formatters.Binary;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
@@ -322,14 +324,16 @@ WHERE
         }
         public async Task<ExecuteResult> GetTransactionStatus(string SeqNo, string NozzleId, string sn, string FuelItemFdcReleaseTokenAttribute, string FuelItemOriginalGrossAmount, DateTime? FuelItemTransactionEndTime = null)
         {
-            //  MS.WebCore.Logger.Logger.WriteLog(string.Format(@" GetTransactionStatus:SeqNo {0} NozzleId{1}
-            //  sn {2} "));
+            string endtime = FuelItemTransactionEndTime != null ? FuelItemTransactionEndTime.Value.ToString("yyyy-MM-dd hh:mm:ss") : null;
+            MS.WebCore.Logger.Logger.WriteLog(string.Format(@" GetTransactionStatus:SeqNo: {0} NozzleId: {1}
+              sn: {2} FuelItemFdcReleaseTokenAttribute: {3} FuelItemTransactionEndTime : {4}", SeqNo, NozzleId, sn, FuelItemFdcReleaseTokenAttribute,
+              endtime));
             ExecuteResult<TransactionStatusDto> result = new ExecuteResult<TransactionStatusDto>();
             string _startTime = DateTime.Now.AddHours(-1).ToString("yyyy-MM-dd HH:mm:ss");
             string where = string.Empty;
-            if (!string.IsNullOrEmpty(FuelItemFdcReleaseTokenAttribute))
+            if (!string.IsNullOrEmpty(FuelItemFdcReleaseTokenAttribute) || FuelItemFdcReleaseTokenAttribute != "0")
             {
-                where += string.Format(@" and ptli.""FuelItemFdcReleaseTokenAttribute""  = '{0}'", FuelItemFdcReleaseTokenAttribute);
+                where += string.Format(@" and  (ptli.""FuelItemFdcReleaseTokenAttribute"" is null or ptli.""FuelItemFdcReleaseTokenAttribute""  = '{0}') ", FuelItemFdcReleaseTokenAttribute);
             }
             if (!string.IsNullOrEmpty(FuelItemOriginalGrossAmount))
             {
@@ -346,8 +350,9 @@ WHERE
 
                 if (FuelItemTransactionEndTime != null && buid != null && buid.ToString() != "5914f34a-b69d-42b9-9fd4-0b8f6fba715e")
                 {
-                    string sdate = FuelItemTransactionEndTime.Value.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss");
-                    string edate = FuelItemTransactionEndTime.Value.AddSeconds(-2).ToString("yyyy-MM-dd HH:mm:ss");
+                    string sdate = FuelItemTransactionEndTime.Value.AddMinutes(5).ToString("yyyy-MM-dd HH:mm:ss");
+                    string edate = FuelItemTransactionEndTime.Value.AddMinutes(-10).ToString("yyyy-MM-dd HH:mm:ss");
+
                     where += string.Format(@" and ptli.""FuelItemTransactionEndTime"" >= '{0}' and ptli.""FuelItemTransactionEndTime"" <= '{1}'"
     , edate, sdate);
                 }
@@ -388,11 +393,51 @@ and ptli.""FuelItemOriginalGrossAmount""  > 0 {4}"
     discount = _.Key.GrossAmount - _.Key.NetAmount
 })?.ToList();
                 result.SetData(nozzles);
+                if (nozzles == null || nozzles.Count() <= 0)
+                {
+                    var gross_amount = decimal.Parse(FuelItemOriginalGrossAmount);
+                    int amount = (int)Math.Round((double)gross_amount * 100);
+                    string sdate = FuelItemTransactionEndTime.Value.AddMinutes(-3).ToString("yyyy-MM-dd HH:mm:ss");
+                    string edate = FuelItemTransactionEndTime.Value.AddMinutes(3).ToString("yyyy-MM-dd HH:mm:ss");
+                    string sql = string.Format(@"select * from offline_transaction_info a  where
+business_unit_info_id = '{5}'
+and transaction_type  = 10 
+and gross_amount  = {0}
+and nozzle_no = '{1}'
+and trx_sequence_no  = '{2}'
+and trx_end_time >= '{3}' and trx_end_time <= '{4}'
+and card_type in (1,3)", amount, NozzleId, SeqNo, sdate, edate, buid);
+                    var OfflineTransactionInfo = _AccountingContext.DbContext.OfflineTransactionInfo
+.FromSqlRaw(sql)
+.ToList();
+                    if (OfflineTransactionInfo != null && OfflineTransactionInfo.Count() == 1)
+                    {
+                        var TransactionInfo = OfflineTransactionInfo.FirstOrDefault();
+                        var anonymousObject = new
+                        {
+                            CardNo = TransactionInfo.CardNo,
+                            PayAmount = TransactionInfo.Amount,
+                            CardBalance = TransactionInfo.CardBalance,
+                            Version = "1.0"
+                        };
+
+                        string jsonString = JsonConvert.SerializeObject(anonymousObject);
+                        List<TransactionStatusDto> transactionStatusDtos = new List<TransactionStatusDto>();
+
+                        transactionStatusDtos.Add(new TransactionStatusDto() { 
+                             ResultCode = "1",
+                             ResultMessage = jsonString
+                        });
+                        result.SetData(transactionStatusDtos);
+                    }
+                }
+
             }
             catch (Exception ex)
             {
                 ExecuteResult result_ = new ExecuteResult();
                 result_.Message = ex.ToString();
+                MS.WebCore.Logger.Logger.WriteLog(string.Format(@" GetTransactionStatus Exception:{0}", ex.ToString()));
                 return result_;
             }
             return result;

+ 20 - 1
src/MS.WebApi/Controllers/AccountController.cs

@@ -176,12 +176,23 @@ namespace MS.WebApi.Controllers
         /// 短信发送
         /// </summary>
         /// <returns></returns>
-        [Route("SendSms")]
+        [Route("CardSendSms")]
         [HttpGet]
         [AllowAnonymous]
         public async Task SendSms()
         {
              await _accountService.SendSms();
+        }
+        /// <summary>
+        /// 短信发送
+        /// </summary>
+        /// <returns></returns>
+        [Route("SendSMS")]
+        [HttpPost]
+        [AllowAnonymous]
+        public async Task SendSms(IFuelSendSMSDto fuelSendSMSDto)
+        {
+            
         }
         [HttpPost("upload")]
         public async Task<IActionResult> Upload(IFormFile file)
@@ -217,5 +228,13 @@ namespace MS.WebApi.Controllers
             var data = await _accountService.userIdAndName(Buid, page, pageSize);
             return Ok(new { statusCode = "OK", message = "OK", result = data });
         }
+        [Route("userIdAndNameRedis")]
+        [HttpGet]
+        [AllowAnonymous]
+        public async Task<IActionResult> userIdAndNameRedis(string Buid, int page = 1, int pageSize = 1000)
+        {
+            var data = await _accountService.userIdAndNameRedis(Buid, page, pageSize);
+            return Ok(new { statusCode = "OK", message = "OK", result = data });
+        }
     }
 }

+ 11 - 0
src/MS.WebApi/Initialize/Startup.cs

@@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
 using MS.Component.Aop;
 using MS.Component.Jwt;
 using MS.DbContexts;
@@ -14,6 +15,7 @@ using MS.Models.Automapper;
 using MS.Services;
 using MS.UnitOfWork;
 using MS.WebApi.Filters;
+using MS.WebApi.Middlewares;
 using MS.WebApi.stockdbModels;
 using MS.WebApi.waynedbModels;
 using MS.WebCore;
@@ -21,6 +23,7 @@ using MS.WebCore.MultiLanguages;
 using Npgsql.EntityFrameworkCore.PostgreSQL;
 using System;
 using System.IO;
+using System.Net;
 using System.Text.Json;
 
 namespace MS.WebApi
@@ -137,6 +140,14 @@ namespace MS.WebApi
 
             app.UseCors(WebCoreExtensions.MyAllowSpecificOrigins);//添加跨域
 
+            var loggerFactory = LoggerFactory.Create(builder =>
+            {
+                builder.AddConsole();
+            });
+            var logger = loggerFactory.CreateLogger<SignatureValidator>();
+            var signatureValidator = new SignatureValidator(logger);
+            app.UseMiddleware<SignatureValidationMiddleware>(signatureValidator);//签名验证
+
             app.UseAuthentication();//添加认证中间件
 
             app.UseAuthorization();

+ 1 - 1
src/MS.WebApi/MS.WebApi.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>

+ 63 - 0
src/MS.WebApi/Middlewares/SignatureValidationMiddleware.cs

@@ -0,0 +1,63 @@
+using Microsoft.AspNetCore.Http;
+using System.Net;
+using System.Threading.Tasks;
+using System;
+
+namespace MS.WebApi.Middlewares
+{
+    public class SignatureValidationMiddleware
+    {
+        private readonly RequestDelegate _next;
+        private readonly SignatureValidator _validator;
+
+        public SignatureValidationMiddleware(RequestDelegate next, SignatureValidator validator)
+        {
+            _next = next;
+            _validator = validator;
+        }
+
+        /// <summary>
+        /// 处理每个 HTTP 请求的异步方法。
+        /// </summary>
+        /// <param name="context">HTTP 上下文。</param>
+        /// <returns>一个任务表示的操作。</returns>
+        public async Task InvokeAsync(HttpContext context)
+        {
+            // 跳过登录接口
+            var path = context.Request.Path.Value;
+            if (!path.StartsWith("/api/Account/SendSMS", StringComparison.OrdinalIgnoreCase))
+            {
+                await _next(context);
+                return;
+            }
+            var headers = context.Request.Headers;
+
+            //var apply = _authorization.GetApply(SecretId);
+            //if (apply == null)
+            //{
+            //    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+            //    await context.Response.WriteAsync("无效的签名,未找到SecretId");
+            //    return;
+            //}
+            var ValidateSignature = await _validator.ValidateSignature(context.Request, "tWWGfad0WMZxQy7i5ZNz64HB678JiYC2");
+            if (ValidateSignature)
+            {
+                await _next(context);
+                return;
+            }
+            //await _next(context);
+            //return;
+
+            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+            await context.Response.WriteAsync("无效的签名");
+            return;
+
+
+            await _next(context);
+        }
+    }
+    public class AdApply
+    { 
+         public string AppSecret { get; set; }
+    }
+}

+ 211 - 0
src/MS.WebApi/Middlewares/SignatureValidator.cs

@@ -0,0 +1,211 @@
+using Microsoft.Extensions.Logging;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using MS.Models;
+using Microsoft.AspNetCore.Http;
+
+namespace MS.WebApi.Middlewares
+{
+    public class SignatureValidator
+    {
+        private readonly ILogger<SignatureValidator> _logger;
+
+        public SignatureValidator(ILogger<SignatureValidator> logger)
+        {
+            _logger = logger;
+        }
+
+        /// <summary>
+        /// 验证传入请求的签名是否有效。
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        public async Task<bool> ValidateSignature(HttpRequest request, string Secret)
+        {
+            try
+            {
+                var signMethod = request.Headers["sign_method"].ToString();
+                var secretId = request.Headers["secret_id"].ToString();
+                var nonce = request.Headers["nonce"].ToString();
+                var timestamp = request.Headers["timestamp"].ToString();
+                var signature = request.Headers["signature"].ToString();//签名串
+                string bodyContent = string.Empty;
+                if (request.Body != null)
+                {
+                    // 将 Body 流读取为字符串
+                    request.EnableBuffering(); // 允许多次读取 Body
+                    using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
+                    {
+                        bodyContent = await reader.ReadToEndAsync();
+                    }
+                    request.Body.Position = 0; // 重置流的位置以便后续读取
+                }
+                if (string.IsNullOrEmpty(signMethod) || string.IsNullOrEmpty(secretId) ||
+                    string.IsNullOrEmpty(nonce) || string.IsNullOrEmpty(timestamp) ||
+                    string.IsNullOrEmpty(signature))
+                {
+                    _logger.LogError("缺少签名信息");
+                    return false;
+                }
+
+                // 查询字符串
+                //var queryString = HttpUtility.ParseQueryString(request.QueryString.ToString());
+                //var queryParameters = queryString.AllKeys
+                //    .OrderBy(k => k)
+                //    .Select(k => $"{k}={queryString[k]}")
+                //    .Aggregate((current, next) => current + "&" + next);
+
+                // 待签字符串  
+                var stringToSign = $"sign_method={signMethod}&secret_id={secretId}&nonce={nonce}&timestamp={timestamp}";
+                if (!string.IsNullOrEmpty(bodyContent))
+                {
+                    stringToSign += "&" + bodyContent;
+                }
+                if (request.QueryString.Value != "")
+                {
+                    var querystring = request.QueryString.Value.Substring(1);
+                    stringToSign += "&" + querystring;
+                }
+                //var sfd =  Sm4Encryptor.Encrypt(stringToSign, Secret);
+                //stringToSign = Sm4Encryptor.Encrypt(stringToSign, Secret);
+                stringToSign = stringToSign.SM4Encrypt_ECB(Secret);
+                // 根据 RFC3986 对字符串进行 URL 编码
+                var encodedString = Uri.EscapeDataString(stringToSign);
+
+                string base64 = StringToBase64(encodedString);
+                return base64.Equals(signature, StringComparison.Ordinal);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "签名验证过程中发生错误。");
+                return false;
+            }
+        }
+
+        public static string StringToBase64(string input)
+        {
+            if (string.IsNullOrEmpty(input))
+                return string.Empty;
+
+            // 将字符串转换为字节数组
+            byte[] bytes = Encoding.UTF8.GetBytes(input);
+
+            // 将字节数组转换为 Base64 字符串
+            return Convert.ToBase64String(bytes);
+        }
+
+        public string Encrypt(string plainText)
+        {
+            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+
+            // 加密
+            // byte[] encryptedBytes = sm4.EncryptECB(plainTextBytes);
+
+            // 这里我们模拟加密过程,直接返回原始数据(你需要替换为实际的加密代码)
+            byte[] encryptedBytes = plainTextBytes; // 假设加密后数据不变,仅作示例
+
+            // 返回Base64编码的加密字符串
+            return Convert.ToBase64String(encryptedBytes);
+        }
+
+
+        public string SM4Encrypt_ECB(string plainText, string secretKey, bool hexString = false)
+        {
+            Sm4Context sm4Context = new Sm4Context();
+            sm4Context.isPadding = true;
+            sm4Context.mode = 1;
+            byte[] key = (!hexString) ? Encoding.Default.GetBytes(secretKey) : Decode(secretKey);
+            Sm4 sm = new Sm4();
+            sm.sm4_setkey_enc(sm4Context, key);
+            byte[] data = sm.sm4_crypt_ecb(sm4Context, Encoding.Default.GetBytes(plainText));
+            return Encoding.Default.GetString(Encode(data, 0, data.Length));
+        }
+        public byte[] Decode(string hexString)
+        {
+            // 确保输入字符串长度为偶数
+            if (hexString.Length % 2 != 0)
+            {
+                throw new ArgumentException("Hexadecimal string must have an even length", nameof(hexString));
+            }
+
+            // 创建一个输出流来存储解码后的字节数据
+            using (MemoryStream outputStream = new MemoryStream(hexString.Length / 2))
+            {
+                for (int i = 0; i < hexString.Length; i += 2)
+                {
+                    // 获取两个字符并将其转换为字节
+                    char highNibbleChar = hexString[i];
+                    char lowNibbleChar = hexString[i + 1];
+
+                    byte highNibble = CharToByte(highNibbleChar);
+                    byte lowNibble = CharToByte(lowNibbleChar);
+
+                    if (highNibble == 0xFF || lowNibble == 0xFF)
+                    {
+                        throw new ArgumentException("Invalid hexadecimal character encountered.", nameof(hexString));
+                    }
+
+                    // 合并两个半字节为一个完整的字节
+                    byte decodedByte = (byte)((highNibble << 4) | lowNibble);
+
+                    // 将解码后的字节写入输出流
+                    outputStream.WriteByte(decodedByte);
+                }
+
+                return outputStream.ToArray();
+            }
+        }
+
+        // 私有辅助方法用于将字符转换为对应的数值
+        private byte CharToByte(char c)
+        {
+            if (c >= '0' && c <= '9')
+                return (byte)(c - '0');
+            if (c >= 'A' && c <= 'F')
+                return (byte)(c - 'A' + 10);
+            if (c >= 'a' && c <= 'f')
+                return (byte)(c - 'a' + 10);
+
+            // 如果字符不是有效的十六进制字符,则返回0xFF作为错误标志
+            return 0xFF;
+        }
+        // 实例方法用于编码字节数组到十六进制字符串
+        public byte[] Encode(byte[] data, int offset, int length)
+        {
+            if (data == null)
+                throw new ArgumentNullException(nameof(data), "Input data cannot be null.");
+            if (offset < 0 || offset >= data.Length)
+                throw new ArgumentOutOfRangeException(nameof(offset), "Offset is out of range.");
+            if (length <= 0 || offset + length > data.Length)
+                throw new ArgumentOutOfRangeException(nameof(length), "Length is out of range.");
+
+            // 创建一个输出流来存储编码后的十六进制字符串
+            using (MemoryStream outputStream = new MemoryStream(length * 2))
+            {
+                for (int i = offset; i < offset + length; i++)
+                {
+                    byte b = data[i];
+                    // 将每个字节转换为两个十六进制字符
+                    byte highNibble = (byte)((b >> 4) & 0xF);
+                    byte lowNibble = (byte)(b & 0xF);
+
+                    outputStream.WriteByte(ConvertToHexChar(highNibble));
+                    outputStream.WriteByte(ConvertToHexChar(lowNibble));
+                }
+
+                return outputStream.ToArray();
+            }
+        }
+
+        // 私有辅助方法用于将半字节转换为对应的十六进制字符
+        private byte ConvertToHexChar(byte nibble)
+        {
+            if (nibble < 10)
+                return (byte)('0' + nibble); // 数字 0-9
+            else
+                return (byte)('a' - 10 + nibble); // 字母 a-f
+        }
+    }
+}

+ 72 - 1
src/MS.WebCore/Core/RedisExample.cs

@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc.Formatters;
+using Microsoft.AspNetCore.DataProtection.KeyManagement;
+using Microsoft.AspNetCore.Mvc.Formatters;
 using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
@@ -6,6 +7,11 @@ using System.Linq;
 using System.Net;
 using System.Reflection;
 using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MS.Entities.AccountingService_Models;
+using System.Collections.Concurrent;
+using Newtonsoft.Json;
 
 namespace MS.WebCore.Core
 {
@@ -189,5 +195,70 @@ namespace MS.WebCore.Core
             // 注意:HashSet方法返回bool值,表示操作是否成功  
             return redisDb.HashSet(hashKey, fieldKey, value);
         }
+        public Dictionary<string, string> GetHashAll(string key)
+        {
+            HashEntry[] entries = redisDb.HashGetAll(key);
+            var result = new Dictionary<string, string>();
+            foreach (var entry in entries)
+            {
+                result.Add(entry.Name.ToString(), entry.Value.ToString());
+            }
+            return result;
+        }
+        /// <summary>
+        /// 根据key获取hash列表所有数据
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public List<UserIdAndName> GetAllUsersOptimized(string key)
+        {
+            var allEntries = redisDb.HashGetAll(key);
+
+            var result = new List<UserIdAndName>(allEntries.Length);
+
+            var tempList = new UserIdAndName[allEntries.Length];
+            int count = 0;
+
+            for (int i = 0; i < allEntries.Length; i++)
+            {
+                if (!allEntries[i].Value.IsNullOrEmpty)
+                {
+                    var user = ParseUser(allEntries[i].Value.ToString());
+                    if (user != null)
+                    {
+                        tempList[count] = user;
+                        count++;
+                    }
+                }
+            }
+
+            result.AddRange(tempList.Take(count));
+            return result;
+        }
+        public List<UserIdAndName> GetUsersSimplePagedParallel(string key)
+        {
+            var allEntries = redisDb.HashGetAll(key);
+            var result = new ConcurrentBag<UserIdAndName>();
+
+            Parallel.ForEach(allEntries, entry =>
+            {
+                if (!entry.Value.IsNullOrEmpty)
+                {
+                    var user = ParseUser(entry.Value.ToString());
+                    if (user != null) result.Add(user);
+                }
+            });
+
+            return new List<UserIdAndName>(result);
+        }
+
+        private UserIdAndName ParseUser(string input)
+        {
+            if (string.IsNullOrEmpty(input)) return null;
+
+            UserIdAndName rootObject = JsonConvert.DeserializeObject<UserIdAndName>(input);
+
+            return rootObject;
+        }
     }
 }

+ 1 - 0
src/MS.WebCore/MS.WebCore.csproj

@@ -11,6 +11,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\MS.Common\MS.Common.csproj" />
+    <ProjectReference Include="..\MS.Entities\MS.Entities.csproj" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/QuartzSettings/task_job.json

@@ -1 +1 @@
-[{"TaskName":"永利加油站","GroupName":"\t 永利加油站","Interval":"* 0/24 * * * ? ","ApiUrl":"http://tkhs.net.cn:8721/api/Account/addredis?buid=c75b2e74-d51e-42ae-bc89-2d39312c9c30","Describe":null,"LastRunTime":null,"Status":4,"TaskType":2,"ApiRequestType":"GET","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":3,"timeflag":null,"changetime":null},{"TaskName":"测试","GroupName":"测试","Interval":"1-2 0/30 * * * ? ","ApiUrl":"http://localhost:8721/api/membershipGroup/UpdateAsync","Describe":null,"LastRunTime":"2023-05-30T11:00:50","Status":4,"TaskType":2,"ApiRequestType":"POST","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":1,"timeflag":null,"changetime":null},{"TaskName":"111","GroupName":"111","Interval":"0 0 0/1 * * ? ","ApiUrl":"http://localhost:8721/api/Account/AddEmployeeRedisAsync?buid=c75b2e74-d51e-42ae-bc89-2d39312c9c30&date=2020-09-01&Car=0","Describe":null,"LastRunTime":"2024-04-28T01:56:19","Status":4,"TaskType":2,"ApiRequestType":"GET","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":2,"timeflag":null,"changetime":null}]
+[{"TaskName":"测试","GroupName":"测试","Interval":"1-2 0/30 * * * ? ","ApiUrl":"http://localhost:8721/api/membershipGroup/UpdateAsync","Describe":null,"LastRunTime":"2023-05-30T11:00:50","Status":4,"TaskType":2,"ApiRequestType":"POST","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":1,"timeflag":null,"changetime":null},{"TaskName":"111","GroupName":"111","Interval":"0 0 0/1 * * ? ","ApiUrl":"http://localhost:8721/api/Account/AddEmployeeRedisAsync?buid=c75b2e74-d51e-42ae-bc89-2d39312c9c30&date=2020-09-01&Car=0","Describe":null,"LastRunTime":"2024-04-28T01:56:19","Status":4,"TaskType":2,"ApiRequestType":"GET","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":2,"timeflag":null,"changetime":null},{"TaskName":"永利加油站","GroupName":"\t 永利加油站","Interval":"* 0/24 * * * ? ","ApiUrl":"http://tkhs.net.cn:8721/api/Account/addredis?buid=c75b2e74-d51e-42ae-bc89-2d39312c9c30","Describe":null,"LastRunTime":null,"Status":4,"TaskType":2,"ApiRequestType":"GET","ApiAuthKey":null,"ApiAuthValue":null,"ApiParameter":null,"DllClassName":null,"DllActionName":null,"id":3,"timeflag":null,"changetime":null}]