添加好友功能实现文档
目标
实现一个添加好友的功能,具体流程如下:
- 被加好友根据自己的userId获取手机号和当前时间戳生成邀请码,并发送给另外一个人。
- 另外一个人根据要加好友的手机号和邀请码添加好友。
逻辑要求
- 自己不能关注自己,也不能关注不存在的人。
- 不能关注已经关注过的人。
- 邀请码五分钟内有效。
开发思路
1. 生成邀请码
首先,被加好友需要生成一个邀请码。邀请码的生成需要包含以下信息:
- 被加好友的手机号
- 当前时间戳
生成邀请码的步骤如下:
- 获取当前时间戳。
- 将手机号、一个固定的字符串(如"encode123")和时间戳拼接成一个字符串。
- 使用SHA-256算法对拼接后的字符串进行哈希处理。
- 将哈希结果编码为Base64字符串。
- 将Base64字符串和时间戳拼接成最终的邀请码。
2. 检查邀请码
当用户尝试添加好友时,需要对邀请码进行验证。验证步骤如下:
- 将邀请码拆分为哈希部分和时间戳部分。
- 检查时间戳是否在有效时间内(五分钟)。
- 重新生成哈希并与邀请码中的哈希部分进行比较。
- 检查用户是否尝试关注自己。
- 检查用户是否尝试关注不存在的用户。
- 检查用户是否已经关注过该好友。
3. 添加好友
在邀请码验证通过后,将用户与被加好友的关系存储到数据库中。
详细实现
生成邀请码的代码
long timestamp = System.currentTimeMillis();
String combinedString = param.get("phone").toString() + "encode123" + timestamp;
// 使用SHA-256生成哈希
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(combinedString.getBytes(StandardCharsets.UTF_8));
// 将哈希编码为Base64字符串
String inviteCode = Base64.getUrlEncoder().encodeToString(hash);
return inviteCode + "" + timestamp;
代码解析
long timestamp = System.currentTimeMillis();
获取当前时间戳。String combinedString = param.get("phone").toString() + "encode123" + timestamp;
将手机号、固定字符串和时间戳拼接成一个字符串。MessageDigest digest = MessageDigest.getInstance("SHA-256");
获取SHA-256算法的实例。byte[] hash = digest.digest(combinedString.getBytes(StandardCharsets.UTF_8));
对拼接后的字符串进行哈希处理。String inviteCode = Base64.getUrlEncoder().encodeToString(hash);
将哈希结果编码为Base64字符串。return inviteCode + "" + timestamp;
将Base64字符串和时间戳拼接成最终的邀请码。
检查邀请码的代码
@Override
public String checkFollowUser(Map param) throws NoSuchAlgorithmException {
String inviteCode = param.get("invitedCode").toString();
String[] parts = inviteCode.split("");
if (parts.length != 2) {
return "错误的邀请码";
} else {
String code = parts[0];
long timestamp = Long.parseLong(parts[1]);
// 检查是否在有效时间内
long currentTime = System.currentTimeMillis();
if (currentTime - timestamp > 5 * 60 * 1000) {
return "邀请码已过期";
} else {
// 重新生成哈希并比较
String expectedCode = generateInviteCode(param.get("phone").toString(), timestamp).split("")[0];
if (expectedCode.equals(code)) {
// 自己不能关注自己,也不能关注不存在的人
// 也不能关注关注过的人
long countMe = userDao.checkFollowUserMe(param);
if (countMe > 0) {
return "不能关注自己";
} else {
long countExist = userDao.checkFollowUser(param);
if (countExist == 0) {
return "关注的用户不存在";
} else {
long count = userDao.checkAlreadyFollowUser(param);
if (count > 0) {
return "已经关注该用户";
} else {
return null;
}
}
}
} else {
return "无效的邀请码,想关注的用户与对应邀请码不匹配";
}
}
}
}
代码解析
String inviteCode = param.get("invitedCode").toString();
获取邀请码。String[] parts = inviteCode.split("");
将邀请码拆分为哈希部分和时间戳部分。if (parts.length != 2) { return "错误的邀请码"; }
检查邀请码格式是否正确。long timestamp = Long.parseLong(parts[1]);
获取时间戳。long currentTime = System.currentTimeMillis();
获取当前时间。if (currentTime - timestamp > 5 * 60 * 1000) { return "邀请码已过期"; }
检查时间戳是否在有效时间内。String expectedCode = generateInviteCode(param.get("phone").toString(), timestamp).split("")[0];
重新生成哈希。if (expectedCode.equals(code)) { ... } else { return "无效的邀请码,想关注的用户与对应邀请码不匹配"; }
比较哈希部分。long countMe = userDao.checkFollowUserMe(param); if (countMe > 0) { return "不能关注自己"; }
检查用户是否尝试关注自己。long countExist = userDao.checkFollowUser(param); if (countExist == 0) { return "关注的用户不存在"; }
检查用户是否尝试关注不存在的用户。long count = userDao.checkAlreadyFollowUser(param); if (count > 0) { return "已经关注该用户"; }
检查用户是否已经关注过该好友。
添加好友的代码
<!-- 关注用户 -->
<insert id="followUser" parameterType="Map">
INSERT INTO user_follow (user_id, follow_user_id)
SELECT
#{userId} AS user_id,
ui.user_id AS follow_user_id
FROM
user_info ui
WHERE
ui.phone = #{phone};
</insert>
代码解析
<insert id="followUser" parameterType="Map">
定义一个插入操作,参数类型为Map。INSERT INTO user_follow (user_id, follow_user_id)
插入数据到user_follow
表。SELECT #{userId} AS user_id, ui.user_id AS follow_user_id FROM user_info ui WHERE ui.phone = #{phone};
根据手机号查询被加好友的userId,并将用户与被加好友的关系存储到数据库中。
实现添加好友功能的好处
安全性
1. 使用SHA-256和Base64编码生成邀请码
- 不可逆性:SHA-256是一种不可逆的哈希算法,确保了邀请码的安全性,即使被截获也无法轻易破解。
- 唯一性:通过手机号和时间戳生成的哈希值保证了每个邀请码的唯一性,避免了重复和冲突。
- 编码安全:Base64编码确保了哈希值在传输过程中不会因为特殊字符而导致问题。
2. 时间戳验证
- 时效性:邀请码包含生成时的时间戳,并在验证时检查是否在五分钟内有效,防止邀请码被长期滥用。
- 防止重放攻击:时间戳验证可以有效防止重放攻击,即使邀请码被截获,也只能在短时间内使用。
功能性
1. 防止自我关注
- 逻辑检查:在验证邀请码时,检查用户是否尝试关注自己,避免了无意义的操作。
2. 防止关注不存在的用户
- 用户存在性检查:在验证邀请码时,检查被加好友是否存在于系统中,确保关注操作的有效性。
3. 防止重复关注
- 重复关注检查:在验证邀请码时,检查用户是否已经关注过该好友,避免重复关注。
用户体验
1. 简单易用
- 邀请码生成和验证:用户只需生成邀请码并发送给好友,好友在添加时输入邀请码即可完成关注操作,流程简单明了。
2. 快速响应
- 即时验证:通过时间戳和哈希值的验证,可以快速判断邀请码的有效性,提升用户体验。
可维护性
1. 清晰的代码结构
- 模块化设计:生成邀请码、验证邀请码和添加好友的逻辑分开实现,代码结构清晰,易于维护和扩展。
- 注释和文档:详细的注释和文档说明了每个步骤的实现逻辑,便于后续开发人员理解和维护。
2. 数据库操作的封装
- SQL语句封装:将数据库操作封装在XML文件中,便于修改和管理。
扩展性
1. 灵活的参数配置
- 参数化设计:通过Map传递参数,便于后续扩展和修改,如增加新的验证逻辑或参数。
2. 可扩展的验证逻辑
- 逻辑检查:当前实现了防止自我关注、关注不存在的用户和重复关注的逻辑,后续可以根据需求增加更多的验证逻辑,如黑名单检查等。
膳逸
Base URLs:
Authentication
shanyi-api
POST 关注用户
POST /user/followUser
前端提交userId与要关注的用户的uuid以及邀请码,后端实现关注用户。
要注意1、自己不能关注自己,也不能关注不存在的人
2、还不能关注关注过的人
3、邀请码设计
3、邀请码设计
Body 请求参数
{
"userId": 5,
"phone": "15553581028",
"invitedCode": "1lNxOsO0-oFJRAMcUL9Q3dM7HaF2WjGl-nMO3tJgY2c=1719044274146"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|---|---|---|---|
token | header | string | 否 | none |
body | body | object | 否 | none |
» userId | body | integer | 是 | 我的id |
» phone | body | string | 是 | 要关注的那个人的uuid |
» invitedCode | body | string | 是 | 邀请码 |
返回示例
成功
{
"userId": 5,
"phone": "15935883218",
"invitedCode": "c2WCNNcuXqTryIGz_QD8P5kFlsDV1Wzw5bqYeodkKr4=1718474341408"
}
{
"msg": "success",
"code": 200
}
不能关注自己
{
"userId": 5,
"phone": "13621403803",
"invitedCode": "4FwIJAffAcKNIgsh8eQv7VKlVasKzagtD4wkTZxQoMs=1719044129755"
}
{
"msg": "不能关注自己",
"code": 500
}
已经关注该用户
{
"userId": 5,
"phone": "15553581028",
"invitedCode": "1lNxOsO0-oFJRAMcUL9Q3dM7HaF2WjGl-nMO3tJgY2c=1719044274146"
}
{
"msg": "已经关注该用户",
"code": 500
}
错误的邀请码
{
"userId": 5,
"phone": "15553581028",
"invitedCode": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
{
"msg": "错误的邀请码",
"code": 500
}
邀请码已过期
{
"userId": 5,
"phone": "15553581028",
"invitedCode": "1lNxOsO0-oFJRAMcUL9Q3dM7HaF2WjGl-nMO3tJgY2c=1719044274146"
}
{
"msg": "邀请码已过期",
"code": 500
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 成功 | Inline |
返回数据结构
状态码 200
名称 | 类型 | 必选 | 约束 | 中文名 | 说明 |
---|---|---|---|---|---|
» code | integer | true | none | none | |
» msg | string | true | none | none |
POST 生成邀请码
POST /user/generatingInvitedCode
根据userId(查找uuid)和当前时间生成5分钟有效的邀请码
Body 请求参数
{
"userId": "4"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|---|---|---|---|
token | header | string | 否 | none |
body | body | object | 否 | none |
» userId | body | string | 是 | none |
返回示例
成功
{
"msg": "success",
"result": "DjAUnHoBHUaFmenKFJrGmNv-OHFSKzvqOXCW4xHMb1I=----1716435888948",
"code": 200
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 成功 | Inline |
返回数据结构
状态码 200
名称 | 类型 | 必选 | 约束 | 中文名 | 说明 |
---|---|---|---|---|---|
» code | integer | true | none | none | |
» msg | string | true | none | none | |
» result | string | true | none | none |
结论
通过上述代码实现了添加好友的功能,确保了用户不能关注自己、不能关注不存在的人以及不能重复关注同一个人。同时,邀请码在五分钟内有效,保证了安全性和时效性。
关键点总结
- 邀请码生成:使用SHA-256算法和Base64编码生成唯一的邀请码。
- 邀请码验证:确保邀请码在有效时间内,并且与被加好友的手机号匹配。
- 逻辑检查:防止用户关注自己、关注不存在的用户以及重复关注同一个用户。
- 数据库操作:将用户与被加好友的关系存储到数据库中。
通过以上步骤,完整地实现了添加好友的功能,满足了所有的逻辑要求。