第52章 短信验证服务和登录的后端定义实现
1 Services.Messages.SmsValidate
using Core.Domain.Messages;
using Data;
using Microsoft.EntityFrameworkCore;
namespace Services.Messages
{
/// <summary>
/// 【短信验证服务--类】
/// <remarks>
/// 摘要:
/// 通过类中的方法成员实现当前程序与短信验证表之间的CURD操作。
/// </remarks>
/// </summary>
public class SmsValidateService : ISmsValidateService
{
#region 拷贝构造方法与变量
private readonly IRepository<SmsValidate> _smsValidateRepository;
/// <summary>
/// 【拷贝构建方法】
/// <remarks>
/// 摘要:
/// 依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。
/// </remarks>
/// </summary>
public SmsValidateService(
IRepository<SmsValidate> smsValidateRepository)
{
_smsValidateRepository = smsValidateRepository;
}
#endregion
/// <param name="phone">1个指定的手机号。</param>
/// <param name="validateCode">1个输入短信验证码。</param>
/// <summary>
/// 【异步短信验证?】
/// <remarks>
/// 摘要:
/// 获取1个值false(不等)/true(相等),该值指示用户输入的短信验证码与持久化存储的短信验证码是否相等,如果相等则用户通过验证可以执行登录操作;反之用户未通过验证不能执行登录操作。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(不等)/true(相等)。
/// </returns>
/// </summary>
public async Task<bool> ValidateSmsValidateAsync(string phone, string validateCode)
{
if (string.IsNullOrWhiteSpace(phone)|| string.IsNullOrWhiteSpace(validateCode))
return false;
SmsValidate _smsValidate = await SmsValidateByPhoneAndCode(phone, validateCode);
if (_smsValidate!=null)
return true;
return false;
}
/// <param name="phone">1个指定的手机号。</param>
/// <param name="validateCode">1个输入短信验证码。</param>
/// <summary>
/// 【异步通过手机号和短信验证码获取】
/// <remarks>
/// 摘要:
/// 直接从短信验证表中获用户实体的1个指定实例;或从分布式缓存数据库获取短信验证实体的1个指定实例。
/// </remarks>
/// <returns>
/// 返回:
/// 短信验证实体的1个指定实例。
/// </returns>
/// </summary>
public async Task<SmsValidate> SmsValidateByPhoneAndCode(string phone, string validateCode)
{
if (string.IsNullOrWhiteSpace(phone) || string.IsNullOrWhiteSpace(validateCode))
return null;
return await _smsValidateRepository.Table
.Where(x => x.Phone == phone
&& x.Code == validateCode
&& !x.IsValidate
&& x.ExpiryDateTime >= DateTime.Now
&& x.Count < 3).OrderByDescending(x => x.CreatedDateTime)
.LastOrDefaultAsync();
}
/// <param name="smsValidate">短信验证实体的1个指定实例。</param>
/// <summary>
/// 【异步插入短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体的1个指定实例持久化插入到短信验证表中后,并从缓存数据库中移除与短信验证相关的所有缓存项。
/// </remarks>
/// </summary>
public async Task InsertSmsValidateAsync(SmsValidate smsValidate)
{
await _smsValidateRepository.InsertAsync(smsValidate);
}
/// <param name="smsValidateList">列表实例,该实例存储着短信验证实体的1/n个指定实例。</param>
/// <summary>
/// 【异步插入短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体的1/n个指定实例持久化插入到短信验证表中后,并从缓存数据库中移除与短信验证相关的所有缓存项。
/// </remarks>
/// </summary>
public async Task InsertSmsValidateAsync(IList<SmsValidate> smsValidateList)
{
await _smsValidateRepository.InsertAsync(smsValidateList);
}
/// <param name="smsValidate">短信验证实体的1个指定实例。</param>
/// <summary>
/// 【异步更新用户】
/// <remarks>
/// 摘要:
/// 把短信验证实体实体的1个指定实例持久化更新到短信验证表中后,并从缓存数据库中移除与短信验证实体相关的所有缓存项。
/// </remarks>
/// </summary>
public async Task UpdateSmsValidateAsync(SmsValidate smsValidate)
{
await _smsValidateRepository.UpdateAsync(smsValidate);
}
/// <param name="smsValidateList">列表实例,该实例存储着短信验证实体的1/n个指定实例。</param>
/// <summary>
/// 【异步更新短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体实体的1/n个指定实例持久化更新到短信验证表中后,并从缓存数据库中移除与短信验证实体相关的所有缓存项。
/// </remarks>
/// </summary>
public async Task UpdateSmsValidateAsync(IList<SmsValidate> smsValidateList)
{
await _smsValidateRepository.UpdateAsync(smsValidateList);
}
}
}
2 Services.Messages.ISmsValidate
using Core.Domain.Messages;
namespace Services.Messages
{
/// <summary>
/// 【短信验证服务--接口】
/// <remarks>
/// 摘要:
/// 通过继承于该接口具体实现类中的方法成员实现当前程序与短信验证表之间的CURD操作。
/// </remarks>
/// </summary>
public interface ISmsValidateService
{
/// <param name="phone">1个指定的手机号。</param>
/// <param name="validateCode">1个输入短信验证码。</param>
/// <summary>
/// 【异步短信验证?】
/// <remarks>
/// 摘要:
/// 获取1个值false(不等)/true(相等),该值指示用户输入的短信验证码与持久化存储的短信验证码是否相等,如果相等则用户通过验证可以执行登录操作;反之用户未通过验证不能执行登录操作。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(不等)/true(相等)。
/// </returns>
/// </summary>
Task<bool> ValidateSmsValidateAsync(string phone, string validateCode);
/// <param name="phone">1个指定的手机号。</param>
/// <param name="validateCode">1个输入短信验证码。</param>
/// <summary>
/// 【异步通过手机号和短信验证码获取】
/// <remarks>
/// 摘要:
/// 直接从短信验证表中获用户实体的1个指定实例;或从分布式缓存数据库获取短信验证实体的1个指定实例。
/// </remarks>
/// <returns>
/// 返回:
/// 短信验证实体的1个指定实例。
/// </returns>
/// </summary>
Task<SmsValidate> SmsValidateByPhoneAndCode(string phone, string validateCode);
/// <param name="smsValidate">短信验证实体的1个指定实例。</param>
/// <summary>
/// 【异步插入短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体的1个指定实例持久化插入到短信验证表中后,并从缓存数据库中移除与短信验证相关的所有缓存项。
/// </remarks>
/// </summary>
Task InsertSmsValidateAsync(SmsValidate smsValidate);
/// <param name="smsValidateList">列表实例,该实例存储着短信验证实体的1/n个指定实例。</param>
/// <summary>
/// 【异步插入短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体的1/n个指定实例持久化插入到短信验证表中后,并从缓存数据库中移除与短信验证相关的所有缓存项。
/// </remarks>
/// </summary>
Task InsertSmsValidateAsync(IList<SmsValidate> smsValidateList);
/// <param name="smsValidate">短信验证实体的1个指定实例。</param>
/// <summary>
/// 【异步更新用户】
/// <remarks>
/// 摘要:
/// 把短信验证实体实体的1个指定实例持久化更新到短信验证表中后,并从缓存数据库中移除与短信验证实体相关的所有缓存项。
/// </remarks>
/// </summary>
Task UpdateSmsValidateAsync(SmsValidate smsValidate);
/// <param name="smsValidateList">列表实例,该实例存储着短信验证实体的1/n个指定实例。</param>
/// <summary>
/// 【异步更新短信验证】
/// <remarks>
/// 摘要:
/// 把短信验证实体实体的1/n个指定实例持久化更新到短信验证表中后,并从缓存数据库中移除与短信验证实体相关的所有缓存项。
/// </remarks>
/// </summary>
Task UpdateSmsValidateAsync(IList<SmsValidate> smsValidateList);
}
}
3 向Services.Customers.CustomerService添加定义
/// <param name="phone">1个指定的手机号字符串。</param>
/// <summary>
/// 【异步通过手机号获取用户】
/// <remarks>
/// 摘要:
/// 直接从用户表中获用户实体的1个指定实例;或从分布式缓存数据库获取用户实体的1个指定实例(即使该实例处于逻辑删除状态,也获取该实例)。
/// </remarks>
/// <returns>
/// 返回:
/// 用户实体的1个指定实例。
/// </returns>
/// </summary>
public async Task<Customer> GetCustomerByPhoneAsync(string phone)
{
if (string.IsNullOrWhiteSpace(phone))
return null;
return await _customerRepository.Table.Where(x => x.Phone == phone).FirstOrDefaultAsync();
}
/// <param name="phone">1个指定的手机号。</param>
/// <param name="password">1个将要被加密的密码(明码)字符串。</param>
/// <summary>
/// 【异步通过手机号进行用户验证?】
/// <remarks>
/// 摘要:
/// 获取1个值false(不等)/true(相等),该值指示用户输入的密码通过指定的加密方式加密后是否与持久化存储的密码相相等,如果相等则用户通过验证可以执行登录操作;反之用户未通过验证不能执行登录操作。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(不等)/true(相等)。
/// </returns>
/// </summary>
public async Task<bool> ValidateCustomerByPhoneAsync(string phone, string password)
{
Customer _customer = await GetCustomerByPhoneAsync(phone);
if (_customer == null)
return false;
CustomerPassword _customerPassword = await GetCustomerPasswordsAsync(_customer.Id);
if (!PasswordsMatch(_customerPassword, password))
return false;
return true;
}
4 WebApi.Models.Customer.RegisterModel
namespace WebApi.Models.Customer
{
/// <summary>
/// 【用户注册模型--纪录】
/// <remarks>
/// 摘要:
/// 通过该纪录中的属性成员实例存储用于用户注册操作。
/// </remarks>
/// </summary>
public record RegisterModel
{
/// <summary>
/// 【用户名】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定的用户名(账户、昵称)。
/// </remarks>
/// </summary>
public string Name { get; set; }
/// <summary>
/// 【电子邮箱】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定用户所对应的电子邮箱。
/// </remarks>
/// </summary>
public string Email { get; set; }
/// <summary>
/// 【手机号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定用户所对应的手机号。
/// </remarks>
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 【密码】
/// <remarks>
/// 摘要:
/// 获取/设置1个用于登录操作的密码。
/// </remarks>
/// </summary>
public string Password { get; set; }
}
}
5 WebApi.Models.Customer.LoginSmsModel
namespace WebApi.Models.Customer
{
/// <summary>
/// 【短信验证登录模型--纪录】
/// <remarks>
/// 摘要:
/// 通过该纪录中的属性成员实例存储用于短信验证登录操作账户名、密码及其验证码。
/// </remarks>
/// </summary>
public record LoginSmsModel
{
/// <summary>
/// 【手机号】
/// <remarks>
/// 摘要:
/// 获取/设置1个用于短信验证登录操作的手机号。
/// </remarks>
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 【密码】
/// <remarks>
/// 摘要:
/// 获取/设置1个用于短信验证登录操作的密码。
/// </remarks>
/// </summary>
public string Password { get; set; }
/// <summary>
/// 【短信验证码】
/// <remarks>
/// 摘要:
/// 获取/设置向1个指定手机所发送的1个指定短信验证码。
/// </remarks>
/// </summary>
public string Code { get; set; }
}
}
6 向WebApi.Controllers.CustomerController添加定义
/// <param name="phone">1个指定的手机号。</param>
/// <summary>
/// 【异步短信验证码发送】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过第3方短信验证码发送服务平台,向1个指定的手机中发送1个指定的短信验证码后,把短信验证码实体的1个指定实例持久化到短信验证码表中。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定的手机中所发送的1个指定的短信验证码。
/// </returns>
private async Task<int> SmsValidateSendAsync(string phone)
{
//从单例实例的字典成员实例中获取当前程序所有配置相关数据。
var appSettings = Singleton<AppSettings>.Instance;
//从应用配置类实例中获取分布式缓存连接相关数据。
SmsConfig _smsConfig = appSettings.Get<SmsConfig>();
using var random = new SecureRandomNumberGenerator();
int _code = random.Next(100000, 999999);
Config config = new Config
{
AccessKeyId = _smsConfig.AccessKeyId,
AccessKeySecret = _smsConfig.AccessKeySecret,
};
config.Endpoint = _smsConfig.Endpoint;
Client _client = new Client(config);
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "code", _code.ToString() }
};
string _templateParam = JsonConvert.SerializeObject(data);
SendSmsRequest _sendSmsRequest = new SendSmsRequest
{
PhoneNumbers = phone,
SignName = _smsConfig.SignName,
TemplateCode = _smsConfig.TemplateCode,
TemplateParam = _templateParam, //验证码信息
};
SendSmsResponse _sendSmsResponse = await _client.SendSmsAsync(_sendSmsRequest);
if (_sendSmsResponse.StatusCode==200)
{
SmsValidate _smsValidate = new SmsValidate()
{
Phone = phone,
Content = $"您验证码是{_code},请不要将验证码泄露给他人!",
Code = _code.ToString(),
Count = 0,
CreatedDateTime = DateTime.Now,
ExpiryDateTime = DateTime.Now.AddMinutes(5),
};
await _smsValidateService.InsertSmsValidateAsync(_smsValidate);
return _code;
}
return 0;
}
/// <param name="phone">1个指定的手机号。</param>
/// <summary>
/// 【新建短信验证码发送--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过第3方短信验证码发送服务平台,向1个指定的手机中发送1个指定的短信验证码后,把短信验证码实体的1个指定实例持久化到短信验证码表中。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定的手机中所发送的1个指定的短信验证码。
/// </returns>
[HttpPost]
public async Task<MessageModel<string>> SmsValidateCreate([FromBody] string phone)
{
int _code = await SmsValidateSendAsync(phone);
if (_code>= 100000 && _code <=999999)
return MessageModel<string>.GetSuccess($"验证码:{_code}已经成功发送到手机,请在5分钟内使用手机中的验证码进行登录!", _code.ToString());
return MessageModel<string>.Fail("验证码发送失败!", 500);
}
/// <param name="registerModel">用户注册记录的1个指定实例。</param>
/// <summary>
/// 【用户注册--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过用户注册操作把1个指定用户持久化到用户注册表中。
/// </remarks>
/// <returns>
/// 返回:
/// 用户实体的1个指定实例。
/// </returns>
[HttpPost]
public virtual async Task<MessageModel<Customer>> Register([FromBody] RegisterModel registerModel)
{
Customer _customer = new Customer() {
Name = registerModel.Name,
Email = registerModel.Email,
Phone = registerModel.Phone,
CreatedDateTime = DateTime.Now,
UpdatedDateTime = DateTime.Now,
};
await _customerService.InsertCustomerAsync(_customer);
CustomerPassword _customerPassword = new CustomerPassword
{
CustomerId = _customer.Id,
PasswordFormat = PasswordFormat.Hashed,
};
var saltKey = _encryptionService.CreateSaltKey(CustomerPassword.PasswordSaltKeySize);
_customerPassword.PasswordSalt = saltKey;
_customerPassword.Password = _encryptionService.CreatePasswordHash(registerModel.Password, saltKey, CustomerPassword.DefaultHashedPasswordFormat);
await _customerService.InsertCustomerPasswordAsync(_customerPassword);
await _customerService.AddCustomerRoleAsync(new CustomerRole { CustomerId = _customer.Id, RoleId = 2});
int _code = await SmsValidateSendAsync(registerModel.Phone);
//注意:由于该用户实例有512位的密码,会造成异常:“This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.”
if (_code >= 100000 && _code <= 999999)
return MessageModel<Customer>.GetSuccess($"用户{registerModel.Phone}成功注册,请在5分钟内使用手机中的验证码进行登录对当前注册进行激活操作!", _customer);
return MessageModel<Customer>.Fail($"用户{registerModel.Phone}成功注册失败!", 500);
}
/// <param name="email">1个指定的电子邮箱。</param>
/// <summary>
/// 【电子邮箱已被注册?--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 获取1个值false(未被注册)/true(已被注册),该值指示1个指定的电子邮箱是否已被注册,为用户注册和登录操作提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(未被注册)/true(已被注册)。
/// </returns>
[HttpGet]
public virtual async Task<MessageModel<bool>> IsEmail(string email)
{
Customer _customer = await _customerService.GetCustomerByEmailAsync(email);
if (_customer == null)
return MessageModel<bool>.Fail($"指定电邮箱:{email}不存在!", 500);
return MessageModel<bool>.GetSuccess($"成功获取指定电邮箱:{email}!", true);
}
/// <param name="phone">1个指定的手机号。</param>
/// <summary>
/// 【手机号已被注册?--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 获取1个值false(未被注册)/true(已被注册),该值指示1个指定的手机号是否已被注册,为用户注册和登录操作提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(未被注册)/true(已被注册)。
/// </returns>
[HttpGet]
public virtual async Task<MessageModel<bool>> IsPhone(string phone)
{
Customer _customer = await _customerService.GetCustomerByPhoneAsync(phone);
if (_customer == null)
return MessageModel<bool>.Fail($"指定手机号:{phone}不存在!", 500);
return MessageModel<bool>.GetSuccess($"成功获取指定手机号:{phone}!", true);
}
/// <param name="phone">1个指定的手机号。</param>
/// <param name="code">1个指定的短信验证码。</param>
/// <summary>
/// 【短信验证码有有效?--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 获取 1个值false(无效)/true(有效),该值指示1个指定的短信验证码是否还有效,为登录操作提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(无效)/true(有效)。
/// </returns>
[HttpGet]
public virtual async Task<MessageModel<bool>> IsCode(string phone, string code)
{
if (await _smsValidateService.ValidateSmsValidateAsync(phone, code))
return MessageModel<bool>.GetSuccess($"成功获取指定短信验证码:{code}!", true);
return MessageModel<bool>.Fail($"指定短信验证码:{code}不存在!", 500);
}
/// <param name="loginSms">短信验证登录模型记录的1个指定实例。</param>
/// <summary>
/// 【短信验证登录--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过登录操作获取1个指定用户的1个指定令牌(Token)字符串实例,为访问指定权限的Api提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定用户的1个指定令牌(Token)字符串实例。
/// </returns>
[HttpPost]
public async Task<MessageModel<TokenViewModel>> LoginSms([FromBody] LoginSmsModel loginSms)
{
if (await _customerService.ValidateCustomerByPhoneAsync(loginSms.Phone, loginSms.Password))
{
if(await _smsValidateService.ValidateSmsValidateAsync(loginSms.Phone, loginSms.Code))
{
Customer _customer = await _customerService.GetCustomerByPhoneAsync(loginSms.Phone);
//激活注册用户。
_customer.IsActive = true;
_customer.UpdatedDateTime = DateTime.Now;
await _customerService.UpdateCustomerAsync(_customer);
SmsValidate _smsValidate = await _smsValidateService.SmsValidateByPhoneAndCode(loginSms.Phone, loginSms.Code);
_smsValidate.IsValidate = true;
await _smsValidateService.UpdateSmsValidateAsync(_smsValidate);
string _roleList = await _customerService.GetRoleNameByCustomerIdAsync(_customer.Id);
var claims = new List<Claim> {
new Claim(ClaimTypes.Email, _customer.Email),
new Claim(JwtRegisteredClaimNames.Jti, _customer.Id.ToString()),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(TimeSpan.FromSeconds(60 * 60).TotalSeconds).ToString()) };
claims.AddRange(_roleList.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//从单例实例的字典成员实例中获取当前程序所有配置相关数据。
AppSettings _appSettings = Singleton<AppSettings>.Instance;
//从应用配置类实例中获取JwtBearer身份认证相关数据。
JwtBearerConfig _jwtBearerConfig = _appSettings.Get<JwtBearerConfig>();
//获取秘钥。
SymmetricSecurityKey _symmetricSecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_jwtBearerConfig.SecretKey));
//通过1个指定的加密算法名称,实例化。
SigningCredentials _signingCredentials = new SigningCredentials(_symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
JwtSecurityToken _jwtSecurityToken = new JwtSecurityToken(
issuer: _jwtBearerConfig.Issuer,
audience: _jwtBearerConfig.Audience,
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.Add(TimeSpan.FromSeconds(60 * 60)),
signingCredentials: _signingCredentials
);
string _token = new JwtSecurityTokenHandler().WriteToken(_jwtSecurityToken);
TokenViewModel _tokenViewModel = new TokenViewModel
{
Success = true,
Token = _token,
ExpiresIn = TimeSpan.FromSeconds(60 * 60).TotalSeconds,
TokenType = "Bearer"//基于JwtBearer身份认证方式。
};
return MessageModel<TokenViewModel>.GetSuccess("成功登录!", _tokenViewModel);
}
}
return MessageModel<TokenViewModel>.Fail("登录失败!", 500);
}
7 循环引用异常:“This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.”
解决方案:
//忽略循环引用,解决异常:“This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.”
services.AddControllers().AddNewtonsoftJson(option =>
option.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
);
对以上功能更为具体实现和注释见:230304_043shopDemo(短信验证服务和登录的后端定义实现)。
相关文章:
第52章 短信验证服务和登录的后端定义实现
1 Services.Messages.SmsValidate using Core.Domain.Messages; using Data; using Microsoft.EntityFrameworkCore; namespace Services.Messages { /// <summary> /// 【短信验证服务--类】 /// <remarks> /// 摘要: /// 通过类中的方法成员实…...
谷歌验证码的使用
1. 表单重复提交之验证码 1.1 表单重复提交三种常见情况 提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转用户正常提交服务器&…...
Git学习入门(1)- git的安装与配置
title: git学习(1) - git的安装与配置CSDN: https://blog.csdn.net/jj6666djdbbd?typeblogBlog: https://helloylh.comGithub: https://github.com/luumodtags: gitabbrlink: 12001description: 本文主要讲解了git的安装,配置基本工作date: …...
【Python】使用Playwright断言方法验证网页和Web应用程序状态
作为测试框架,Playwright 提供了一系列断言方法,您可以使用它们来验证网页和 Web 应用程序的状态。在这篇博客中,田辛老师将介绍 Playwright 中可用的各种断言方法,并为每种方法提供示例。 assert page.url() expected_url &…...
libgdx导入blender模型
具体就是参考 官网 https://libgdx.com/wiki/graphics/3d/importing-blender-models-in-libgdx blender 教程可以看八个案例教程带你从0到1入门blender【已完结】 这里贴一下过程图。 1.初始环境搭建略过。 2.打开blender 选中摄像机和灯光,右键进行删除。 3.选中…...
【20230227】回溯算法小结
回溯法又叫回溯搜索法,是搜索的一种方式。回溯法本质是穷举所有可能。如果想让回溯法高效一些,可以加一些剪枝操作。回溯算法解决的经典问题:组合问题切割问题子集问题排列问题棋盘问题如何去理解回溯法?回溯法解决的问题都可以抽…...
centos安装rocketmq
centos安装rocketmq1 下载rocketmq二进制包2 解压二进制包3 修改broker.conf4 修改runbroker.sh和runserver.sh的JVM参数5 启动NameServer和Broker6 安装rockermq dashboard(可视化控制台)1 下载rocketmq二进制包 点击rocketmq二进制包下载地址,下载完成之后通过ft…...
汇编语言程序设计(二)之寄存器
系列文章 汇编语言程序设计(一) 寄存器 在学习汇编的过程中,我们经常需要操作寄存器,那么寄存器又是什么呢?它是用来干什么的? 它有什么分类?又该如何操作?… 你可能会有许多的…...
华为OD机试Golang解题 - 单词接龙 | 独家
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典文章目录 华为Od必看系列使用说明本期题目…...
Elasticsearch的搜索命令
Elasticsearch的搜索命令 文章目录Elasticsearch的搜索命令数据准备URI Searchq(查询字符串)analyzer(指定查询字符串时使用的分析器)df(指定查询字段)_source(指定返回文档的字段)s…...
为什么人们宁可用Lombok,也不把成员设为public?
目录专栏导读一、从零了解JavaBean1、基本概念2、JavaBean的特征3、JavaBean的优点二、定义最简单的JavaBean三、思考一个问题,为何属性是private,然后用get/set方法?四、下面系统的分析以下,why?五、不和谐的声音,禁…...
【Redis】Redis 如何实现分布式锁
Redis 如何实现分布式锁1. 什么是分布式锁1.1 分布式锁的特点1.2 分布式锁的场景1.3 分布式锁的实现方式2. Redis 实现分布式锁2.1 setnx expire2.2 set ex px nx2.3 set ex px nx 校验唯一随机值,再删除2.4 Redisson 实现分布式锁1. 什么是分布式锁 分布式锁其实…...
C++ 断言
文章目录前言assertstatic_assert前言 断言(Assertion)是一种常用的编程手段,用于排除程序中不应该出现的逻辑错误。它是一种很好的Debug工具。其作用是判断表达式是否为真。C提供了assert和static_assert来进行断言。在C库中也有断言,其中断言与C的相同…...
C++修炼之练气期第五层——引用
目录 1.引用的概念 2.引用的性质 3.常量引用 4.使用场景 1.作参数 2.作返回值 5.传值与传引用的效率比较 6.值和引用作为返回值的性能比较 7.引用与指针 指针与引用的不同点 要说C语言中哪个知识点最难学难懂,大部分人可能和我一样的答案——指针。C既然…...
从企业数字化发展的四个阶段,看数字化创新战略
《Edge: Value-Driven Digital Transformation》一书根据信息技术与企业业务发展的关系把企业的数字化分为了四个阶段: 技术与业务无关技术作为服务提供者开始合作科技引领差异化优势以技术为业务核心 下图展示了这四个阶段的特点: 通过了解和分析各个…...
vulnhub five86-1
总结:私钥登录,隐藏文件很多 目录 下载地址 漏洞分析 信息收集 网站渗透 爆破密码 提权 下载地址 Five86-1.zip (Size: 865 MB)Download (Mirror): https://download.vulnhub.com/five86/Five86-1.zip使用:下载以后打开压缩包,使用vm直…...
28个案例问题分析---01---redis没有及时更新问题--Redis
redis没有及时更新问题一:背景介绍二:前期准备pom依赖连接Redis工具类连接mysql工具类三:过程使用redis缓存,缓存用户年龄业务对应流程图使用redis缓存用户年龄对应代码四:总结一:背景介绍 业务中使用redis…...
[1.3_3]计算机系统概述——系统调用
文章目录第一章 计算机系统概述系统调用(一)什么是系统调用,有何作用(二)系统调用与库函数的区别(三)小例子:为什么系统调用是必须的(四)什么功能要用到系统调…...
Vue基础学习 第一个Vue程序 el挂载点 v-指令(1)
Vue简介 Vue是一个Javascript框架Vue框架可以简化Dom操作响应式数据驱动 : 页面是由数据生成的,当数据出现改动,页面也会即时改变 第一个Vue程序 Vue中文文档官网:https://v2.cn.vuejs.org/v2/guide/ 根据官方文档的说法&#…...
前端页面性能
提升页面性能的方法资源压缩合并,减少HTTP请求非核心代码异步加载异步加载方式?1)动态脚本加载、2)defer、3)async(在加载js的时候在script标签上添加这两个属性,<script src"./test.js" charset"utf-8" defer><…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程 传统编程会将所有函数放在main.c中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数声明,其他.c文…...
