From 248ac1fe07f96c65b7864a7ddfe05d2159a8639b Mon Sep 17 00:00:00 2001 From: zpc Date: Sat, 16 Nov 2024 03:44:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=9E=E5=90=8D=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +- .../Controllers/AccountController.cs | 12 + .../CloudGaming.Code/Account/AccountBLL.cs | 299 +++++++++++++----- .../CloudGaming.Code/Account/AccountExtend.cs | 153 +++++++++ .../Aliyun/AlibabaIdCardVerify.cs | 105 ++++++ .../AppExtend/CloudGamingBase.cs | 59 +++- .../CloudGaming.Code/CloudGaming.Code.csproj | 1 + .../Contract/CloudGamingExtend.cs | 38 +++ .../Contract/IIdCardVerify.cs | 22 ++ .../IPhoneNumberVerificationService.cs | 2 +- .../AlibabaPhoneNumberVerificationService.cs | 16 +- .../Account/User/UserActionTypeEnum.cs | 35 ++ .../Account/User/UserInfoCache.cs | 29 ++ .../Account/User/UserInfoDto.cs | 2 +- .../Account/UserRealAuthenticationRequest.cs | 28 ++ .../Db/Db_Ext/CloudGamingCBTContext.cs | 44 +++ .../Db_Ext/T_User_RealAuthentication_Log.cs | 56 ++++ .../Db_User/CloudGamingUserContext.cs | 43 +++ .../DbSqlServer/Db_User/T_User_Data.cs | 5 + .../Db_User/T_User_LimitActionLog.cs | 56 ++++ .../Utility/DateTimeExtensions.cs | 2 +- .../Utility/OtherExtensions.cs | 133 ++++++++ 22 files changed, 1054 insertions(+), 98 deletions(-) create mode 100644 src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlibabaIdCardVerify.cs create mode 100644 src/CloudGaming/Code/CloudGaming.Code/Contract/CloudGamingExtend.cs create mode 100644 src/CloudGaming/Code/CloudGaming.Code/Contract/IIdCardVerify.cs rename src/CloudGaming/Code/CloudGaming.Code/{Sms => }/Contract/IPhoneNumberVerificationService.cs (93%) create mode 100644 src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserActionTypeEnum.cs create mode 100644 src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs create mode 100644 src/CloudGaming/Model/CloudGaming.DtoModel/Account/UserRealAuthenticationRequest.cs create mode 100644 src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_User_RealAuthentication_Log.cs create mode 100644 src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_LimitActionLog.cs create mode 100644 src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/OtherExtensions.cs diff --git a/README.md b/README.md index d653fc5..254597d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ # CloudGaming -云游戏 \ No newline at end of file +云游戏 + +# CloudGaming + +云游戏 + +## 发布 +cd /disk/CodeManage/CloudGaming/src/CloudGaming +docker build -t cloudgaming:dev-0.0.4 -f Api/CloudGaming.Api/Dockerfile . + +docker run -d -p 81:80 cloudgaming:dev-0.0.4 \ No newline at end of file diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs index f151cfa..e7d5621 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs @@ -68,4 +68,16 @@ public class AccountController : CloudGamingControllerBase AccountBLL account = new AccountBLL(ServiceProvider); return await account.GetUserInfo(); } + + /// + /// 获取用户信息 + /// + /// + [HttpPost] + [Authorize] + public async Task> RealAuthentication([FromBody] UserRealAuthenticationRequest authenticationRequest) + { + AccountBLL account = new AccountBLL(ServiceProvider); + return await account.RealAuthentication(authenticationRequest.UserName, authenticationRequest.IdCard, authenticationRequest.DeviceNumber); + } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs index 39fd2b1..1724ace 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs @@ -1,6 +1,7 @@ using Alipay.EasySDK.Kernel; using CloudGaming.Code.Account.Contract; +using CloudGaming.Code.Contract; using CloudGaming.Code.Sms; using CloudGaming.DtoModel.Account; using CloudGaming.DtoModel.Account.Login; @@ -15,6 +16,8 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Org.BouncyCastle.Utilities.Encoders; + using System; using System.Collections.Generic; using System.Linq; @@ -130,9 +133,9 @@ namespace CloudGaming.Code.Account userCurrency = new List(); } var userCurrencyDic = userCurrency.ToDictionary(it => (UserCurrencyType)it.CurrencyType); - var userInfoKey = GetUserInfoRedisKey(user.Id); - var userInfo = LoadUserInfo(user, userData, userCurrencyDic); - await RedisCache.StringSetAsync(userInfoKey, userInfo, TimeSpan.FromHours(1)); + //创建用户缓存 + await AccountExtend.LogUserInfoCahceAsync(RedisCache, user, userData, userCurrencyDic); + //创建jwt登录 var jwt = GenerateJwtToken(user); var accountLogIn = new AccountLogInResponse @@ -251,7 +254,8 @@ namespace CloudGaming.Code.Account UpdateAt = DateTime.Now, PhoneNum = account.GetUserDataProperty(UserDataPropertyEnum.PhoneNum), UserId = userId, - Email = account.GetUserDataProperty(UserDataPropertyEnum.Email) + Email = account.GetUserDataProperty(UserDataPropertyEnum.Email), + DeviceNumber = account.DeviceNumber }; await Dao.DaoUser.Context.T_User_Data.AddAsync(userData); await Dao.DaoUser.Context.SaveChangesAsync(); @@ -362,12 +366,8 @@ namespace CloudGaming.Code.Account #endregion - public string GetUserInfoRedisKey(int userId) - { - return $"user:userInfo:{userId}"; - } /// - /// + /// 获取用户信息 /// /// public async Task GetUserInfo() @@ -377,81 +377,230 @@ namespace CloudGaming.Code.Account { throw MessageBox.Show(ResonseCode.NotFoundRecord, "未找到用户"); } - string key = GetUserInfoRedisKey(userId); - var userInfo = await RedisCache.StringGetAsync(key); - if (userInfo == null) - { - userInfo = new UserInfo() { }; - //用户信息 - var user = await Dao.DaoUser.Context.T_User.FirstOrDefaultAsync(it => it.Id == userId); - //用户扩展信息 - var userData = await Dao.DaoUser.Context.T_User_Data.FirstOrDefaultAsync(it => it.UserId == userId); - //用户货币 - var userCurrency = await Dao.DaoUser.Context.T_User_Currency.Where(it => it.UserId == userId).ToDictionaryAsync(it => (UserCurrencyType)it.CurrencyType); - if (user == null) - { - throw MessageBox.Show(ResonseCode.NotFoundRecord, "未找到用户信息"); - } - if (userData == null) - { - throw MessageBox.Show(ResonseCode.NotFoundRecord, "未找到用户扩展信息"); - } - - userInfo = LoadUserInfo(user, userData, userCurrency); - await RedisCache.StringSetAsync(key, userInfo, TimeSpan.FromHours(1)); - } + UserInfoCache? userInfo = await GetUserInfoCache(); UserInfoDto userInfoDto = Mapper.Map(userInfo); + if (!string.IsNullOrEmpty(userInfoDto.UserName)) + { + userInfoDto.UserName = userInfoDto.UserName.Length <= 2 + ? userInfoDto.UserName.Substring(0, 1) + "*" + : userInfoDto.UserName.Substring(0, 1) + "*" + userInfoDto.UserName.Substring(userInfoDto.UserName.Length - 1); + } + // 将用户身份证号设置成 "前四位*后四位" + if (!string.IsNullOrEmpty(userInfoDto.IdCard) && userInfoDto.IdCard.Length >= 8) + { + userInfo.IdCard = userInfoDto.IdCard.Substring(0, 4) + "*********" + userInfoDto.IdCard.Substring(userInfoDto.IdCard.Length - 4); + } return userInfoDto; } - /// - /// 加载用户缓存 - /// - /// - /// - /// - /// - /// - private UserInfo LoadUserInfo(T_User? user, T_User_Data? userData, Dictionary userCurrency) - { - var userInfo = new UserInfo() { }; - userInfo.NickName = user.NickName; - userInfo.UserId = user.Id; - userInfo.UserIcon = user.UserIconUrl; - userInfo.IsRealName = user.UserRealNameStatus > 0; - userInfo.IsJuveniles = false; - userInfo.UserName = ""; - userInfo.IdCard = ""; - if (userInfo.IsRealName) - { - userInfo.IsJuveniles = user.UserRealNameStatus == 2; - if (!string.IsNullOrEmpty(user.UserName)) - { - // 将用户昵称设置成 "陈*风" 或 "陈*"(如果只有两个字符) - userInfo.UserName = user.UserName.Length <= 2 - ? user.UserName.Substring(0, 1) + "*" - : user.UserName.Substring(0, 1) + "*" + user.UserName.Substring(user.UserName.Length - 1); - } - // 将用户身份证号设置成 "前四位*后四位" - if (!string.IsNullOrEmpty(user.IDCard) && user.IDCard.Length >= 8) - { - userInfo.IdCard = user.IDCard.Substring(0, 4) + "*********" + user.IDCard.Substring(user.IDCard.Length - 4); - } - } - userInfo.TotalGamingTime = 0; - userInfo.PhoneNum = userData?.PhoneNum ?? ""; - userInfo.Email = userData?.Email ?? ""; - userInfo.Diamond = (int)userCurrency.GetUserCurrency(UserCurrencyType.钻石); - return userInfo; - } - + #region 实名认证 /// /// 实名认证 /// /// - public async Task RealAuthentication(string userName, string idCard) + public async Task> RealAuthentication(string userName, string idCard, string deviceNumber) { - return true; + // 参数校验 + ValidateParameters(userName, idCard); + + // 检查用户信息和状态 + CheckUserRealNameStatus(userName, idCard); + + // 获取用户 + var user = await Dao.DaoUser.Context.T_User.FirstOrDefaultAsync(it => it.Id == _UserId); + if (user == null) + { + throw CreateException(ResonseCode.NotFoundRecord, "用户不存在"); + } + + // 实名认证接口验证 + var idCardVerify = this.GetIdCardVerify(); + var (isv, msg) = await idCardVerify.IdCardVerify(userName, idCard); + if (!isv) + { + await LogRealAuthenticationFailure(userName, idCard, deviceNumber, msg); + throw CreateException(ResonseCode.NotFoundRecord, "身份证号错误"); + } + + // 更新用户实名认证状态 + UpdateUserRealNameStatus(user, userName, idCard); + + // 发放实名认证奖励 + var rewardMessage = await GrantRewardsIfEligible(deviceNumber); + + // 保存日志 + await LogRealAuthenticationSuccess(userName, idCard, deviceNumber, rewardMessage); + + // 更新缓存 + await UpdateUserCache(userName, idCard); + + return new BaseResponse(0, $"认证成功!{rewardMessage}") { }; ; } + + /// + /// 校验参数 + /// + private void ValidateParameters(string userName, string idCard) + { + if (string.IsNullOrEmpty(userName)) + { + throw CreateException(ResonseCode.ParamError, "用户名为空"); + } + if (string.IsNullOrEmpty(idCard) || !OtherExtensions.ValidateIDCard(idCard)) + { + throw CreateException(ResonseCode.ParamError, "身份证号错误"); + } + + } + + /// + /// 检查用户实名认证状态 + /// + private void CheckUserRealNameStatus(string userName, string idCard) + { + //判断用户是否实名认证过,并且不等于未成年人 + if (UserInfo.IsRealName && !UserInfo.IsJuveniles) + { + throw MessageBox.Show(0, "此账号已实名认证成功,无需重复实名"); + } + if (UserInfo.RealNameDateTimeLast != null && DateTime.Now.Subtract((UserInfo.RealNameDateTimeLast ?? DateTime.MinValue)).TotalSeconds < 10) + { + throw MessageBox.Show(ResonseCode.Error, "两次绑定身份证号时间间隔太短,请稍后再绑定~"); + } + if (UserInfo.IdCard == idCard && UserInfo.UserName == userName) + { + throw MessageBox.Show(0, "此账号已实名认证成功,无需重复验证"); + } + if (UserInfo.RealAuthentCount > 10) + { + throw MessageBox.Show(0, "此账号今日实名认证次数已到达上限"); + } + } + + /// + /// 更新用户实名认证状态 + /// + private void UpdateUserRealNameStatus(T_User user, string userName, string idCard) + { + var (isValid, age, _, _) = OtherExtensions.GetIDCardBirthdayAgeSex(idCard); + user.UserName = userName; + user.IDCard = idCard; + + if (isValid) + { + UserInfo.IsJuveniles = age < 18; + user.UserRealNameStatus = UserInfo.IsJuveniles ? 2 : 1; + } + + UserInfo.IsRealName = true; + UserInfo.RealNameDateTimeLast = DateTime.Now; + UserInfo.RealAuthentCount++; + } + + /// + /// 发放实名认证奖励 + /// + private async Task GrantRewardsIfEligible(string deviceNumber) + { + if (string.IsNullOrEmpty(deviceNumber)) + { + deviceNumber = UserInfo.DeviceNumber; + } + + var hasUserReward = await Dao.DaoUser.Context.T_User_LimitActionLog + .AnyAsync(it => it.UserId == _UserId && it.ActionType == (int)UserActionTypeEnum.ActionType.实名认证); + + if (hasUserReward) + { + return "用户已经领取过奖励,不再发放奖励"; + } + + var hasDeviceReward = await Dao.DaoUser.Context.T_User_LimitActionLog + .AnyAsync(it => it.DeviceNumber == deviceNumber && it.ActionType == (int)UserActionTypeEnum.ActionType.实名认证); + + if (!hasDeviceReward) + { + var limitActionLog = new T_User_LimitActionLog + { + CreateTime = DateTime.Now, + CreateTimeDay = int.Parse(DateTime.Now.ToString("yyyyMMdd")), + ActionType = (int)UserActionTypeEnum.ActionType.实名认证, + DeviceNumber = deviceNumber, + UserId = _UserId, + Ip = HttpContextAccessor.HttpContext.GetClientIpAddress() + }; + + await Dao.DaoUser.Context.T_User_LimitActionLog.AddAsync(limitActionLog); + await Dao.DaoUser.Context.SaveChangesAsync(); + return "奖励已经发送,钻石*10"; + } + + return "用户设备号重复,不发放奖励"; + } + + /// + /// 记录实名认证失败日志 + /// + private async Task LogRealAuthenticationFailure(string userName, string idCard, string deviceNumber, string msg) + { + if (msg.Length > 100) + { + msg = msg.Substring(0, 100); + } + var log = new T_User_RealAuthentication_Log + { + CreateTime = DateTime.Now, + DeviceNumber = deviceNumber, + IdCard = idCard, + UserId = _UserId, + UserName = userName, + IsRewards = false, + RealResults = msg, + Message = "认证失败" + }; + + await Dao.DaoExt.Context.T_User_RealAuthentication_Log.AddAsync(log); + await Dao.DaoExt.Context.SaveChangesAsync(); + UserInfo.RealNameDateTimeLast = DateTime.Now; + UserInfo.RealAuthentCount++; + await this.SaveUserInfoCacheChangesAsync(); + } + + /// + /// 记录实名认证成功日志 + /// + private async Task LogRealAuthenticationSuccess(string userName, string idCard, string deviceNumber, string message) + { + var log = new T_User_RealAuthentication_Log + { + CreateTime = DateTime.Now, + DeviceNumber = deviceNumber, + IdCard = idCard, + UserId = _UserId, + UserName = userName, + IsRewards = message.Contains("奖励已经发送"), + RealResults = "认证成功", + Message = message + }; + + await Dao.DaoExt.Context.T_User_RealAuthentication_Log.AddAsync(log); + await Dao.DaoExt.Context.SaveChangesAsync(); + } + + /// + /// 更新用户缓存 + /// + private async Task UpdateUserCache(string userName, string idCard) + { + UserInfo.UserName = userName; + UserInfo.IdCard = idCard; + UserInfo.IsRealName = true; + UserInfo.RealNameDateTimeLast = DateTime.Now; + UserInfo.RealAuthentCount++; + await this.SaveUserInfoCacheChangesAsync(); + } + + #endregion + } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs index 8eb425b..3745fa1 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs @@ -1,11 +1,18 @@ using CloudGaming.Code.Account.Contract; using CloudGaming.Code.Account.Login; +using CloudGaming.Code.DataAccess; using CloudGaming.DtoModel.Account.Login; using CloudGaming.DtoModel.Account.User; +using HuanMeng.DotNetCore.Redis; + using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using StackExchange.Redis; + +using Swashbuckle.AspNetCore.SwaggerGen; + using System; using System.Collections.Generic; using System.Linq; @@ -66,5 +73,151 @@ namespace CloudGaming.Code.Account } return 0; } + + /// + /// 用户缓存的key + /// + /// + /// + public static string GetUserInfoRedisKey(int userId) + { + return $"user:userInfo:{userId}"; + } + + /// + /// + /// + /// + /// + /// + /// + public static async Task GetUserInfo(this UserInfoCache userInfoCache, int userId, CloudGamingBase cloudGamingBase) + { + userInfoCache = await GetUserInfo(userId, cloudGamingBase) ?? new UserInfoCache(); + return userInfoCache; + } + /// + /// 获取用户信息 + /// + /// + /// + /// + public static async Task GetUserInfo(int userId, CloudGamingBase cloudGamingBase) + { + if (userId == 0) + { + return new UserInfoCache(); + } + string key = GetUserInfoRedisKey(userId); + var userInfo = await cloudGamingBase.RedisCache.StringGetAsync(key); + if (userInfo == null) + { + var Dao = cloudGamingBase.Dao; + userInfo = new UserInfoCache() { }; + //用户信息 + var user = await Dao.DaoUser.Context.T_User.FirstOrDefaultAsync(it => it.Id == userId); + //用户扩展信息 + var userData = await Dao.DaoUser.Context.T_User_Data.FirstOrDefaultAsync(it => it.UserId == userId); + //用户货币 + var userCurrency = await Dao.DaoUser.Context.T_User_Currency.Where(it => it.UserId == userId).ToDictionaryAsync(it => (UserCurrencyType)it.CurrencyType); + if (user == null) + { + throw MessageBox.Show(ResonseCode.NotFoundRecord, "未找到用户信息"); + } + if (userData == null) + { + throw MessageBox.Show(ResonseCode.NotFoundRecord, "未找到用户扩展信息"); + } + + userInfo.LoadUserInfo(user, userData, userCurrency); + await cloudGamingBase.RedisCache.StringSetAsync(key, userInfo, TimeSpan.FromHours(1)); + } + + return userInfo; + } + + /// + /// 设置用户信息 + /// + /// + /// + /// + /// + /// + public static async Task LogUserInfoCahceAsync(IDatabase database, T_User user, T_User_Data? userData = null, Dictionary userCurrency = null) + { + var key = GetUserInfoRedisKey(user.Id); + var userInfo = new UserInfoCache() { }; + LoadUserInfo(userInfo, user, userData, userCurrency); + await database.StringSetAsync(key, userInfo, TimeSpan.FromHours(1)); + return userInfo; + } + + /// + /// 加载用户缓存 + /// + /// + /// + /// + /// + /// + public static UserInfoCache LoadUserInfo(this UserInfoCache userInfo, T_User? user = null, T_User_Data? userData = null, Dictionary userCurrency = null) + { + if (userInfo == null) + { + userInfo = new UserInfoCache(); + } + if (user != null) + { + userInfo.NickName = user.NickName; + userInfo.UserId = user.Id; + userInfo.UserIcon = user.UserIconUrl; + userInfo.IsRealName = user.UserRealNameStatus > 0; + userInfo.IsJuveniles = user.UserRealNameStatus == 2; + userInfo.UserName = user.UserName ?? ""; + userInfo.IdCard = user.IDCard ?? ""; + + } + if (userData != null) + { + userInfo.PhoneNum = userData?.PhoneNum ?? ""; + userInfo.Email = userData?.Email ?? ""; + userInfo.DeviceNumber = userData?.DeviceNumber ?? ""; + } + userInfo.TotalGamingTime = 0; + if (userCurrency != null) + { + userInfo.Diamond = (int)userCurrency.GetUserCurrency(UserCurrencyType.钻石); + } + return userInfo; + } + + /// + /// 加载用户缓存 + /// + /// + /// + /// + /// + /// + public static UserInfoCache LoadUserInfo(T_User? user = null, T_User_Data? userData = null, Dictionary userCurrency = null) + { + var userInfo = new UserInfoCache(); + return LoadUserInfo(userInfo, user, userData, userCurrency); + } + + + /// + /// 保存用户缓存 + /// + /// + /// + public static async Task SaveUserInfoCacheChangesAsync(this CloudGamingBase cloudGamingBase) + { + var userInfo = cloudGamingBase.UserInfo; + var key = GetUserInfoRedisKey(cloudGamingBase.UserInfo.UserId); + await cloudGamingBase.RedisCache.StringSetAsync(key, userInfo, TimeSpan.FromHours(1)); + return userInfo; + } } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlibabaIdCardVerify.cs b/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlibabaIdCardVerify.cs new file mode 100644 index 0000000..813e085 --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlibabaIdCardVerify.cs @@ -0,0 +1,105 @@ +using CloudGaming.Code.Contract; + +using Newtonsoft.Json; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Tea; + +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace CloudGaming.Code.Aliyun +{ + /// + /// 阿里云身份证号验证 AlibabaExtend + /// + public class AlibabaIdCardVerify(AliyunConfig aliyunConfig) : IIdCardVerify + { + + /// Description: + /// + /// 使用AK&SK初始化账号Client + /// + /// + /// + /// Client + /// + /// + /// Exception: + /// Exception + public AlibabaCloud.SDK.Cloudauth20190307.Client CreateClient() + { + // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。 + // 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378671.html。 + AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config + { + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。 + AccessKeyId = aliyunConfig.AccessKeyId, + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 + AccessKeySecret = aliyunConfig.AccessKeySecret, + }; + // Endpoint 请参考 https://api.aliyun.com/product/Cloudauth + config.Endpoint = "cloudauth.aliyuncs.com"; + return new AlibabaCloud.SDK.Cloudauth20190307.Client(config); + } + + /// + /// + /// + /// + /// + /// + public async Task<(bool isVerify, string msg)> IdCardVerify(string userName, string idCard) + { + AlibabaCloud.SDK.Cloudauth20190307.Client client = CreateClient(); + AlibabaCloud.SDK.Cloudauth20190307.Models.Id2MetaVerifyRequest id2MetaVerifyRequest = new AlibabaCloud.SDK.Cloudauth20190307.Models.Id2MetaVerifyRequest + { + ParamType = "normal", + IdentifyNum = idCard, + UserName = userName, + }; + AlibabaCloud.TeaUtil.Models.RuntimeOptions runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions(); + try + { + // 复制代码运行请自行打印 API 的返回值 + var obj = await client.Id2MetaVerifyWithOptionsAsync(id2MetaVerifyRequest, runtime); + if (obj.Body.ResultObject.BizCode == "1") + { + return (true, obj.Body.ResultObject.BizCode); + } + else + { + return (false, obj.Body.ResultObject.BizCode == "2" ? "核验不一致" : $"{obj.Body.ResultObject.BizCode}_查无记录"); + } + } + catch (TeaException error) + { + // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。 + // 错误 message + Console.WriteLine(error.Message); + // 诊断地址 + Console.WriteLine(error.Data["Recommend"]); + AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message); + return (false, error.Message); + } + catch (Exception _error) + { + TeaException error = new TeaException(new Dictionary + { + { "message", _error.Message } + }); + // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。 + // 错误 message + Console.WriteLine(error.Message); + // 诊断地址 + Console.WriteLine(error.Data["Recommend"]); + AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message); + return (false, error.Message); + } + } + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs index e231d32..f758d5c 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs @@ -1,5 +1,6 @@ using AutoMapper; +using CloudGaming.Code.Account; using CloudGaming.Code.Cache; using CloudGaming.Code.Config; using CloudGaming.Code.DataAccess; @@ -249,15 +250,15 @@ namespace CloudGaming.Code.AppExtend #endregion #region 用户信息 - private RequestUserInfo? _userInfo; + private RequestUserInfo? _requestUserInfo; /// /// 用户信息 /// - public RequestUserInfo UserInfo + public RequestUserInfo RequestUserInfo { get { - if (_userInfo == null) + if (_requestUserInfo == null) { var accessToken = HttpContextAccessor.HttpContext.Request.Headers.GetAuthorization(); if (!string.IsNullOrEmpty(accessToken)) @@ -276,7 +277,7 @@ namespace CloudGaming.Code.AppExtend } var nickName = principal.FindFirst("NickName")?.Value; var userId = int.Parse(userIdStr); - this._userInfo = new RequestUserInfo() + this._requestUserInfo = new RequestUserInfo() { UserId = userId, NickName = nickName @@ -284,7 +285,7 @@ namespace CloudGaming.Code.AppExtend } catch (Exception) { - _userInfo = new RequestUserInfo() + _requestUserInfo = new RequestUserInfo() { UserId = 0 }; @@ -292,14 +293,14 @@ namespace CloudGaming.Code.AppExtend } else { - _userInfo = new RequestUserInfo() + _requestUserInfo = new RequestUserInfo() { UserId = 0 }; } } - return _userInfo; + return _requestUserInfo; } } @@ -310,9 +311,51 @@ namespace CloudGaming.Code.AppExtend { get { - return UserInfo?.UserId ?? 0; + return RequestUserInfo?.UserId ?? 0; } } + + private UserInfoCache _userInfo; + /// + /// 用户信息 + /// + public UserInfoCache UserInfo + { + get + { + if (_userInfo == null) + { + if (_UserId == 0) + { + _userInfo = new UserInfoCache(); + } + + _userInfo = _userInfo.GetUserInfo(_UserId, this).Result ?? new UserInfoCache(); + } + return _userInfo; + } + } + + public async Task GetUserInfoCache() + { + if (_UserId == 0) + { + _userInfo = new UserInfoCache(); + } + if (_userInfo == null) + { + _userInfo = await _userInfo.GetUserInfo(_UserId, this) ?? new UserInfoCache(); + } + return _userInfo; + } #endregion + + /// + /// 创建异常 + /// + public Exception CreateException(ResonseCode errorCode, string message) + { + return MessageBox.Show(errorCode, message); + } } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj index 7904fc4..b3eaae3 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj +++ b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj @@ -10,6 +10,7 @@ + diff --git a/src/CloudGaming/Code/CloudGaming.Code/Contract/CloudGamingExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Contract/CloudGamingExtend.cs new file mode 100644 index 0000000..b2a028a --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/Contract/CloudGamingExtend.cs @@ -0,0 +1,38 @@ + +using CloudGaming.Code.Sms; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CloudGaming.Code.Aliyun; + +namespace CloudGaming.Code.Contract +{ + /// + /// + /// + public static class CloudGamingExtend + { + /// + /// 获取短信 + /// + /// + /// + public static IPhoneNumberVerificationService GetPhoneNumberVerificationService(this AliyunConfig aliyunOssConfig) + { + return new AlibabaPhoneNumberVerificationService(aliyunOssConfig); + } + + /// + /// + /// + /// + /// + public static IIdCardVerify GetIdCardVerify(this CloudGamingBase cloudGamingBase) + { + return new AlibabaIdCardVerify(cloudGamingBase.AppConfig.AliyunConfig); + } + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/Contract/IIdCardVerify.cs b/src/CloudGaming/Code/CloudGaming.Code/Contract/IIdCardVerify.cs new file mode 100644 index 0000000..9852a3a --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/Contract/IIdCardVerify.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.Contract +{ + /// + /// + /// + public interface IIdCardVerify + { + /// + /// 身份证号验证 + /// + /// 姓名 + /// 身份证号 + /// + Task<(bool isVerify, string msg)> IdCardVerify(string userName, string idCard); + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/Sms/Contract/IPhoneNumberVerificationService.cs b/src/CloudGaming/Code/CloudGaming.Code/Contract/IPhoneNumberVerificationService.cs similarity index 93% rename from src/CloudGaming/Code/CloudGaming.Code/Sms/Contract/IPhoneNumberVerificationService.cs rename to src/CloudGaming/Code/CloudGaming.Code/Contract/IPhoneNumberVerificationService.cs index 4765a88..16d1086 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Sms/Contract/IPhoneNumberVerificationService.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Contract/IPhoneNumberVerificationService.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace CloudGaming.Code.Sms.Contract +namespace CloudGaming.Code.Contract { /// /// 发送手机短信 diff --git a/src/CloudGaming/Code/CloudGaming.Code/Sms/AlibabaPhoneNumberVerificationService.cs b/src/CloudGaming/Code/CloudGaming.Code/Sms/AlibabaPhoneNumberVerificationService.cs index ca79868..714f23a 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Sms/AlibabaPhoneNumberVerificationService.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Sms/AlibabaPhoneNumberVerificationService.cs @@ -1,4 +1,6 @@ -using CloudGaming.Code.Sms.Contract; + + +using CloudGaming.Code.Contract; using System; using System.Collections.Generic; @@ -18,7 +20,7 @@ namespace CloudGaming.Code.Sms /// Description: /// - /// 使用AK&SK初始化账号Client + /// 使用AK&SK初始化账号Client /// /// /// @@ -102,15 +104,7 @@ namespace CloudGaming.Code.Sms /// public static class SmsExtend { - /// - /// 获取短信 - /// - /// - /// - public static IPhoneNumberVerificationService GetPhoneNumberVerificationService(this AliyunConfig aliyunOssConfig) - { - return new AlibabaPhoneNumberVerificationService(aliyunOssConfig); - } + } } diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserActionTypeEnum.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserActionTypeEnum.cs new file mode 100644 index 0000000..20b9273 --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserActionTypeEnum.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.DtoModel.Account.User +{ + + /// + /// + /// + public class UserActionTypeEnum + { + /// + /// 行为类型 + /// + public enum ActionType + { + /// + /// 新人礼包 + /// + 新人礼包 = 1, + /// + /// 新人第二天登录礼包 + /// + 新人第二天登录礼包 = 2, + /// + /// 实名认证 + /// + 实名认证 = 3, + 换渠道礼包 = 4 + } + } +} diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs new file mode 100644 index 0000000..e65bb3d --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.DtoModel.Account.User +{ + /// + /// 用户缓存 + /// + public class UserInfoCache : UserInfo + { + /// + /// 最后一次实名认证时间 + /// + public DateTime? RealNameDateTimeLast { get; set; } + + /// + /// 实名认证次数 + /// + public int RealAuthentCount { get; set; } + + /// + /// 设备号 + /// + public string DeviceNumber { get; set; } + } +} diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoDto.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoDto.cs index 1d9e8c6..e48c6e2 100644 --- a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoDto.cs +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoDto.cs @@ -11,7 +11,7 @@ namespace CloudGaming.DtoModel.Account.User /// /// 用户信息 /// - [AutoMap(typeof(UserInfo))] + [AutoMap(typeof(UserInfoCache))] public class UserInfoDto : UserInfo { } diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/UserRealAuthenticationRequest.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/UserRealAuthenticationRequest.cs new file mode 100644 index 0000000..8b283c4 --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/UserRealAuthenticationRequest.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.DtoModel.Account +{ + /// + /// 用户实名认证 + /// + public class UserRealAuthenticationRequest + { + /// + /// 姓名 + /// + public string UserName { get; set; } + /// + /// 身份证号 + /// + public string IdCard { get; set; } + /// + /// 设备号 + /// + public string DeviceNumber { get; set; } + + } +} diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs index 2c953e1..623fa00 100644 --- a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs +++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs @@ -64,6 +64,11 @@ public partial class CloudGamingCBTContext : DbContext /// public virtual DbSet T_User_Login_Log { get; set; } + /// + /// 用户实名认证记录表 + /// + public virtual DbSet T_User_RealAuthentication_Log { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {// => optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;"); } @@ -205,6 +210,45 @@ public partial class CloudGamingCBTContext : DbContext }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK_T_USER_REALAUTHENTICATIONLO"); + + entity.ToTable(tb => tb.HasComment("用户实名认证记录表")); + + entity.HasIndex(e => e.UserId, "UserId").IsDescending(); + + entity.Property(e => e.Id).HasComment("主键Id"); + entity.Property(e => e.CreateTime) + .HasDefaultValueSql("(getdate())") + .HasComment("创建时间") + .HasColumnType("datetime"); + entity.Property(e => e.DeviceNumber) + .HasMaxLength(100) + .HasComment("设备号"); + entity.Property(e => e.IdCard) + .HasMaxLength(20) + .HasDefaultValue("") + .HasComment("身份证号") + .UseCollation("Chinese_PRC_CI_AS"); + entity.Property(e => e.IsRewards).HasComment("是否发放奖励"); + entity.Property(e => e.Message) + .HasMaxLength(100) + .HasComment("扩展消息"); + entity.Property(e => e.RealResults) + .HasMaxLength(100) + .HasComment("认证结果"); + entity.Property(e => e.UserId) + .HasDefaultValueSql("('')") + .HasComment("用户id"); + entity.Property(e => e.UserName) + .HasMaxLength(30) + .HasDefaultValue("") + .HasComment("姓名") + .UseCollation("Chinese_PRC_CI_AS"); + + }); + OnModelCreatingPartial(modelBuilder); } diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_User_RealAuthentication_Log.cs b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_User_RealAuthentication_Log.cs new file mode 100644 index 0000000..ccbbb2d --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_User_RealAuthentication_Log.cs @@ -0,0 +1,56 @@ +using System; + +namespace CloudGaming.GameModel.Db.Db_Ext; + +/// +/// 用户实名认证记录表 +/// +public partial class T_User_RealAuthentication_Log +{ + public T_User_RealAuthentication_Log() { } + + /// + /// 主键Id + /// + public virtual int Id { get; set; } + + /// + /// 用户id + /// + public virtual int UserId { get; set; } + + /// + /// 姓名 + /// + public virtual string UserName { get; set; } = null!; + + /// + /// 身份证号 + /// + public virtual string IdCard { get; set; } = null!; + + /// + /// 创建时间 + /// + public virtual DateTime CreateTime { get; set; } + + /// + /// 设备号 + /// + public virtual string? DeviceNumber { get; set; } + + /// + /// 是否发放奖励 + /// + public virtual bool IsRewards { get; set; } + + /// + /// 认证结果 + /// + public virtual string? RealResults { get; set; } + + /// + /// 扩展消息 + /// + public virtual string? Message { get; set; } +} diff --git a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/CloudGamingUserContext.cs b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/CloudGamingUserContext.cs index 53f6d01..b3a314b 100644 --- a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/CloudGamingUserContext.cs +++ b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/CloudGamingUserContext.cs @@ -63,6 +63,11 @@ public partial class CloudGamingUserContext : MultiTenantDbContext//DbContext /// public virtual DbSet T_User_IntentOrder { get; set; } + /// + /// 用户设备限制行为日志表 + /// + public virtual DbSet T_User_LimitActionLog { get; set; } + /// /// 小程序登录表 /// @@ -228,6 +233,9 @@ public partial class CloudGamingUserContext : MultiTenantDbContext//DbContext entity.Property(e => e.CreateAt) .HasComment("创建时间") .HasColumnType("datetime"); + entity.Property(e => e.DeviceNumber) + .HasMaxLength(255) + .HasComment("设备号"); entity.Property(e => e.Email) .HasMaxLength(255) .HasComment("邮箱"); @@ -292,6 +300,41 @@ public partial class CloudGamingUserContext : MultiTenantDbContext//DbContext } }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK_T_USER_LIMITACTIONLOG"); + + entity.ToTable(tb => tb.HasComment("用户设备限制行为日志表")); + + entity.Property(e => e.Id).HasComment("自增"); + entity.Property(e => e.ActionId) + .HasMaxLength(100) + .HasComment("行为参数") + .UseCollation("Chinese_PRC_CI_AS"); + entity.Property(e => e.ActionType).HasComment("行为类型1新人礼包,2新人第二天登录礼包,3实名认证奖励"); + entity.Property(e => e.CreateTime) + .HasComment("创建时间") + .HasColumnType("datetime"); + entity.Property(e => e.CreateTimeDay).HasComment("创建时间,天"); + entity.Property(e => e.DeviceNumber) + .HasMaxLength(100) + .HasComment("设备号"); + entity.Property(e => e.Extend1) + .HasMaxLength(200) + .HasComment("扩展字段") + .UseCollation("Chinese_PRC_CI_AS"); + entity.Property(e => e.Ip) + .HasMaxLength(100) + .HasComment("请求Ip") + .UseCollation("Chinese_PRC_CI_AS"); + entity.Property(e => e.UserId).HasComment("用户Id"); + //添加全局筛选器 + if (this.TenantInfo != null) + { + entity.HasQueryFilter(it => it.TenantId == this.TenantInfo.TenantId); + } + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PK__T_User_M__3214EC073889A8B9"); diff --git a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_Data.cs b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_Data.cs index d3a3d2b..60d1625 100644 --- a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_Data.cs +++ b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_Data.cs @@ -43,4 +43,9 @@ public partial class T_User_Data: MultiTenantEntity /// 修改时间 /// public virtual DateTime UpdateAt { get; set; } + + /// + /// 设备号 + /// + public virtual string? DeviceNumber { get; set; } } diff --git a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_LimitActionLog.cs b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_LimitActionLog.cs new file mode 100644 index 0000000..283c594 --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_User/T_User_LimitActionLog.cs @@ -0,0 +1,56 @@ +using System; + +namespace CloudGaming.Model.DbSqlServer.Db_User; + +/// +/// 用户设备限制行为日志表 +/// +public partial class T_User_LimitActionLog: MultiTenantEntity +{ + public T_User_LimitActionLog() { } + + /// + /// 自增 + /// + public virtual int Id { get; set; } + + /// + /// 用户Id + /// + public virtual int UserId { get; set; } + + /// + /// 请求Ip + /// + public virtual string? Ip { get; set; } + + /// + /// 行为类型1新人礼包,2新人第二天登录礼包,3实名认证奖励 + /// + public virtual int ActionType { get; set; } + + /// + /// 行为参数 + /// + public virtual string? ActionId { get; set; } + + /// + /// 创建时间 + /// + public virtual DateTime CreateTime { get; set; } + + /// + /// 创建时间,天 + /// + public virtual int CreateTimeDay { get; set; } + + /// + /// 设备号 + /// + public virtual string? DeviceNumber { get; set; } + + /// + /// 扩展字段 + /// + public virtual string? Extend1 { get; set; } +} diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/DateTimeExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/DateTimeExtensions.cs index deb92e9..72b700c 100644 --- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/DateTimeExtensions.cs +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/DateTimeExtensions.cs @@ -10,7 +10,7 @@ namespace HuanMeng.DotNetCore.Utility public static class DateTimeExtensions { /// - /// 获取时间戳,秒 + /// 获取时间戳,秒 /// /// /// diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/OtherExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/OtherExtensions.cs new file mode 100644 index 0000000..06338e4 --- /dev/null +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/OtherExtensions.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace HuanMeng.DotNetCore.Utility +{ + /// + /// 其它扩展类 + /// + public static class OtherExtensions + { + /// + /// 验证身份证号是否正确 + /// + /// + /// + + public static bool ValidateIDCard(string idNumber) + { + if (string.IsNullOrEmpty(idNumber)) + { + return false; + } + + // 验证长度 + if (idNumber.Length != 18) + { + return false; + } + + // 验证格式 + Regex regex = new Regex(@"^\d{17}(\d|X)$"); + if (!regex.IsMatch(idNumber)) + { + return false; + } + + // 验证校验码 + return ValidateCheckDigit(idNumber); + } + /// + /// 验证校验码 + /// + /// + /// + + private static bool ValidateCheckDigit(string idNumber) + { + // 加权因子 + int[] weights = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; + // 校验码 + char[] checkDigits = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' }; + + int sum = 0; + for (int i = 0; i < 17; i++) + { + sum += (idNumber[i] - '0') * weights[i]; + } + + int mod = sum % 11; + return idNumber[17] == checkDigits[mod]; + } + + /// + /// 根据身份证号计算年龄、性别、生日 + /// + /// 身份证号 + /// + public static (bool verify, int age, string sex, string birthday) GetIDCardBirthdayAgeSex(string identityCard) + { + var birthday = ""; + var age = 0; + var sex = ""; + if (string.IsNullOrEmpty(identityCard)) + { + return (false, 0, "", ""); + } + else + { + if (identityCard.Length != 15 && identityCard.Length != 18)//身份证号码只能为15位或18位其它不合法 + { + return (false, 0, "", ""); + } + } + + birthday = ""; + string strSex = string.Empty; + if (identityCard.Length == 18)//处理18位的身份证号码从号码中得到生日和性别代码 + { + birthday = identityCard.Substring(6, 4) + "-" + identityCard.Substring(10, 2) + "-" + identityCard.Substring(12, 2); + strSex = identityCard.Substring(14, 3); + } + if (identityCard.Length == 15) + { + birthday = "19" + identityCard.Substring(6, 2) + "-" + identityCard.Substring(8, 2) + "-" + identityCard.Substring(10, 2); + strSex = identityCard.Substring(12, 3); + } + + age = CalculateAge(birthday);//根据生日计算年龄 + + if (int.Parse(strSex) % 2 == 0)//性别代码为偶数是女性奇数为男性 + { + sex = "女"; + } + else + { + sex = "男"; + } + return (true, age, sex, birthday); + } + + /// + /// 根据出生日期,计算精确的年龄 + /// + /// 生日 + /// + public static int CalculateAge(string birthDay) + { + DateTime birthDate = DateTime.Parse(birthDay); + DateTime nowDateTime = DateTime.Now; + int age = nowDateTime.Year - birthDate.Year; + //再考虑月、天的因素 + if (nowDateTime.Month < birthDate.Month || (nowDateTime.Month == birthDate.Month && nowDateTime.Day < birthDate.Day)) + { + age--; + } + return age; + } + } +}