1、设计数据库

2、写基本框架

entity、controller、service、exception、utils、mapper

mapper层:

生成了一系列的CRUD方法

工具类:线程安全的日期工具类、 ​​参数校验工具类​

线程安全的日期工具类​​:主要用于 ​​日期格式化(format)和解析(parse)​​,并解决了 SimpleDateFormat 的线程安全问题​

参数校验工具类:主要用于检查对象参数是否满足“至少有一个非空字段”的条件,并提供了一些字符串辅助方法(如首字母大写、判空)

3、登录验证码校验

这段代码是一个 ​​验证码生成与校验​​ 的接口,主要用于生成图片验证码并存储到 HttpSession 中,以便后续验证用户输入的验证码是否正确 

  1. ​生成图片验证码​

    • 使用 CreateImageCode 类创建一个 ​​130x38 像素​​ 的验证码图片,包含 ​​5 个随机字符​​,干扰线数量为 ​​10​​。
    • 生成的验证码字符串存储在 code 变量中。
  2. ​设置 HTTP 响应头​

    • 禁用缓存,确保每次请求都生成新的验证码:
      response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0);
    • 设置响应类型为 image/jpeg,表示返回的是 JPEG 图片:
      response.setContentType("image/jpeg");
  3. ​存储验证码到 Session​

    • 根据 type 参数决定验证码的用途:
      • type=1:普通验证码(如登录验证),存储到 Constants.CHECK_CODE_KEY
      • 其他情况(如邮箱验证码),存储到 Constants.CHECK_CODE_KEY_EMAIL
  4. ​输出验证码图片​

    • 调用 vCode.write(response.getOutputStream()) 将生成的图片写入 HTTP 响应流,返回给前端显示。

为了辅助上述方法,因此创建了一个图形验证码生成工具类​​(CreateImageCode

package com.cjl.entity.dto;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;public class CreateImageCode {//图片宽度private int width=160;//图片高度private int height=40;//验证码字符个数private int codeCount=4;//验证码干扰线数private int lineCount=20;//验证码private String code=null;//验证码图片bufferprivate BufferedImage  buffImg=null;Random random=new Random();public CreateImageCode(){createCode();}public CreateImageCode(int width, int height, int codeCount, int lineCount){this.width=width;this.height=height;this.codeCount=codeCount;this.lineCount=lineCount;createCode();}public CreateImageCode(int width, int height, int lineCount){this.width=width;this.height=height;this.lineCount=lineCount;createCode();}public CreateImageCode(int width, int height){this.width=width;this.height=height;createCode();}//生成图片private void createCode(){int fontWidth=width/codeCount; //字体宽度int fontHeight=height-5; //字体高度int codeY=height-8; //验证码y坐标//创建图像bufferbuffImg=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);Graphics g=buffImg.getGraphics();//设置背景颜色g.setColor(getRandColor(200,250));g.fillRect(0,0,width,height);//设置字体Font font=new Font("Fixedsys",Font.BOLD,fontHeight);g.setFont(font);//设置干扰线for(int i=0;i<lineCount;i++){int xs=random.nextInt(width);int ys=random.nextInt(height);int xe=xs+random.nextInt(width);int ye=ys+random.nextInt(height);g.setColor(getRandColor(1,255));g.drawLine(xs,ys,xe,ye);}//添加噪点float yawpRate=0.01f; //噪声率int area=(int)(yawpRate*width*height); //像素个数for(int i=0;i<area;i++){int x=random.nextInt(width);int y=random.nextInt(height);buffImg.setRGB(x,y,random.nextInt(255));}String str1=RandomStr(codeCount); //随机生成验证码this.code=str1;for(int i=0;i<codeCount;i++){String strRand=str1.substring(i,i+1);g.setColor(getRandColor(1,255));//g.drawString(a,x,y);//a是要画出来的东西,x,y是坐标,基于要画的东西最左侧字符的基线g.drawString(strRand,i*fontWidth+3,codeY);}}//得到随机字符private String RandomStr(int n){String str1="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";String str2="";int len=str1.length()-1;double r;for(int i=0;i<n;i++){r=(Math.random())*len;str2=str2+str1.charAt((int)r);}return str2;}//得到随机颜色private Color getRandColor(int fc,int bc){if(fc>255) fc=255;if(bc>255) bc=255;int r=fc+random.nextInt(bc-fc);int g=fc+random.nextInt(bc-fc);int b=fc+random.nextInt(bc-fc);return new Color(r,g,b);}//画干扰线private void shearY(Graphics g,int w1,int h1,Color color){int period=random.nextInt(40)+10; //50; //振幅boolean borderGap=true;int frames=20;int phase=7;for(int i=0;i<w1;i++){double d=(double)(period>>1)* Math.sin((double)i/period+ (6.2831853071795862D* (double)phase/frames));g.copyArea(i,0,1,h1,0,(int)d);if(borderGap){g.setColor(color);g.drawLine(i, (int)d, i, 0);g.drawLine(i, (int)d+h1, i, h1);}}}public void write(OutputStream sos) throws IOException {ImageIO.write(buffImg,"png",sos);sos.close();}public BufferedImage   getBuffImg(){return buffImg;}public String getCode(){return code.toLowerCase();}
}

最终达到的效果:

4、创建邮箱数据库

依旧通过java生成器生成,不过,第一次遇见这种:

	<!-- 通用查询结果列--><sql id="base_column_list">e.email,e.code,e.creat_time,e.status</sql><sql id="base_condition_filed"><if test="query.email != null and query.email!=''">and  e.email = #{query.email}</if><if test="query.code != null and query.code!=''">and  e.code = #{query.code}</if><if test="query.creatTime != null and query.creatTime!=''"><![CDATA[ and  e.creat_time=str_to_date(#{query.creatTime}, '%Y-%m-%d') ]]></if><if test="query.status != null">and  e.status = #{query.status}</if></sql><!-- 通用条件列--><sql id="base_condition"><where><include refid="base_condition_filed" /></where></sql><!-- 通用查询条件列--><sql id="query_condition"><where><include refid="base_condition_filed" /><if test="query.emailFuzzy!= null  and query.emailFuzzy!=''">and  e.email like concat('%', #{query.emailFuzzy}, '%')</if><if test="query.codeFuzzy!= null  and query.codeFuzzy!=''">and  e.code like concat('%', #{query.codeFuzzy}, '%')</if><if test="query.creatTimeStart!= null and query.creatTimeStart!=''"><![CDATA[ and  e.creat_time>=str_to_date(#{query.creatTimeStart}, '%Y-%m-%d') ]]></if><if test="query.creatTimeEnd!= null and query.creatTimeEnd!=''"><![CDATA[ and  e.creat_time< date_sub(str_to_date(#{query.creatTimeEnd},'%Y-%m-%d'),interval -1 day) ]]></if></where></sql>

 <sql id="base_column_list"> ???我很好奇了这是什么

通过 <sql> 标签定义可重用的 SQL 片段,并通过 <include> 标签引用这些片段,从而提高代码的复用性和可维护性。

当 MyBatis 启动时,会解析这些 <sql> 片段并存储在内存中,形成可重用的 SQL 模板。

1. ​​片段注册​
  • base_column_list → 字段列表模板
  • base_condition_filed → 基础条件模板
  • base_condition → 完整 WHERE 条件(直接引用 base_condition_filed
  • query_condition → 扩展 WHERE 条件(引用 base_condition_filed 并追加模糊/范围查询)
2.逻辑关系

这段代码的核心思想就是 ​​将常用的 SQL 片段拆解成可复用的模块​​,通过 MyBatis 的 <sql> 和 <include> 机制实现 ​​逻辑复用​​ 和 ​​动态拼接​​。但它不仅仅是简单的“代码片段集合”,而是一种 ​​模块化 SQL 设计模式​

可能跟我一样第一次遇见的同学看见这里就有点蒙圈了,没事,我们来举例 一个简单的代码来理解:

1. 定义可复用的 SQL 片段(乐高积木块)
<!-- 基础车体(相当于字段列表) -->
<sql id="base_car_body">id, brand, model, color
</sql><!-- 基础轮子(相当于基础条件) -->
<sql id="base_wheels"><if test="wheelSize != null">AND wheel_size = #{wheelSize}</if>
</sql>
2.组装小车(简单查询)
<select id="selectBasicCar" resultType="Car">SELECT <include refid="base_car_body"/>  <!-- 插入车体 -->FROM cars<where><include refid="base_wheels"/>    <!-- 插入轮子条件 --></where>
</select>
3.生成的SQL​​(当 wheelSize=18 时):
SELECT id, brand, model, color
FROM cars
WHERE wheel_size = 18

这样是不是就好理解一点了!!

5、实现发送邮箱验证码接口 

就在主播写这个接口的发送邮箱验证的时候,命名mappers互相跳转都没有问题,困扰我一个多小时,结果发现没有在​​属性配置文件添加mybatis.mapper-locations=classpath:mappers/*.xml,因此虽然我可以互相跳转,但是代码自己找不到sql映射,属所以说写代码还是要规范

言归正传,

当我们写完这个接口,我们要考虑很多因素,首先就是用户输入的验证码是不是正确的,要根据之前定义的checkCode方法来查看,其次就要考虑status的问题,如果账户已经被注册了就没有必要了要进行判断,不仅如此,当用户多次点击发送的时候,我们只需要禁用之前的验证码,这个如何实现?

答案是在service层

@Update("update email_code set status=1 where email=#{email} and status=0")

为什么这样就可以?我们来分析,当检测到用户未注册的时候,代码的流程走到这里来的时候,此时他的status就会变成1,也就是说每次发送新验证码前,会先执行 disableEmailCode 方法,将所有该邮箱未使用的验证码(status=0)标记为被使用了已禁用(status=1)。因此能保证最晚(最新)的那一个验证码才会生效

我们现在来发送邮件, ​​JavaMailSender​​(Spring框架提供的邮件发送工具)来创建并发送一封简单的电子邮件:

流程:

1.入口​​:调用sendMailCode(email, code)方法,传入收件人邮箱和验证码

2.获取系统邮件模板

SysSettingDto sysSettingDto = redisComponent.getSysSetting();

设置邮件发送的格式: 

3.构建邮件内容
  • ​标题处理​​:直接使用系统配置的标题

    helper.setSubject(sysSettingDto.getRegisterMailTitle());

  • ​内容格式化​​:将验证码插入模板
    String.format("您好,您的邮箱验证码为:%s,15分钟有效", "A1B2C3")

提问:有人就好奇了,redis一开始是空的哪来的模板??

答案是 

1. 尝试从Redis读取(此时返回null),发现为空时,创建默认配置

最后保存到Redis(无过期时间) 

整个发邮件的大致过程就是这样,最后用户会收到:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/905302.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/905302.shtml
英文地址,请注明出处:http://en.pswp.cn/news/905302.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

langchain学习

无门槛免费申请OpenAI ChatGPT API搭建自己的ChatGPT聊天工具 https://nuowa.net/872 基本概念 LangChain 能解决大模型的两个痛点&#xff0c;包括模型接口复杂、输入长度受限离不开自己精心设计的模块。根据LangChain 的最新文档&#xff0c;目前在 LangChain 中一共有六大…

Protobuf工具

#region 知识点一 什么是 Protobuf //Protobuf 全称是 protocol - buffers&#xff08;协议缓冲区&#xff09; // 是谷歌提供给开发者的一个开源的协议生成工具 // 它的主要工作原理和我们之前做的自定义协议工具类似 // 只不过它更加的完善&…

zst-2001 上午题-历年真题 软件工程(38个内容)

CMM 软件工程 - 第1题 b 软件工程 - 第2题 c 软件工程 - 第3题 c 软件工程 - 第4题 b 软件工程 - 第5题 b CMMI 软件工程 - 第6题 0.未完成&#xff1a;未执行未得到目标。1.已执行&#xff1a;输入-输出实现支持2.已管理&#xff1a;过程制度化&#x…

软考架构师考试-UML图总结

考点 选择题 2-4分 案例分析0~1题和面向对象结合考察&#xff0c;前几年固定一题。近3次考试没有出现。但还是有可能考。 UML图概述 1.用例图&#xff1a;描述系统功能需求和用户&#xff08;参与者&#xff09;与系统之间的交互关系&#xff0c;聚焦于“做什么”。 2.类图&…

数据结构(七)——图

一、图的定义与基本术语 1.图的定义 图G由顶点集V和边集E组成&#xff0c;记为G(V,E)&#xff0c;其中V(G)表示图G中顶点的有限非空集&#xff1b;E(G)表示图G中顶点之间的关系&#xff08;边&#xff09;的集合 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&…

Android7 Input(六)InputChannel

概述: 本文讲述Android Input输入框架中 InputChannel的功能。从前面的讲述&#xff0c;我们知道input系统服务最终将输入事件写入了InputChannel&#xff0c;而input属于system_server进程&#xff0c;App属于另外一个进程&#xff0c;当Input系统服务想要把事件传递给App进行…

【 Redis | 实战篇 秒杀实现 】

目录 前言&#xff1a; 1.全局ID生成器 2.秒杀优惠券 2.1.秒杀优惠券的基本实现 2.2.超卖问题 2.3.解决超卖问题的方案 2.4.基于乐观锁来解决超卖问题 3.秒杀一人一单 3.1.秒杀一人一单的基本实现 3.2.单机模式下的线程安全问题 3.3.集群模式下的线程安全问题 前言&…

如何用URDF文件构建机械手模型并与MoveIt集成

机械手URDF文件的编写 我们用urdf文件来描述我们的机械手的外观以及物理性能。这里为了简便&#xff0c;就只用了基本的圆柱、立方体了。追求美观的朋友&#xff0c;还可以用dae文件来描述机械手的外形。 import re def remove_comments(text):pattern r<!--(.*?)-->…

《构建社交应用的安全结界:双框架对接审核API的底层逻辑与实践》

用户生成内容如潮水般涌来。从日常的生活分享&#xff0c;到激烈的观点碰撞&#xff0c;这些内容赋予社交应用活力&#xff0c;也带来管理难题。虚假信息、暴力言论、侵权内容等不良信息&#xff0c;如同潜藏的暗礁&#xff0c;威胁着社交平台的健康生态。内容审核机制&#xf…

39:分类器流程

第一步 创建支持向量机分类器 create_class_svm (7, rbf, KernelParam, Nu, |ClassNames|, one-versus-one, principal_components, 5, SVMHandle) 第二步 添加样本到分类器里 for ClassNumber : 0 to |ClassNames| - 1 by 1 *列出目录下的所有文件 list_files (ReadPath…

LangChain对话链:打造智能多轮对话机器人

LangChain对话链:打造智能多轮对话机器人 目录 LangChain对话链:打造智能多轮对话机器人ConversationChain 是什么核心功能与特点基本用法示例内存机制自定义提示词应用场景与其他链的结合`SequentialChain` 是什么![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0…

el-select 结合 el-tree:树形下拉数据

一、单选 <template><div class"selectTree-wapper"><el-selectv-model"selectValue"placeholder"请选择"popper-class"custom-el-select-class"ref"selectRef"clearableclear"clearHandle">&…

BFS算法篇——从晨曦到星辰,BFS算法在多源最短路径问题中的诗意航行(下)

文章目录 引言一、01矩阵1.1 题目链接&#xff1a;https://leetcode.cn/problems/01-matrix/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 二、飞地的数量2.1 题目链接&#xff1a;https://leetcode.cn/problems/number-of-enclaves…

Leetcode (力扣)做题记录 hot100(49,136,169,20)

力扣第49题&#xff1a;字母异位词分组 49. 字母异位词分组 - 力扣&#xff08;LeetCode&#xff09; 遍历数组&#xff0c;将每一个字符串变成char数组 然后排序&#xff0c;如果map里面有则将他的值返回来&#xff08;key是排序好的字符串&#xff09; class Solution {pu…

【自学30天掌握AI开发】第1天 - 人工智能与大语言模型基础

自学30天掌握AI开发 - 第1天 &#x1f4c6; 日期和主题 日期&#xff1a;第1天 主题&#xff1a;人工智能与大语言模型基础 &#x1f3af; 学习目标 了解人工智能的发展历史和基本概念掌握大语言模型的基本原理和工作机制区分不同类型的AI模型及其特点理解AI在当前社会中的…

WebRTC 源码原生端Demo入门-1

1、概述 我的代码是比较新的&#xff0c;基于webrtc源码仓库的main分支的&#xff0c;在windows下把源码仓库下载好了后&#xff0c;用visual stdio 2022打开进行编译调试src/examples/peerconnection_client测试项目,主要是跑通这个demo来入手和调试&#xff0c;纯看代码很难…

【LeetCode】删除排序数组中的重复项 II

题目 链接 思路 双指针 我好聪明啊&#xff0c;自己想出了这个双指针的办法&#xff0c;哈哈哈哈哈哈哈&#xff0c;太高兴了 代码 class Solution(object):def removeDuplicates(self, nums):""":type nums: List[int]:rtype: int"""nlen…

通义千问席卷日本!开源界“卷王”阿里通义千问成为日本AI发展新基石

据日本经济新闻&#xff08;NIKKEI&#xff09;报道&#xff0c;通义千问已成为日本AI开发的新基础&#xff0c;其影响力正逐步扩大&#xff0c;深刻改变着日本AI产业的格局。 同时&#xff0c;日本经济新闻将通义千问Qwen2.5-Max列为全球AI模型综合评测第六名&#xff0c;不仅…

第J7周:对于ResNeXt-50算法的思考

目录 思考 一、代码功能分析 1. 构建 shortcut 分支&#xff08;残差连接的旁路&#xff09; 2. 主路径的第一层卷积&#xff08;11&#xff09; 4. 主路径的第三层卷积&#xff08;11&#xff09; 5. 残差连接 激活函数 二、问题分析总结&#xff1a;残差结构中通道数不一致的…

如何解决Jmeter中的乱码问题?

在 JMeter 中遇到乱码问题通常是由于字符编码不一致导致的&#xff0c;常见于 HTTP 请求响应、参数化文件读取、报告生成等场景。以下是系统化的解决方案&#xff1a; 1. HTTP 请求响应乱码 原因&#xff1a; 服务器返回的字符编码&#xff08;如UTF-8、GBK&#xff09;与 J…