【抽奖系统开发实战】Spring Boot 项目的用户模块设计:注册登录、权限管控与敏感数据加密

【抽奖系统开发实战】Spring Boot 项目的用户模块设计:注册登录、权限管控与敏感数据加密

文章目录

一、注册

1.1 敏感字段加密

用户注册时,密码和手机号等敏感信息需加密存储,避免明文泄露风险。

  • 密码加密:采用加盐哈希(SHA-256)方案,用户注册时生成随机盐值,将密码与盐拼接后进行哈希运算,最终存储哈希结果,确保不可逆。
  • 手机号加密:因业务可能需使用明文手机号(如发送短信),采用AES对称加密,存储加密后的结果,使用时解密。
  • 加密工具:引入国产Java工具类库Hutool,封装加密解密方法,简化开发。

HuTool 官网地址:https://hutool.cn/
Maven仓库地址:https://mvnrepository.com/artifact/cn.hutool

可引入以下依赖使用:

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version></dependency>

1.2 用户注册

时序图

在这里插入图片描述

前后端交互接口

  • 请求/register POST
{"name":"张三","mail":"[email protected]","phoneNumber":"13188888888","password":"123456789","identity":"ADMIN"}
  • 响应
{"code":200,"data":{"userId":22},"msg":""}

Controller层接口设计

  • 接收注册参数,通过@Validated注解进行参数校验,调用Service层完成注册逻辑,返回注册结果。
  • 核心类:
    • UserRegisterParam:注册入参封装(姓名、邮箱、手机号、密码、身份),含@NotBlank等校验注解。
    • UserRegisterResult:注册出参封装(用户ID)。
    • UserIdentityEnum:用户身份枚举(普通用户NORMAL、管理员ADMIN)。

控制层代码示例:

@RestControllerpublicclassUserController{privatestaticfinalLogger logger =LoggerFactory.getLogger(UserController.class);@AutowiredprivateUserService userService;@AutowiredprivateVerificationCodeService verificationCodeService;/** * 注册 * @param param * @return */@PostMapping("/register")publicCommonResult<UserRegisterResult>userRegister(@Validated@RequestBodyUserRegisterParam param){// 日志打印 logger.info("userRegister UserRegisterParam 用户注册: {}",JacksonUtil.writeValueAsString(param));// 调用Service 层服务进行访问UserRegisterDTO userRegisterDTO = userService.register(param);returnCommonResult.success(convertToUserRegisterResult(userRegisterDTO));}privateUserRegisterResultconvertToUserRegisterResult(UserRegisterDTO userRegisterDTO){UserRegisterResult result =newUserRegisterResult();if(null== userRegisterDTO){thrownewControllerException(ControllerErrorCodeConstants.REGISTER_ERROR);} result.setUserId(userRegisterDTO.getUserId());return result;}}

使用SpringBoot中集成的Validation需要引入依赖:

<!-- spring-boot 2.3及以上的版本只需要引⼊下⾯的依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

Service层接口设计

  • 接口与实现分离,定义UserService接口,UserServiceImpl实现具体逻辑:
    1. 校验注册信息(邮箱格式、手机号格式、身份合法性、密码格式、邮箱/手机号唯一性)。
    2. 对密码和手机号进行加密处理。
    3. 调用Dao层将用户信息存入数据库。
    4. 返回用户ID封装结果。
  • 核心工具类:RegexUtil,封装邮箱、手机号、密码的格式校验正则表达式。

实现类代码示例:

@Slf4j@ServicepublicclassUserServiceImplimplementsUserService{@AutowiredprivateUserMapper userMapper;@AutowiredprivateVerificationCodeService verificationCodeService;/** * 注册 * * @param param * @return */@OverridepublicUserRegisterDTOregister(UserRegisterParam param){// 校验用户信息checkRegisterInfo(param);//加密数据UserDO userDO =newUserDO(); userDO.setUserName(param.getName()); userDO.setEmail(param.getMail()); userDO.setPhoneNumber(newEncrypt(param.getPhoneNumber())); userDO.setIdentity(param.getIdentity());if(StringUtils.hasLength(param.getPassword())){ userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));}// 保存用户数据 userMapper.insert(userDO);// 构造返回UserRegisterDTO userRegisterDTO =newUserRegisterDTO(); userRegisterDTO.setUserId(userDO.getId());return userRegisterDTO;}privatevoidcheckRegisterInfo(UserRegisterParam param){if(null== param){thrownewServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);}// 校验邮箱格式 [email protected](!RegexUtil.checkMail(param.getMail())){thrownewServiceException(ServiceErrorCodeConstants.MAIL_ERROR);}// 校验手机号格式if(!RegexUtil.checkMobile(param.getPhoneNumber())){thrownewServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);}// 校验身份信息if(null==UserIdentityEnum.forName(param.getIdentity())){thrownewServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);}// 校验密码(管理员必填)if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())&&!StringUtils.hasLength(param.getPassword())){thrownewServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);}// 校验密码格式 至少6位if(StringUtils.hasLength(param.getPassword())&&!RegexUtil.checkPassword(param.getPassword())){thrownewServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);}// 校验邮箱唯一if(checkMailUsed(param.getMail())){thrownewServiceException(ServiceErrorCodeConstants.MAIL_USED);}// 校验手机号唯一if(checkPhoneNumberUsed(param.getPhoneNumber())){thrownewServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);}}/** * 检验手机号是否被使用 * @param phoneNumber * @return */privatebooleancheckPhoneNumberUsed(String phoneNumber){int count = userMapper.countByPhone(newEncrypt(phoneNumber));return count >0;}/** * 校验邮箱是否被使用 * @param mail * @return */privatebooleancheckMailUsed(String mail){int count = userMapper.countByMail(mail);return count >0;}}
接口分离设计的好处: 有助于创建更加灵活、可维护和可扩展的软件系统。抽象与具体实现分离:接口定义了一组操作的契约,而实现则提供了这些操作的具体行为。这种分离允许改变具体实现而不影响使用接口的客户端代码。支持多态性:接口允许通过共同的接口来引用不同的实现,这是多态性的基础,使得代码更加灵活和通用。提高代码的可读性和可理解性:接口提供了清晰的 API 视图,使得其他开发者能够更容易地理解和使用这些 API。安全性:接口可以隐藏实现细节,只暴露必要的操作,这有助于保护系统的内部状态和实现不被外部直接访问。遵循开闭原则:软件实体应当对扩展开放,对修改封闭。接口与实现的分离使得在不修改客户端代码的情况下扩展系统的功能。促进面向对象的设计:接口与实现的分离鼓励开发者进行面向对象的设计,考虑如何将系统分解为可重用和可组合的组件。

Dao层接口设计

  • 使用MyBatis实现数据库交互,核心接口UserMapper
    • insert:插入用户信息,支持自动生成主键。
    • countByPhoneNumber:查询手机号绑定的用户数(校验唯一性)。
    • countByMail:查询邮箱绑定的用户数(校验唯一性)。
  • 核心类:UserDO,映射用户表,继承BaseDO(含主键id、创建时间gmtCreate、更新时间gmtModified)。

注册页面前端实现

  • 使用jQuery Validate插件校验表单字段(姓名、邮箱、手机号、密码必填,密码长度≥6)。
  • 表单验证通过后,通过AJAX发送POST请求到/register接口,携带用户注册信息。
  • 注册成功跳转登录页,失败提示错误信息。

1.3 TypeHandler

对手机号进行存储时,要先将手机号加密,如果要拿出使用时,还要进行一次解密操作。为简化手机号的自动加解密操作,使用MyBatis的TypeHandler实现字段处理。

TypeHandler:简单理解就是当处理特殊字段时,实现一些方法,让mybatis遇到这些特定字段可以自动运行处理。

  • Encrypt类:标记需加解密的字段类型,封装待加密的字符串值。
  • EncryptTypeHandler类:实现BaseTypeHandler<Encrypt>,重写以下方法:
    • setNonNullParameter:设置参数时对手机号进行AES加密。
    • getNullableResult:查询结果时对手机号进行AES解密。
  • 配置:在application.properties中指定TypeHandler的包路径。

二、控制层通用异常处理

使用@RestControllerAdvice+@ExceptionHandler实现全局异常处理,统一响应格式。可以针对所有异常类
型先进行通用处理后,再对特定异常类型进行不同的处理操作

  • 捕获ExceptionServiceExceptionControllerException三类异常。
  • 日志记录异常信息,返回CommonResult格式的错误响应,包含错误码和错误提示。

三、登录

3.1 发送验证码

采用阿里云短信服务发送验证码,支持短信登录方式。因为没用企业认证,只能使用其提供的一些特定模板,示例如下:

在这里插入图片描述

时序图

在这里插入图片描述

配置

  • application.properties中配置阿里云短信服务的AccessKeyId、AccessKeySecret、签名名称。

核心工具类

  • SMSUtil:封装短信发送逻辑,通过阿里云SDK调用短信接口,处理发送结果。
  • CaptchaUtil:基于Hutool生成随机数字验证码。

CaptchaUtil示例:

/** * 随机验证码生成工具 */publicclassCaptchaUtil{/** * 生成随机验证码 * * @param length 验证码位数 * @return */publicstaticStringgetCaptcha(int length){// 自定义纯数字的验证码(随机4位数字,可重复)RandomGenerator randomGenerator =newRandomGenerator("0123456789", length);LineCaptcha lineCaptcha =cn.hutool.captcha.CaptchaUtil.createLineCaptcha(200,100); lineCaptcha.setGenerator(randomGenerator);// 重新生成code lineCaptcha.createCode();return lineCaptcha.getCode();}}

前后端交互接口

  • 请求/verification-code/send?phoneNumber=13199999999 GET
  • 响应
{"code":200,"data":true,"msg":""}

Controller层接口设计

  • 接收手机号参数,调用VerificationCodeService发送验证码,返回发送结果。

Service层接口设计

  • VerificationCodeService接口定义发送验证码和获取验证码的方法。
  • VerificationCodeServiceImpl实现逻辑:
    1. 校验手机号格式。
    2. 生成验证码。
    3. 调用SMSUtil发送短信。
    4. 将验证码存入Redis,设置60秒过期时间。

VerificationCodeServiceImpl阿里云短信验证码服务类示例:

/** * 阿里云短信验证码服务类 */@ServicepublicclassVerificationCodeServiceImplimplementsVerificationCodeService{@AutowiredprivateSMSUtil smsUtil;@AutowiredprivateRedisUtil redisUtil;/** * 发送并缓存验证码 * * @param phoneNumber */@OverridepublicvoidsendVerificationCode(String phoneNumber){// 校验手机号if(!RegexUtil.checkMobile(phoneNumber)){thrownewServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);}// 生成随机验证码 ,长度 4String code =CaptchaUtil.getCaptcha(Constants.CODE_LENGTH);// 发送验证码 smsUtil.sendVerifyCode(phoneNumber,code);// 缓存验证码// 一个手机号对应一个验证码,再次接收会覆盖 redisUtil.set(Constants.VERIFICATION_CODE_PREFIX+ phoneNumber ,code ,Constants.VERIFICATION_CODE_TIMEOUT);}}

3.2 Redis的配置与使用

> 核心工具类RedisUtil

基于StringRedisTemplate封装Redis操作,避免乱码问题,核心方法:

  • hasKey:判断键是否存在。
  • setExpire:设置键的过期时间。
  • getExpire:获取键的过期时间。
  • del:删除键。
  • get:获取字符串类型键的值。
  • set:设置字符串类型键的值(支持过期时间)。

RedisUtil示例:

/** * Redis 工具缓存 */@ConfigurationpublicclassRedisUtil{privatestaticfinalLogger logger =LoggerFactory.getLogger(RedisUtil.class);@AutowiredprivateStringRedisTemplate stringRedisTemplate;// --------- String ---------/** * 设置值 * * @param key * @param value * @return */publicbooleanset(String key,String value){try{ stringRedisTemplate.opsForValue().set(key,value);returntrue;}catch(Exception e){ logger.error("RedisUtil error,set({}, {})",key,value,e);returnfalse;}}/** * 设置值(可设置过期时间) * * @param key * @param value * @param time 单位:秒 * @return */publicbooleanset(String key,String value,Long time){try{ stringRedisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);returntrue;}catch(Exception e){ logger.error("RedisUtil error,set({}, {}, {})",key,value,time,e);returnfalse;}}/** * 获取值 * * @param key * @return */publicStringget(String key){try{returnStringUtils.hasText(key)? stringRedisTemplate.opsForValue().get(key):null;}catch(Exception e){ logger.error("RedisUtil error,get({})",key,e);returnnull;}}}

3.3 JWT

采用JWT(JSON Web Token)实现无状态登录,解决集群环境下Session共享问题。

> JWT 令牌介绍

JWT 全称: JSON Web Token,官网: https://jwt.io/

JSON Web Token (JWT) 是一个开放的行业标准 (RFC 7519),用于客户端和服务器之间传递安全可靠的信息。
其本质是一个 token,是一种紧凑的 URL 安全方法。

JWT 组成: JWT 由三部分组成,每部分中间使用点 (.) 分隔,例如:aaaaa.bbbbb.cccc

  • Header (头部)
    头部包括令牌的类型(即 JWT)及使用的哈希算法(如 HMAC SHA256 或 RSA)。

Signature (签名)
此部分用于防止 JWT 内容被篡改,确保安全性。

防止被篡改,而不是防止被解析。

JWT 之所以安全,就是因为最后的签名。JWT 当中任何一个字符被篡改,整个令牌都会校验失败。
就好比身份证,之所以能标识一个人的身份,是因为它不能被篡改,而不是因为内容加密。(任何人都可以看到身份证的信息,JWT 也是)

Payload (负载)
负载部分是存放有效信息的地方,里面是一些自定义内容。比如:

{"userId":"123","userName":"zhangsan"}

也可以存在 JWT 提供的现成字段,比如 exp (过期时间戳) 等。

此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。
> 核心工具类JWTUtil
  • genJwt:生成JWT令牌,包含自定义载荷(用户ID、身份)、签发时间、过期时间(1小时),使用HMAC SHA算法签名。
  • parseJWT:解析JWT令牌,验证签名合法性,返回载荷信息。
  • getUserIdFromToken:从令牌中提取用户ID。

JWTUtil示例:

publicclassJWTUtil{privatestaticfinalLogger logger =LoggerFactory.getLogger(JWTUtil.class);/** * 密钥:Base64编码的密钥 */privatestaticfinalStringSECRET="********";/** * 生成安全密钥:将一个Base64编码的密钥解码并创建一个HMAC SHA密钥。 */privatestaticfinalSecretKeySECRET_KEY=Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET));/** * 过期时间(单位: 毫秒) */privatestaticfinallongEXPIRATION=24*60*60*1000;/** * 生成密钥 * * @param claim {"id": 12, "name":"张山"} * @return */publicstaticStringgenJwt(Map<String,Object> claim){//签名算法String jwt =Jwts.builder().setClaims(claim)// 自定义内容(载荷).setIssuedAt(newDate())// 设置签发时间.setExpiration(newDate(System.currentTimeMillis()+EXPIRATION))// 设置过期时间.signWith(SECRET_KEY)// 签名算法.compact();return jwt;}/** * 验证密钥 */publicstaticClaimsparseJWT(String jwt){if(!StringUtils.hasLength(jwt)){returnnull;}// 创建解析器, 设置签名密钥JwtParserBuilder jwtParserBuilder =Jwts.parserBuilder().setSigningKey(SECRET_KEY);Claims claims =null;try{//解析token claims = jwtParserBuilder.build().parseClaimsJws(jwt).getBody();}catch(Exception e){// 签名验证失败 logger.error("解析令牌错误,jwt:{}", jwt, e);}return claims;}

3.4 管理员登录

支持“电话+密码”和“电话+验证码”两种登录方式,基于JWT返回令牌。

登录流程:

  1. 登陆页面把用户名密码提交给服务器。
  2. 服务器端验证用户名密码是否正确,如果正确,服务器生成令牌,下发给客户端。
  3. 客户端把令牌存储起来(比如Cookie、local storage等),后续请求时,把token发给服务器。
  4. 服务器对令牌进行校验,如果令牌正确,进行下一步操作。
在这里插入图片描述

时序图

在这里插入图片描述


前后端交互接口

  • 密码登录请求/password/login POST
{"loginName":"13199999999","password":"123456","mandatoryIdentity":"ADMIN"}
  • 密码登录响应
{"code":200,"data":{"token":"eyJhbGciOiJIUzI1NiJ9...","identity":"ADMIN"},"msg":""}
  • 验证码登录请求/message/login POST
{"loginMobile":"13199999999","verificationCode":"0475","mandatoryIdentity":"ADMIN"}
  • 验证码登录响应
{"code":200,"data":{"token":"eyJhbGciOiJIUzI1NiJ9...","identity":"ADMIN"},"msg":""}

Controller层接口设计

  • 提供两个登录接口,分别处理密码登录和验证码登录。
  • 接收登录参数,通过@Validated校验,调用UserService完成登录逻辑,返回UserLoginResult(含token和身份)。
  • 核心类:
    • UserPasswordLoginParam:密码登录入参(登录名、密码、强制身份)。
    • ShortMessageLoginParam:验证码登录入参(手机号、验证码、强制身份)。
    • UserLoginResult:登录出参(token、身份)。

Service层接口设计

  • UserService接口新增login方法,支持不同登录参数类型。
  • UserServiceImpl实现逻辑:
    1. 区分登录类型(密码登录/验证码登录)。
    2. 密码登录:校验登录名格式(邮箱/手机号)、查询用户信息、校验身份、校验密码,生成JWT令牌。
    3. 验证码登录:校验手机号格式、查询用户信息、校验身份、校验验证码(从Redis获取),生成JWT令牌。

Dao层接口设计

  • UserMapper新增接口:
    • selectByEmail:通过邮箱查询用户信息。
    • selectByPhoneNumber:通过手机号查询用户信息。

登录页面前端实现

  • 支持tab切换两种登录方式(密码登录/验证码登录)。
  • 使用jQuery Validate校验表单字段(手机号、密码/验证码必填)。
  • 验证码登录支持60秒倒计时重新获取。
  • 登录成功后,将token和身份存入localStorage,跳转管理员首页。

四、强制登录

通过拦截器实现非登录页面的强制登录校验。例如,当用户当前尚未登陆,访问抽奖页时希望自动跳转到登录页面。

4.1 前端处理

  • 所有AJAX请求通过ajaxSend方法在请求头中添加user_token(从localStorage获取),让后端取校验。
  • 若请求返回401(未登录),跳转至登录页(含iframe框架场景处理)。

4.2 后端处理

登录拦截器LoginInterceptor

实现HandlerInterceptor,重写preHandle方法:

  • 从请求头获取user_token
  • 调用JWTUtil解析令牌,校验合法性。
  • 令牌无效或不存在时,返回401状态码,拦截请求。

拦截器类示例:

/** * 登录拦截器 * */@Slf4j@ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{/** * 预处理,业务请求前调用 * * @param request * @param response * @param handler * @return * @throws Exception */@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{// 获取请求头String token = request.getHeader("user_token"); log.info("获取Token: {}",token); log.info("获取请求路径: {}",request.getRequestURI());// 令牌解析Claims claims =JWTUtil.parseJWT(token);if(null== claims){ log.error("解析JWT令牌失败!"); response.setStatus(401);returnfalse;} log.info("解析JWT令牌成功!");returntrue;}}

配置类AppConfig

实现WebMvcConfigurer,配置拦截器:

  • 拦截所有请求(/**)。
  • 排除登录、注册、验证码发送等接口,以及静态资源(HTML、CSS、JS等)。

拦截器排除路径示例:

/** * 拦截器排除路径 * */@ConfigurationpublicclassAppConfigimplementsWebMvcConfigurer{@AutowiredprivateLoginInterceptor loginInterceptor;privatefinalList<String> excludes =Arrays.asList("/**/*.html","/css/**","/js/**","/pic/**","/*.jpg","/*.png","/favicon.ico","/**/login","/register","/verification-code/send","/winning-records/show");@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){ registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(excludes);}}

五、用户管理

5.1 后台管理页面

  • 管理员首页(admin.html)通过iframe加载子页面,支持导航菜单切换。
  • 监听子页面消息,实现子页面向父页面传递跳转指令。
  • 提供退出登录功能,清除localStorage中的token,跳转登录页。

5.2 注册用户(管理员新增)

  • 前端逻辑与普通注册类似,但新增用户后直接跳转到用户列表页。
  • 通过URL参数jumpList控制跳转行为,admin参数控制用户身份。

5.3 人员列表展示

时序图

在这里插入图片描述


前后端交互接口

  • 请求/base-user/find-list GET(可选参数identity筛选身份)
  • 响应
{"code":200,"data":[{"userId":15,"userName":"郭靖","identity":"NORMAL"},{"userId":14,"userName":"王五","identity":"ADMIN"}],"msg":""}

Controller层接口设计

  • 接收身份筛选参数,调用UserService查询用户列表,转换为UserBaseInfoResult返回。

Service层接口设计

  • UserService新增findUserList方法,支持按身份筛选(null查询所有)。
  • UserServiceImpl实现逻辑:查询用户列表,转换为UserDTO,包含用户ID、姓名、邮箱、手机号、身份。

Dao层接口设计

  • UserMapper新增selectUserList方法,支持按身份查询用户列表,按ID降序排序。

人员列表页面前端实现

  • 页面加载时通过AJAX请求/base-user/find-list接口,获取用户列表。
  • 渲染表格展示用户ID、姓名、身份。
  • 未登录时跳转至登录页。

Read more

分享本周所学——三维重建算法3D Gaussian Splatting(3DGS)

分享本周所学——三维重建算法3D Gaussian Splatting(3DGS)

大家好,欢迎来到《分享本周所学》第十二期。本人是一名人工智能初学者,刚刚读完大二。前几天自学了一下3D Gaussian Splatting(3DGS),觉得非常有意思。写这篇文章主要是因为网上大部分关于3DGS的文章都比较晦涩,我自己学的时候也是翻阅了大量的论文博客视频,所以想结合自己的学习过程,写一篇让所有人都能看懂的文章。我不会假设你有任何机器学习或者数学的基础知识,即使你只是刚刚接触人工智能领域的小白,我也会让你看懂。如果你觉得我有任何一个地方(即使只是一个标点符号)写的不对、不好或者不清楚,麻烦你在评论区指出来,这会对我有极大的帮助。         这里先放一下原论文: https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gaussian_splatting_low.pdfhttps://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gaussian_splatting_low.pdf         同时这篇文章参考了一个B站

By Ne0inhk

优选算法——前缀和

👇作者其它专栏 《数据结构与算法》《算法》《C++起始之路》 前缀和相关题解 1.前缀和 算法思路: a.先预处理出来一个【前缀和】数组:         用dp[i]表示:[1,i]区间内所有元素的和,那么dp[i-1]里面存的就是[1,i-1]区间内所有元素的和,那么:可得到递推公式:dp[i]=dp[i-1]+arr[i]; b.使用前缀和数组,【快速】求出【某一个区间内】所有元素的和:         当访问的区间是[l,r]时:区间内所有元素的和为:dp[r]-dp[l-r]。 #include <

By Ne0inhk
第19章—数据结构篇:深入quicklist核心方法

第19章—数据结构篇:深入quicklist核心方法

在上一讲中,我们重点介绍了 quicklist 的核心结构,一起分析了 quicklist 里面关键的结构体定义以及关键的配置含义。本节我们将继续重点介绍 quicklist 的核心函数,主要包括:插入数据、弹出数据以及查询数据这三方面的函数实现。 创建 quicklist 首先来看创建 quicklist 实例的 quicklistNew() 函数,可以用 CLoin 调用链的视图看一下它都调了哪些函数。 可以看到,quicklistNew() 函数先会调用 quicklistCreate() 创建一个空 quicklist 实例,里面就是走 malloc 分配内存,然后通过 quicklistSetCompressDeth() 和 quicklistSetFill() 函数来初始化 compress 和 fill 两个字段。 插入数据 向 quicklist 插入一个元素的入口函数是 quicklistPush() 函数,其核心代码片段如下: voidquicklistPush(

By Ne0inhk
【狂热算法篇】完全背包异次元冒险:容量魔法觉醒,价值风暴来袭!

【狂热算法篇】完全背包异次元冒险:容量魔法觉醒,价值风暴来袭!

欢迎拜访:羑悻的小杀马特.-ZEEKLOG博客 本篇主题:轻轻松松拿捏完全背包问题呀!!! 制作日期:2026.03.04 隶属专栏:美妙的算法世界 目录 一·问题定义: 二·具体问题演示:  三·动态规划解答完全背包: 3.1非装满状态: 3.1.1状态定义: 3.1.2状态转移方程:   3.1.3初始化: 3.1.4返回值: 3.1.5填充dp表: 3.1.6非装满状态代码总结: 3.1.7非装满状态滚动数组降维优化:  3.2装满状态: 3.2.1状态定义: 3.2.2状态转移方程:  3.

By Ne0inhk