diff --git a/snailmall-user-service/pom.xml b/snailmall-user-service/pom.xml
index 24a242b..49b6b36 100644
--- a/snailmall-user-service/pom.xml
+++ b/snailmall-user-service/pom.xml
@@ -104,6 +104,11 @@
io.springfox
springfox-swagger-ui
+
+ backport-util-concurrent
+ backport-util-concurrent
+ 3.1
+
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
index 12daca4..3ffdca5 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
@@ -8,119 +8,153 @@ import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
-
import java.io.IOException;
import java.text.SimpleDateFormat;
/**
- * jackson的序列化和反序列化
+ * 该类 `JsonUtil` 是用于处理 `Jackson` 库的序列化和反序列化操作的工具类。
+ * 它通过配置 `ObjectMapper` 对象的各种属性,来定制序列化和反序列化的行为,例如控制哪些字段参与序列化、如何处理日期格式、如何处理未知属性等情况,
+ * 同时提供了多个重载的方法,分别用于将对象序列化为字符串(普通格式和美化格式)以及将字符串反序列化为不同类型的对象(简单对象、复杂对象等),方便在项目中统一进行 `JSON` 数据与 `Java` 对象之间的转换操作,
+ * 并且在方法中对可能出现的异常情况进行了日志记录,并返回合适的默认值(如 `null`),以增强工具类的健壮性。
+ *
+ * @Slf4j 注解用于生成一个名为 `log` 的日志记录对象,便于在类中记录操作相关的日志信息,特别是在序列化和反序列化出现问题(如 `IOException` 异常抛出时)记录错误情况,方便后续排查问题。
*/
@Slf4j
public class JsonUtil {
+ // 创建一个静态的 `ObjectMapper` 对象,它是 `Jackson` 库中用于进行序列化和反序列化操作的核心类,后续所有的配置和转换操作都基于这个对象来进行。
private static ObjectMapper objectMapper = new ObjectMapper();
static {
- //所有字段都列入进行转换
+ // 以下是对 `ObjectMapper` 对象进行一系列的配置操作,通过静态代码块在类加载时执行这些配置,确保整个应用程序中使用的 `ObjectMapper` 具有统一的序列化和反序列化行为。
+
+ // 设置序列化时包含所有字段,即无论字段是否为 `null`,都将其列入进行转换,通过指定 `JsonSerialize.Inclusion.ALWAYS`,使得在将 `Java` 对象转换为 `JSON` 字符串时,所有的对象属性都会参与序列化过程。
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
- //取消默认转换timestamp形式
- objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
- //忽略空bean转json的错误
- objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);
- //统一时间的格式
+
+ // 取消默认将日期转换为时间戳形式的行为,配置 `SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS` 为 `false`,
+ // 这样在序列化包含日期类型的对象时,会按照后续配置的日期格式(如通过 `setDateFormat` 方法指定的格式)进行序列化,而不是转换为时间戳数值。
+ objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
+
+ // 忽略空 `bean`(即没有属性值的 `Java` 对象)转 `JSON` 的错误,配置 `SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS` 为 `false`,
+ // 当尝试序列化一个没有属性值的对象时,不会抛出异常,而是正常返回相应的 `JSON` 表示(可能为空对象的 `JSON` 格式),提高了序列化操作的容错性。
+ objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
+
+ // 统一时间的格式,通过创建 `SimpleDateFormat` 对象并传入 `DateTimeUtil.STANDARD_FORMAT`(这应该是项目中定义的统一的日期时间格式字符串,例如 "yyyy-MM-dd HH:mm:ss"),
+ // 然后使用 `setDateFormat` 方法设置到 `ObjectMapper` 中,使得在序列化和反序列化日期类型数据时,都按照这个统一格式进行处理,保证日期格式的一致性。
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
- //忽略json存在属性,但是java对象不存在属性的错误
- objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
+
+ // 忽略 `JSON` 中存在属性,但对应的 `Java` 对象不存在该属性的错误,配置 `DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES` 为 `false`,
+ // 在将 `JSON` 字符串反序列化为 `Java` 对象时,如果 `JSON` 字符串中包含了 `Java` 对象中不存在的属性,不会抛出异常,只会忽略这些未知属性进行反序列化操作,增强了反序列化操作的兼容性。
+ objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
- * 序列化方法,将对象转为字符串
- * @param obj
- * @param
- * @return
+ * 该方法是序列化方法,用于将 `Java` 对象转换为 `JSON` 字符串格式。
+ * 首先判断传入的对象是否为 `null`,如果是则直接返回 `null`,若不为 `null`,则尝试使用配置好的 `ObjectMapper` 对象进行序列化操作,
+ * 如果对象本身就是 `String` 类型,则直接返回该对象(因为本身已经是字符串形式,无需再次进行序列化转换),否则调用 `writeValueAsString` 方法将对象转换为 `JSON` 字符串并返回,
+ * 在转换过程中如果出现 `IOException` 异常,则记录警告日志,并返回 `null` 表示转换失败。
+ *
+ * @param obj 要进行序列化的 `Java` 对象,类型为泛型 `T`,可以是任意的 `Java` 类的实例,比如自定义的 `POJO` 类、集合类等,只要其能被 `Jackson` 库正常序列化即可。
+ * @param 泛型类型参数,用于表示传入对象的实际类型,使得方法可以适应不同类型对象的序列化需求,在返回结果时也能保持类型的一致性。
+ * @return 返回序列化后的 `JSON` 字符串,如果传入的对象为 `null` 或者序列化过程出现 `IOException` 异常,则返回 `null`,若序列化成功则返回对应的 `JSON` 格式的字符串表示。
*/
- public static String obj2String(T obj){
- if(obj == null){
+ public static String obj2String(T obj) {
+ if (obj == null) {
return null;
}
try {
- return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
+ return obj instanceof String? (String) obj : objectMapper.writeValueAsString(obj);
} catch (IOException e) {
- log.warn("parse object to string error",e);
+ log.warn("parse object to string error", e);
return null;
}
}
/**
- * 序列化方法,同上,只是输出的格式是美化的,便于测试
- * @param obj
- * @param
- * @return
+ * 该方法也是序列化方法,功能与 `obj2String` 方法类似,同样是将 `Java` 对象转换为 `JSON` 字符串格式,但输出的格式是美化后的,便于阅读和测试。
+ * 它先判断传入的对象是否为 `null`,若是则返回 `null`,不为 `null` 时,若对象本身是 `String` 类型则直接返回该对象,否则通过调用 `writerWithDefaultPrettyPrinter` 方法获取美化后的写入器,
+ * 再使用该写入器的 `writeValueAsString` 方法将对象转换为格式化后的(美化的)`JSON` 字符串并返回,若出现 `IOException` 异常同样记录警告日志并返回 `null`。
+ *
+ * @param obj 要进行序列化的 `Java` 对象,类型为泛型 `T`,与 `obj2String` 方法中参数的含义和作用相同,可接受各种能被 `Jackson` 库序列化的对象类型。
+ * @param 泛型类型参数,用于表示传入对象的实际类型,确保方法的通用性以及返回结果类型的适配性,和 `obj2String` 方法中的泛型作用一致。
+ * @return 返回美化后的序列化后的 `JSON` 字符串,如果传入的对象为 `null` 或者序列化过程出现 `IOException` 异常,则返回 `null`,若序列化成功则返回对应的格式化后的 `JSON` 字符串内容,方便查看和测试。
*/
- public static String obj2StringPretty(T obj){
- if(obj == null){
+ public static String obj2StringPretty(T obj) {
+ if (obj == null) {
return null;
}
try {
- return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
+ return obj instanceof String? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (IOException e) {
- log.warn("parse object to string error",e);
+ log.warn("parse object to string error", e);
return null;
}
}
/**
- * 比较简单的反序列化的方法,将字符串转为单个对象
- * @param str
- * @param clazz
- * @param
- * @return
+ * 该方法是相对简单的反序列化方法,用于将 `JSON` 字符串转换为单个 `Java` 对象。
+ * 首先判断传入的 `JSON` 字符串是否为空(通过 `StringUtils.isEmpty` 方法判断,可避免空指针异常等情况)以及要转换的目标 `Java` 类是否为 `null`,若有一个条件满足则返回 `null`,
+ * 若都不满足,则判断如果目标类就是 `String` 类,则直接将传入的字符串强制转换并返回(因为本身就是期望转换为字符串类型,无需进行复杂的反序列化操作),
+ * 否则使用配置好的 `ObjectMapper` 对象的 `readValue` 方法将 `JSON` 字符串反序列化为指定类型的 `Java` 对象并返回,若在反序列化过程中出现 `IOException` 异常,记录警告日志并返回 `null`。
+ *
+ * @param str 要进行反序列化的 `JSON` 字符串,其格式需要符合 `Jackson` 库的要求以及与要转换的目标 `Java` 类的结构相匹配,否则可能会出现反序列化失败的情况。
+ * @param clazz 目标 `Java` 类的类型,通过传入 `Class` 类型的参数来指定要将 `JSON` 字符串反序列化为哪种类型的对象,例如 `User.class` 表示要转换为 `User` 类型的对象,用于指导 `Jackson` 库进行类型转换。
+ * @param 泛型类型参数,用于表示要反序列化得到的目标对象的实际类型,与传入的 `clazz` 参数的类型保持一致,确保方法的类型安全性以及返回结果的正确性。
+ * @return 返回反序列化后的 `Java` 对象,如果传入的 `JSON` 字符串为空或者目标类为 `null`,或者反序列化过程出现 `IOException` 异常,则返回 `null`,若反序列化成功则返回对应的目标类型的 `Java` 对象实例。
*/
- public static T String2Obj(String str,Class clazz){
- if(StringUtils.isEmpty(str) || clazz == null){
+ public static T String2Obj(String str, Class clazz) {
+ if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
try {
- return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz);
+ return clazz.equals(String.class)? (T) str : objectMapper.readValue(str, clazz);
} catch (IOException e) {
- log.warn("parse string to obj error",e);
+ log.warn("parse string to obj error", e);
return null;
}
}
/**
- * 复杂对象的反序列化(通用)
- * @param str
- * @param typeReference
- * @param
- * @return
+ * 该方法是用于复杂对象的反序列化的通用方法,通过 `TypeReference` 类型来指定复杂的类型信息,实现将 `JSON` 字符串转换为复杂结构的 `Java` 对象。
+ * 先判断传入的 `JSON` 字符串是否为空以及 `TypeReference` 是否为 `null`,若满足其中一个条件则返回 `null`,若都不满足,则判断如果 `TypeReference` 所表示的类型就是 `String` 类型,
+ * 则直接返回传入的字符串(因为本身就是期望转换为字符串类型,无需进行反序列化操作),否则使用配置好的 `ObjectMapper` 对象的 `readValue` 方法按照 `TypeReference` 指定的复杂类型信息将 `JSON` 字符串反序列化为相应的 `Java` 对象并返回,
+ * 在反序列化出现 `IOException` 异常时,记录警告日志并返回 `null`。
+ *
+ * @param str 要进行反序列化的 `JSON` 字符串,同样其格式需要符合 `Jackson` 库要求以及与 `TypeReference` 指定的复杂类型结构相匹配,以保证反序列化能够成功进行。
+ * @param typeReference `TypeReference` 类型的参数,用于详细指定复杂的目标对象类型信息,例如可以表示包含泛型的集合类型(如 `List`)等复杂结构,通过它可以准确地告诉 `Jackson` 库如何进行反序列化操作。
+ * @param 泛型类型参数,用于表示要反序列化得到的目标复杂对象的实际类型,和 `TypeReference` 所指向的类型保持一致,确保方法的类型准确性以及返回结果的正确性。
+ * @return 返回反序列化后的复杂结构的 `Java` 对象,如果传入的 `JSON` 字符串为空或者 `TypeReference` 为 `null`,或者反序列化过程出现 `IOException` 异常,则返回 `null`,若反序列化成功则返回对应的符合复杂类型要求的 `Java` 对象实例。
*/
- public static T Str2Obj(String str, TypeReference typeReference){
- if(StringUtils.isEmpty(str) || typeReference == null){
+ public static T Str2Obj(String str, TypeReference typeReference) {
+ if (StringUtils.isEmpty(str) || typeReference == null) {
return null;
}
try {
- return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference));
+ return (T) (typeReference.getType().equals(String.class)? str : objectMapper.readValue(str, typeReference));
} catch (IOException e) {
- log.warn("parse string to obj error",e);
+ log.warn("parse string to obj error", e);
return null;
}
}
/**
- * 第二种方式实现复杂对象的反序列化
- * @param str
- * @param collectionClass
- * @param elementClasses
- * @param
- * @return
+ * 该方法是实现复杂对象反序列化的另一种方式,通过传入集合类类型以及元素类类型等参数,构造 `JavaType` 来指定复杂的类型信息,进而将 `JSON` 字符串转换为相应的复杂结构的 `Java` 对象。
+ * 首先使用 `objectMapper.getTypeFactory().constructParametricType` 方法根据传入的集合类和元素类等信息构造出 `JavaType`,用于表示复杂的目标对象类型结构,
+ * 然后使用配置好的 `ObjectMapper` 对象的 `readValue` 方法按照构造好的 `JavaType` 将 `JSON` 字符串反序列化为相应的 `Java` 对象并返回,若在反序列化过程中出现 `IOException` 异常,记录警告日志并返回 `null`。
+ * 这种方式适用于当需要反序列化的对象类型是集合类型且集合中的元素类型也需要明确指定的复杂情况,例如将 `JSON` 字符串转换为 `List` 类型的对象时可以使用该方法。
+ *
+ * @param str 要进行反序列化的 `JSON` 字符串,其格式需要符合 `Jackson` 库要求以及与构造的 `JavaType` 所表示的复杂类型结构相匹配,确保反序列化能够正确执行。
+ * @param collectionClass 表示集合类的类型,例如 `List.class`、`Set.class` 等,用于指定反序列化后的对象是哪种集合类型,作为复杂类型结构的外层容器类型信息传递给 `Jackson` 库。
+ * @param elementClasses 可变参数,表示集合中元素的类型,例如 `User.class` 等,按照顺序依次指定集合中元素的具体类型,用于准确构建复杂的目标对象类型结构,指导 `Jackson` 库进行反序列化操作。
+ * @param 泛型类型参数,用于表示要反序列化得到的目标复杂对象的实际类型,与构造的 `JavaType` 所指向的最终类型保持一致,保证方法的类型安全性以及返回结果的正确性。
+ * @return 返回反序列化后的复杂结构的 `Java` 对象,如果在反序列化过程中出现 `IOException` 异常,则返回 `null`,若反序列化成功则返回对应的符合复杂类型要求的 `Java` 对象实例,例如成功将 `JSON` 字符串转换为 `List` 类型的对象等情况。
*/
- public static T Str2Obj(String str,Class> collectionClass,Class>... elementClasses){
- JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
+ public static T Str2Obj(String str, Class> collectionClass, Class>... elementClasses) {
+ JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
try {
- return objectMapper.readValue(str,javaType);
+ return objectMapper.readValue(str, javaType);
} catch (IOException e) {
- log.warn("parse string to obj error",e);
+ log.warn("parse string to obj error", e);
return null;
}
}
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
index e6e5c8a..9f49cb3 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
@@ -3,9 +3,19 @@ package com.njupt.swg.common.utils;
import java.security.MessageDigest;
/**
- * MD5加密工具类
+ * 该类 `MD5Util` 是一个用于进行 `MD5` 加密的工具类,它提供了将输入的字符串按照 `MD5` 算法进行加密并返回加密结果的功能。
+ * 内部包含了几个辅助方法用于字节数组与十六进制字符串之间的转换以及实际执行 `MD5` 加密操作的方法,同时还提供了一个方便的对外公开方法,按照 `UTF-8` 编码进行 `MD5` 加密,
+ * 并且在代码中预留了加盐(增加额外的字符串来提高加密安全性,虽然当前示例中未体现具体加盐操作)的位置,此外还有一个简单的 `main` 方法用于测试 `MD5` 加密功能(实际项目中通常会有更完善的测试手段)。
*/
public class MD5Util {
+ /**
+ * 该私有方法用于将字节数组转换为十六进制字符串表示形式。
+ * 它通过遍历字节数组中的每个字节,调用 `byteToHexString` 方法将每个字节转换为对应的十六进制字符串,然后将这些十六进制字符串依次拼接起来,最终返回拼接后的十六进制字符串结果。
+ * 这个方法主要是作为中间转换步骤,用于后续在 `MD5` 加密结果(以字节数组形式返回)转换为十六进制字符串表示时使用。
+ *
+ * @param b 要转换的字节数组,通常是 `MD5` 加密后得到的字节数组形式的结果,需要将其转换为便于查看和使用的十六进制字符串形式。
+ * @return 返回转换后的十六进制字符串,即将字节数组中的每个字节都转换为十六进制表示后拼接而成的字符串。
+ */
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
@@ -14,6 +24,15 @@ public class MD5Util {
return resultSb.toString();
}
+ /**
+ * 该私有方法用于将单个字节转换为十六进制字符串表示形式。
+ * 先对传入的字节进行处理,如果字节值小于 `0`,则将其加上 `256`(因为在 `Java` 中字节是以有符号数表示,范围是 `-128` 到 `127`,转换为十六进制时需要将其转换为无符号数表示,范围是 `0` 到 `255`),
+ * 然后将处理后的字节值分别计算出高四位和低四位对应的十六进制数字(通过除以 `16` 和取模 `16` 的操作),再从预定义的十六进制数字字符数组 `hexDigits` 中获取对应的字符进行拼接,最终返回该字节对应的十六进制字符串表示。
+ * 这个方法主要是配合 `byteArrayToHexString` 方法,用于将字节逐个转换为十六进制字符串,进而实现字节数组整体的十六进制字符串转换。
+ *
+ * @param b 要转换的单个字节,通常是字节数组中的一个元素,将其转换为十六进制字符串表示形式,以便后续拼接成完整的十六进制字符串结果。
+ * @return 返回转换后的十六进制字符串,即该字节对应的十六进制表示形式,长度为 `2` 个字符,例如 `0f`、`1a` 等。
+ */
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
@@ -24,11 +43,15 @@ public class MD5Util {
}
/**
- * 返回大写MD5
+ * 该私有方法是实际执行 `MD5` 加密操作并将结果转换为十六进制字符串的核心方法,根据是否指定字符编码来处理输入的字符串并返回加密后的十六进制字符串结果(大写形式)。
+ * 首先将传入的原始字符串赋值给 `resultString`,然后通过 `MessageDigest.getInstance` 方法获取 `MD5` 算法对应的 `MessageDigest` 实例(用于执行加密操作),
+ * 如果传入的字符编码为空或者为空字符串,则直接使用默认编码将 `resultString` 转换为字节数组后进行 `MD5` 加密,并将加密后的字节数组通过 `byteArrayToHexString` 方法转换为十六进制字符串赋值给 `resultString`,
+ * 若指定了字符编码,则按照指定的字符编码(如 `UTF-8`)将 `resultString` 转换为字节数组后进行 `MD5` 加密,同样将加密后的字节数组转换为十六进制字符串赋值给 `resultString`,最后返回 `resultString` 并转换为大写形式(常见的 `MD5` 结果展示形式)。
+ * 需要注意的是,当前方法在捕获异常时没有进行详细的处理(只是空的 `catch` 块),实际应用中可能需要根据具体情况进行合适的异常处理,比如记录日志、返回特定的默认值等。
*
- * @param origin
- * @param charsetname
- * @return
+ * @param origin 要进行 `MD5` 加密的原始字符串,例如用户输入的密码等需要加密存储的数据,作为加密的输入内容。
+ * @param charsetname 用于指定字符编码的字符串,例如 "utf-8"、"GBK" 等,若为 `null` 或空字符串,则使用默认编码进行字符串到字节数组的转换,若指定了具体编码,则按照该编码进行转换后再加密。
+ * @return 返回经过 `MD5` 加密并转换为十六进制字符串表示的结果(大写形式),如果加密过程出现异常(虽然当前异常处理不完善),则可能返回 `null`。
*/
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
@@ -44,16 +67,33 @@ public class MD5Util {
return resultString.toUpperCase();
}
+ /**
+ * 该公开的静态方法是对外提供的按照 `UTF-8` 编码进行 `MD5` 加密的便捷方法,它调用了 `MD5Encode` 私有方法,并传入要加密的字符串和 "utf-8" 字符编码参数,
+ * 实现将输入的字符串按照 `UTF-8` 编码进行 `MD5` 加密并返回加密后的十六进制字符串结果(大写形式)。
+ * 同时在方法注释中提到这里可以加盐,意味着可以在将字符串传入 `MD5Encode` 方法之前,添加额外的固定字符串(盐值)与原始字符串拼接后再进行加密,以此提高加密的安全性,不过当前代码中未体现具体的加盐操作实现。
+ *
+ * @param origin 要进行 `MD5` 加密的原始字符串,同样可以是密码等需要加密的数据,会按照 `UTF-8` 编码进行加密处理。
+ * @return 返回按照 `UTF-8` 编码进行 `MD5` 加密并转换为十六进制字符串表示的结果(大写形式),若加密过程出现异常(由 `MD5Encode` 方法内部的异常处理决定)则可能返回 `null`。
+ */
public static String MD5EncodeUtf8(String origin) {
//这里可以加盐
return MD5Encode(origin, "utf-8");
}
+ /**
+ * `main` 方法是 `Java` 程序的入口点,在这里主要用于简单测试 `MD5EncodeUtf8` 方法的功能,通过传入硬编码的字符串 "123456" 调用 `MD5EncodeUtf8` 方法进行 `MD5` 加密,
+ * 并将加密结果打印输出。这只是一个非常简单的测试示例,在实际项目中通常会使用更专业的单元测试框架(如 `JUnit`、`TestNG` 等)来进行更全面、严谨的功能测试,包括对各种边界情况、异常情况等的测试。
+ *
+ * @param args 命令行参数数组,在这里并没有实际使用到,一般用于在命令行启动程序时传入外部参数,改变程序的运行行为等情况。
+ */
public static void main(String[] args) {
System.out.println(MD5EncodeUtf8("123456"));
}
-
+ /**
+ * 定义了一个私有的静态字符串数组 `hexDigits`,用于存储十六进制数字对应的字符表示,即 `0` 到 `9` 和 `a` 到 `f`,
+ * 主要是在 `byteToHexString` 方法中通过索引获取对应的字符来构建字节的十六进制字符串表示形式,作为转换过程中的一个基础数据结构。
+ */
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/ZkClient.java b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/ZkClient.java
index d44bcf2..be22dd0 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/ZkClient.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/common/utils/ZkClient.java
@@ -9,26 +9,46 @@ import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
+ * 该类 `ZkClient` 主要用于创建和配置与 `Zookeeper` 服务器进行交互的 `Curator` 客户端。
+ * 它通过依赖注入获取项目配置参数(`Parameters` 类中存储的相关配置信息),利用 `CuratorFrameworkFactory` 构建 `CuratorFramework` 实例,
+ * 并设置连接字符串、连接超时时间以及重试策略等重要参数,最后启动客户端使其处于可用状态,通过 `@Bean` 注解将创建好的客户端实例交给 `Spring` 容器管理,方便在项目的其他地方进行注入使用,
+ * 实现了在 `Spring` 项目中方便地与 `Zookeeper` 进行交互的功能,例如可以用于分布式锁、服务发现等基于 `Zookeeper` 的应用场景。
+ *
* @Author swg.
* @Date 2019/1/1 17:57
* @CONTACT 317758022@qq.com
* @DESC zk的curator客户端
*/
@Component
+// 该注解表明这个类是 `Spring` 框架中的一个组件,`Spring` 会自动扫描并将其纳入到容器管理中,使得这个类可以参与依赖注入等 `Spring` 容器相关的操作。
public class ZkClient {
@Autowired
+ // 使用 `Spring` 的依赖注入功能,自动注入 `Parameters` 类的实例,通过 `Parameters` 可以获取到项目中关于 `Zookeeper` 的配置参数,比如 `Zookeeper` 服务器的主机地址等信息,用于后续构建 `Curator` 客户端。
private Parameters parameters;
+ /**
+ * `@Bean` 注解标记的方法,用于在 `Spring` 容器中创建并注册一个 `Bean`(在这里就是 `CuratorFramework` 类型的 `Zookeeper` 客户端实例)。
+ * 该方法会被 `Spring` 容器调用,创建好的 `CuratorFramework` 实例会被 `Spring` 容器管理,其他需要使用 `Zookeeper` 客户端的地方可以通过依赖注入的方式获取到这个实例。
+ *
+ * @return 返回创建并配置好的 `CuratorFramework` 实例,它是与 `Zookeeper` 服务器进行交互的客户端对象,已经设置好了连接相关的参数并且已经启动,处于可使用状态。
+ */
@Bean
- public CuratorFramework getZkClient(){
-
- CuratorFrameworkFactory.Builder builder= CuratorFrameworkFactory.builder()
+ public CuratorFramework getZkClient() {
+ // 使用 `CuratorFrameworkFactory` 的 `builder` 方法创建一个构建器对象,通过这个构建器可以方便地设置各种创建 `CuratorFramework` 客户端所需的配置参数,逐步构建出符合需求的客户端实例。
+ CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
+ // 设置要连接的 `Zookeeper` 服务器的连接字符串,通过从注入的 `Parameters` 实例中获取 `zkHost` 属性来获取 `Zookeeper` 服务器的主机地址信息,
+ // 连接字符串的格式通常类似于 "ip1:port1,ip2:port2,...",用于指定要连接的 `Zookeeper` 集群中的服务器节点地址列表。
.connectString(parameters.getZkHost())
+ // 设置连接超时时间,单位为毫秒,这里设置为 `3000` 毫秒,表示客户端尝试连接 `Zookeeper` 服务器时,如果在 `3000` 毫秒内还未成功建立连接,就认为连接超时,触发相应的超时处理逻辑。
.connectionTimeoutMs(3000)
+ // 设置重试策略,这里使用 `RetryNTimes` 策略,表示当连接 `Zookeeper` 出现问题时,会进行重试操作,最多重试 `5` 次,每次重试间隔 `10` 毫秒,
+ // 通过这种重试机制来增加客户端连接成功的概率,提高客户端在面对网络抖动等临时问题时的健壮性。
.retryPolicy(new RetryNTimes(5, 10));
+ // 通过构建器对象的 `build` 方法创建出最终的 `CuratorFramework` 客户端实例,此时实例已经包含了前面设置的连接字符串、连接超时时间以及重试策略等配置信息。
CuratorFramework framework = builder.build();
+ // 启动 `CuratorFramework` 客户端,使其开始尝试连接 `Zookeeper` 服务器,在启动后客户端会根据配置的参数进行连接操作以及后续的相关初始化工作,进入可使用状态,
+ // 可以通过这个客户端实例执行诸如创建节点、获取节点数据、设置分布式锁等各种与 `Zookeeper` 交互的操作。
framework.start();
return framework;
-
}
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java b/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java
index 2079642..e79077e 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java
@@ -21,182 +21,222 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-/**
- * @Author swg.
- * @Date 2019/1/1 13:12
- * @CONTACT 317758022@qq.com
- * @DESC 用户模块,POSTMAN简单测试通过
- */
-
//TODO 先全部开放GET请求
+// 为该控制器类下的所有请求路径设置统一的前缀为 "user",方便对用户模块相关的接口进行统一管理和组织。
@RequestMapping("user")
+// 表明这个类是一个RESTful风格的控制器,其方法返回的数据会直接作为响应体返回(通常以JSON等格式),而不是进行视图解析等操作。
@RestController
+// 使用Slf4j注解生成一个名为log的日志记录对象,方便在类中记录与用户操作相关的日志信息。
@Slf4j
-// 表示标识这个类是swagger的资源
+// 表示标识这个类是swagger的资源,用于生成接口文档,通过value属性指定接口文档中的名称,tags属性用于对接口进行分类标记。
@Api(value = "UserController", tags = {"用户服务接口"})
public class UserController {
+ // 通过Spring的依赖注入功能,自动注入IUserService接口的实现类实例,用于调用用户相关的业务逻辑方法。
@Autowired
private IUserService userService;
+ // 注入CommonCacheUtil实例,用于操作缓存,如在用户登录等操作中与缓存进行交互。
@Autowired
private CommonCacheUtil commonCacheUtil;
/**
- * 用户登陆:验证参数、登陆、写到cookie中并且写到redis中
- * 用户登陆以后,点击其他需要登陆才能看的页面时,先判断是否前端是否有这个key,没有则提示需要登陆
+ * 用户登录方法,处理用户登录的业务逻辑。
+ * 1. 接收用户名和密码,记录用户开始登录的日志信息。
+ * 2. 调用userService的login方法进行登录验证,该方法会根据用户名和密码在数据库等地方进行验证,并返回包含验证结果和用户信息(如果登录成功)的ServerResponse对象。
+ * 3. 如果登录成功,将代表用户的sessionId写到前端浏览器的cookie中,方便后续识别用户登录状态。
+ * 4. 同时将用户信息序列化后存入redis缓存,设置过期时间为30分钟,以便后续快速获取用户信息。
+ * 5. 最后返回登录操作的响应结果给客户端,包含登录是否成功以及相关用户信息(如果成功)。
*/
- @ApiOperation(value="用户登陆", notes="输入用户名,密码,不能为空")
+ @ApiOperation(value = "用户登陆", notes = "输入用户名,密码,不能为空")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "password", value = "用户密码", required = true, dataType = "String")
})
@RequestMapping("/login.do")
- public ServerResponse login(HttpSession session, HttpServletResponse response, String username, String password){
- log.info("【用户{}开始登陆】",username);
- ServerResponse userVOServerResponse = userService.login(username,password);
- if(userVOServerResponse.isSuccess()){
- //登陆成功,那么需要在redis中存储,并且将代表用户的sessionId写到前端浏览器的cookie中
- log.info("【用户{}cookie开始写入】",username);
- CookieUtil.writeLoginToken(response,session.getId());
- //写到redis中,将用户信息序列化,设置过期时间为30分钟
- log.info("【用户{}redis开始写入】",username);
+ public ServerResponse login(HttpSession session, HttpServletResponse response, String username, String password) {
+ // 记录用户开始登录的日志,方便后续排查问题,如查看登录失败的原因等。
+ log.info("【用户{}开始登陆】", username);
+ ServerResponse userVOServerResponse = userService.login(username, password);
+ if (userVOServerResponse.isSuccess()) {
+ // 记录开始写入cookie的日志,用于跟踪cookie写入操作是否正常。
+ log.info("【用户{} cookie开始写入】", username);
+ // 将用户的sessionId写入cookie,使浏览器保存该cookie,用于后续请求识别用户登录状态。
+ CookieUtil.writeLoginToken(response, session.getId());
+ // 记录开始写入redis的日志,便于监控redis写入操作的情况。
+ log.info("【用户{} redis开始写入】", username);
+ // 将用户信息序列化后存入redis缓存,设置过期时间为30分钟,提高后续获取用户信息的效率。
commonCacheUtil.cacheNxExpire(session.getId(), JsonUtil.obj2String(userVOServerResponse.getData()), Constants.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
- log.info("【用户{}登陆成功】",username);
+ // 记录用户登录成功的日志,完整记录登录操作过程。
+ log.info("【用户{}登陆成功】", username);
return userVOServerResponse;
}
-
/**
- * 用户注册,要判断用户名和邮箱是否重复,这里用了分布式锁来防止用户名和邮箱可能出现重复
+ * 用户注册方法,处理用户注册的业务逻辑。
+ * 1. 接收用户对象,记录开始注册的日志信息。
+ * 2. 调用userService的register方法进行用户注册,该方法内部会判断用户名和邮箱是否重复(使用分布式锁保证并发安全),并将用户信息保存到数据库等操作。
+ * 3. 最后返回注册操作的响应结果给客户端,包含注册是否成功以及相关提示信息。
*/
- @ApiOperation(value="创建用户", notes="根据User对象创建用户")
+ @ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping("/register.do")
- public ServerResponse register(User user){
+ public ServerResponse register(User user) {
+ // 记录开始注册的日志,有助于排查注册过程中出现的问题,如用户名或邮箱重复等情况。
log.info("【开始注册】");
- //这里模拟高并发的注册场景,防止用户名字注册重复,所以需要加上分布式锁
+ // 这里模拟高并发的注册场景,防止用户名字注册重复,所以需要加上分布式锁,调用userService的register方法进行实际注册操作。
ServerResponse response = userService.register(user);
+ // 记录用户注册成功的日志,方便监控注册操作的整体情况。
log.info("【用户注册成功】");
return response;
}
/**
- * 判断用户名和邮箱是否重复
+ * 验证用户名和邮箱是否重复方法,处理验证用户名和邮箱唯一性的业务逻辑。
+ * 1. 接收要验证的字符串和类型参数,记录开始验证的日志信息。
+ * 2. 调用userService的checkValid方法进行验证,该方法会根据类型去数据库等地方查询判断传入的用户名或邮箱是否已存在。
+ * 3. 最后返回验证操作的响应结果给客户端,包含验证是否通过以及相关提示信息。
*/
- @ApiOperation(value="验证用户名和邮箱是否重复", notes="用户名和邮箱都不能用已经存在的")
+ @ApiOperation(value = "验证用户名和邮箱是否重复", notes = "用户名和邮箱都不能用已经存在的")
@ApiImplicitParams({
@ApiImplicitParam(name = "str", value = "输入参数", required = true, dataType = "String"),
@ApiImplicitParam(name = "type", value = "参数类型", required = true, dataType = "String")
})
@RequestMapping("/check_valid.do")
public ServerResponse checkValid(@RequestParam("str") String str,
- @RequestParam("type") String type){
+ @RequestParam("type") String type) {
+ // 记录开始验证用户名和邮箱是否重复的日志,方便后续查看验证操作的执行情况。
log.info("【开始验证用户名和邮箱是否重复】");
- ServerResponse response = userService.checkValid(str,type);
+ ServerResponse response = userService.checkValid(str, type);
return response;
}
/**
- * 获取登陆状态用户信息
- * 本地测试的时候,由于cookie是写到oursnai.cn域名下面的,所以需要在hosts文件中添加127.0.0.1 oursnail.cn这个解析
- * 在浏览器中测试的时候,将login方法暂时开放为GET请求,然后请求路径为:http://oursnail.cn:8081/user/login.do?username=admin&password=123456
- * 同样地,在测试获取登陆用户信息接口,也要按照域名来请求,否则拿不到token:http://oursnail.cn:8081/user/get_user_info.do
+ * 获取登录状态用户信息方法,处理获取已登录用户详细信息的业务逻辑。
+ * 1. 从请求中读取登录token,若不存在则认为用户未登录,返回相应错误信息。
+ * 2. 若token存在,从缓存中获取用户信息字符串,若为空也认为用户未登录并返回错误。
+ * 3. 若获取到用户信息字符串,将其反序列化为User对象,再调用userService的getUserInfoFromDB方法从数据库获取更详细的用户信息。
+ * 4. 最后返回获取用户信息的响应结果给客户端,包含获取是否成功以及用户详细信息(如果成功)。
*/
- @ApiOperation(value="获取用户个人信息", notes="登陆状态下获取")
+ @ApiOperation(value = "获取用户个人信息", notes = "登陆状态下获取")
@RequestMapping("/get_user_info.do")
- public ServerResponse getUserInfo(HttpServletRequest request){
+ public ServerResponse getUserInfo(HttpServletRequest request) {
+ // 从请求中读取登录token,用于判断用户是否登录。
String loginToken = CookieUtil.readLoginToken(request);
- if(StringUtils.isEmpty(loginToken)){
+ if (StringUtils.isEmpty(loginToken)) {
+ // 记录用户未登录的日志,方便排查问题,如用户未登录却尝试获取信息的情况。
log.info("【用户未登录,无法获取当前用户信息】");
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
+ // 从缓存中获取用户信息字符串,若为空则表示用户未登录或缓存信息丢失等情况。
String userStr = commonCacheUtil.getCacheValue(loginToken);
- if(userStr == null){
+ if (userStr == null) {
+ // 记录用户未登录的日志,可能是缓存问题导致无法获取用户信息。
log.info("【用户未登录,无法获取当前用户信息】");
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
- User currentUser = JsonUtil.Str2Obj(userStr,User.class);
+ // 将用户信息字符串反序列化为User对象,以便后续获取详细信息。
+ User currentUser = JsonUtil.Str2Obj(userStr, User.class);
UserResVO userResVO = userService.getUserInfoFromDB(currentUser.getId());
- return ServerResponse.createBySuccess("登陆用户获取自身信息成功",userResVO);
+ return ServerResponse.createBySuccess("登陆用户获取自身信息成功", userResVO);
}
-
/**
- * 根据用户名去拿到对应的问题
+ * 根据用户名获取对应的问题方法,用于处理忘记密码时获取问题的业务逻辑。
+ * 1. 接收用户名作为参数,记录用户忘记密码并输入用户名获取问题的日志信息。
+ * 2. 调用userService的getQuestionByUsername方法,根据传入的用户名去查找对应的问题(可能是找回密码时设置的安全问题等)。
+ * 3. 最后返回获取问题操作的响应结果给客户端,包含是否获取到问题以及相关提示信息(如果获取成功则包含问题内容等)。
*/
- @ApiOperation(value="根据用户名去拿到对应的问题", notes="忘记密码时首先根据用户名去获取设置的问题")
+ @ApiOperation(value = "根据用户名去拿到对应的问题", notes = "忘记密码时首先根据用户名去获取设置的问题")
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String")
@RequestMapping("/forget_get_question.do")
- public ServerResponse forgetGetQuestion(String username){
- log.info("【用户{}忘记密码,点击忘记密码输入用户名】",username);
+ public ServerResponse forgetGetQuestion(String username) {
+ // 记录用户忘记密码并进行获取问题操作的日志,方便后续排查忘记密码流程中的问题,如用户名不存在等情况。
+ log.info("【用户{}忘记密码,点击忘记密码输入用户名】", username);
ServerResponse response = userService.getQuestionByUsername(username);
return response;
}
/**
- * 校验答案是否正确
+ * 校验答案是否正确方法,用于处理忘记密码时校验用户提交答案的业务逻辑。
+ * 1. 接收用户名、问题和答案作为参数,记录用户忘记密码并提交问题答案的日志信息。
+ * 2. 调用userService的checkAnswer方法,根据传入的用户名、问题和答案进行校验,判断答案是否正确(与之前设置的答案对比等)。
+ * 3. 最后返回校验答案操作的响应结果给客户端,包含答案是否正确以及相关提示信息。
*/
- @ApiOperation(value="校验答案是否正确", notes="忘记密码时输入正确的用户名之后就可以获取到问题,此时就可以输入答案")
+ @ApiOperation(value = "校验答案是否正确", notes = "忘记密码时输入正确的用户名之后就可以获取到问题,此时就可以输入答案")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "question", value = "设置的问题", required = true, dataType = "String"),
@ApiImplicitParam(name = "answer", value = "提交的答案", required = true, dataType = "String")
})
@RequestMapping("/forget_check_answer.do")
- public ServerResponse forgetCheckAnswer(String username,String question,String answer){
- log.info("【用户{}忘记密码,提交问题答案】",username);
- ServerResponse response = userService.checkAnswer(username,question,answer);
+ public ServerResponse forgetCheckAnswer(String username, String question, String answer) {
+ // 记录用户忘记密码并提交答案进行校验的日志,有助于排查忘记密码答案校验过程中的问题,如答案错误等情况。
+ log.info("【用户{}忘记密码,提交问题答案】", username);
+ ServerResponse response = userService.checkAnswer(username, question, answer);
return response;
}
-
/**
- * 忘记密码的重置密码
+ * 忘记密码的重置密码方法,用于处理忘记密码时重置密码的业务逻辑,需要进行token校验。
+ * 1. 接收用户名、新密码和前端保存的token作为参数,记录用户忘记密码并输入新密码的日志信息。
+ * 2. 调用userService的forgetResetPasswd方法,根据传入的用户名、新密码和token进行密码重置操作,token用于验证用户身份等(可能是之前获取问题或校验答案时生成的)。
+ * 3. 最后返回重置密码操作的响应结果给客户端,包含密码是否重置成功以及相关提示信息。
*/
- @ApiOperation(value="忘记密码的重置密码", notes="输入新的密码,要进行token的校验")
+ @ApiOperation(value = "忘记密码的重置密码", notes = "输入新的密码,要进行token的校验")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String"),
@ApiImplicitParam(name = "forgetToken", value = "前端保存的token", required = true, dataType = "String")
})
@RequestMapping("/forget_reset_password.do")
- public ServerResponse forgetResetPasswd(String username,String passwordNew,String forgetToken){
- log.info("【用户{}忘记密码,输入新密码】",username);
- ServerResponse response = userService.forgetResetPasswd(username,passwordNew,forgetToken);
+ public ServerResponse forgetResetPasswd(String username, String passwordNew, String forgetToken) {
+ // 记录用户忘记密码并进行重置密码操作的日志,方便后续排查忘记密码重置过程中的问题,如新密码不符合要求等情况。
+ log.info("【用户{}忘记密码,输入新密码】", username);
+ ServerResponse response = userService.forgetResetPasswd(username, passwordNew, forgetToken);
return response;
}
/**
- * 登陆状态的重置密码
+ * 登陆状态的重置密码方法,用于处理已登录用户重置密码的业务逻辑。
+ * 1. 从请求中读取cookie获取登录token,若不存在则认为用户未登录,返回相应错误信息。
+ * 2. 若token存在,从redis中获取用户信息字符串,若为空也认为用户未登录并返回错误。
+ * 3. 若获取到用户信息字符串,将其反序列化为User对象,记录用户重置密码的日志信息。
+ * 4. 调用userService的resetPasswd方法,根据传入的老密码和新密码以及用户ID进行密码重置操作(可能会先验证老密码是否正确等)。
+ * 5. 最后返回重置密码操作的响应结果给客户端,包含密码是否重置成功以及相关提示信息。
*/
- @ApiOperation(value="登陆状态的重置密码", notes="登陆的时候只需要输入老的密码和新密码即可")
+ @ApiOperation(value = "登陆状态的重置密码", notes = "登陆的时候只需要输入老的密码和新密码即可")
@ApiImplicitParams({
@ApiImplicitParam(name = "passwordOld", value = "老密码", required = true, dataType = "String"),
@ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String")
})
@RequestMapping("/reset_password.do")
- public ServerResponse resetPasswd(String passwordOld,String passwordNew,HttpServletRequest request){
- //1.读取cookie
+ public ServerResponse resetPasswd(String passwordOld, String passwordNew, HttpServletRequest request) {
+ // 1.读取cookie
String loginToken = CookieUtil.readLoginToken(request);
- if(StringUtils.isEmpty(loginToken)){
+ if (StringUtils.isEmpty(loginToken)) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
- //2.从redis中获取用户信息
+ // 2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken);
- if(userStr == null){
+ if (userStr == null) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
- User currentUser = JsonUtil.Str2Obj(userStr,User.class);
- log.info("【用户{}重置密码】",currentUser);
+ User currentUser = JsonUtil.Str2Obj(userStr, User.class);
+ // 记录用户登录状态下重置密码的日志,有助于排查登录用户重置密码过程中的问题,如老密码错误等情况。
+ log.info("【用户{}重置密码】", currentUser);
- ServerResponse response = userService.resetPasswd(passwordOld,passwordNew,currentUser.getId());
+ ServerResponse response = userService.resetPasswd(passwordOld, passwordNew, currentUser.getId());
return response;
}
/**
- * 更新当前登陆用户信息
+ * 更新当前登录用户信息方法,用于处理已登录用户更新自身信息的业务逻辑。
+ * 1. 从请求中读取登录 `token`,若不存在则认为用户未登录,返回相应错误信息。
+ * 2. 若 `token` 存在,从 `redis` 中获取用户信息字符串,若为空也认为用户未登录并返回错误。
+ * 3. 若获取到用户信息字符串,将其反序列化为 `User` 对象,然后调用 `userService` 的 `updateInfomation` 方法,传入更新后的邮箱、电话、问题、答案以及用户 `ID`,进行用户信息更新操作(可能涉及数据库更新等)。
+ * 4. 最后返回更新信息操作的响应结果给客户端,包含更新是否成功以及相关提示信息。
*/
- @ApiOperation(value="更新当前登陆用户信息", notes="更新用户信息")
+ @ApiOperation(value = "更新当前登陆用户信息", notes = "更新用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "email", value = "邮箱", required = true, dataType = "String"),
@ApiImplicitParam(name = "phone", value = "电话", required = true, dataType = "String"),
@@ -204,40 +244,41 @@ public class UserController {
@ApiImplicitParam(name = "answer", value = "答案", required = true, dataType = "String")
})
@RequestMapping("/update_information.do")
- public ServerResponse updateInformation(String email,String phone,String question,String answer,HttpServletRequest request){
- //1.读取cookie
+ public ServerResponse updateInformation(String email, String phone, String question, String answer, HttpServletRequest request) {
+ // 1.读取cookie
String loginToken = CookieUtil.readLoginToken(request);
- if(StringUtils.isEmpty(loginToken)){
+ if (StringUtils.isEmpty(loginToken)) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
- //2.从redis中获取用户信息
+ // 2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken);
- if(userStr == null){
+ if (userStr == null) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
- User currentUser = JsonUtil.Str2Obj(userStr,User.class);
+ User currentUser = JsonUtil.Str2Obj(userStr, User.class);
- ServerResponse response = userService.updateInfomation(email,phone,question,answer,currentUser.getId());
+ ServerResponse response = userService.updateInfomation(email, phone, question, answer, currentUser.getId());
return response;
}
/**
- * 登出,删除cookie和redis即可
+ * 登出方法,用于处理用户登出的业务逻辑,主要是删除 `cookie` 和 `redis` 中的相关缓存记录。
+ * 1. 从请求中读取登录 `token`,记录用户开始删除 `cookie` 的日志信息。
+ * 2. 使用 `CookieUtil` 的 `delLoginToken` 方法删除 `cookie` 中的登录相关信息,使得客户端后续请求不再携带有效的登录标识。
+ * 3. 记录用户开始删除 `redis` 缓存记录的日志信息,然后使用 `commonCacheUtil` 的 `delKey` 方法删除 `redis` 中与该用户登录相关的缓存记录,清理用户登录状态信息。
+ * 4. 最后返回表示登出成功的响应结果给客户端。
*/
- @ApiOperation(value="登出", notes="退出登陆,删除cookie和redis缓存")
+ @ApiOperation(value = "登出", notes = "退出登陆,删除cookie和redis缓存")
@RequestMapping("/logout.do")
- public ServerResponse logout(HttpServletRequest request,HttpServletResponse response){
+ public ServerResponse logout(HttpServletRequest request, HttpServletResponse response) {
log.info("【用户删除cookie】");
- //1.删除cookie
+ // 1.删除cookie
String loginToken = CookieUtil.readLoginToken(request);
- CookieUtil.delLoginToken(request,response);
+ CookieUtil.delLoginToken(request, response);
log.info("【用户删除redis缓存】");
- //2.删除redis中缓存记录
+ // 2.删除redis中缓存记录
commonCacheUtil.delKey(loginToken);
return ServerResponse.createBySuccess();
}
-
-
-
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/dao/UserMapper.java b/snailmall-user-service/src/main/java/com/njupt/swg/dao/UserMapper.java
index a27b541..fa8219a 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/dao/UserMapper.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/dao/UserMapper.java
@@ -5,34 +5,149 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
+ * 该接口 `UserMapper` 定义了与用户数据操作相关的方法,作为数据访问层(DAO)与数据库进行交互,主要负责对用户表执行各种数据库操作,
+ * 包括但不限于插入、查询、更新和删除用户记录,以及根据特定条件(如用户名、密码、邮箱等)获取用户信息或验证用户相关数据的有效性。
+ * 它通过 `MyBatis` 框架的注解来实现数据库操作的映射,使得上层业务逻辑能够方便地调用这些方法来处理用户数据相关的业务逻辑,
+ * 实现了业务逻辑与数据访问的分离,提高了代码的可维护性和可扩展性。
+ *
* @Author swg.
* @Date 2018/12/31 21:03
* @CONTACT 317758022@qq.com
* @DESC Mapper
*/
@Mapper
+// `@Mapper` 注解是 `MyBatis` 框架用于标识该接口为一个映射器接口的注解,
+// 告诉 `MyBatis` 框架在启动时扫描并生成该接口的代理实现类,该代理类负责将接口中的方法调用转换为对应的数据库操作(通过执行预定义的 `SQL` 语句,这些语句通常在与接口同名的 `XML` 文件中定义,虽然此处未展示相关 `XML` 文件内容),从而实现与数据库的交互。
public interface UserMapper {
+
+ /**
+ * 根据用户的主键 `id` 删除对应的用户记录的方法。
+ * 此方法会在数据库的用户表中执行删除操作,通过主键 `id` 精准定位要删除的记录,删除成功后返回受影响的行数,通常情况下,如果成功删除一条记录,则返回值为 `1`;如果未找到匹配的记录或删除操作失败,返回值可能为 `0` 或其他错误码(具体取决于数据库的实现和操作结果)。
+ *
+ * @param id 要删除的用户记录的主键值,作为唯一标识用于在数据库中确定要删除的特定用户记录,其数据类型为 `Integer`,必须是数据库中用户表主键字段对应的有效类型。
+ * @return 返回一个整数,表示删除操作影响的行数,用于指示操作是否成功以及成功删除的记录数量。
+ */
int deleteByPrimaryKey(Integer id);
+ /**
+ * 向数据库用户表中插入一条完整的用户记录的方法。
+ * 它接受一个 `User` 类型的对象作为参数,该对象包含了要插入的用户的各种信息(如用户名、密码、邮箱等,具体属性取决于 `User` 类的定义),
+ * 方法会根据 `User` 对象的属性与数据库表字段的映射关系(这种映射关系在 `MyBatis` 的配置或实体类注解中定义,此处未详细展示)将数据插入到对应的字段中,
+ * 插入成功后返回受影响的行数,通常插入一条记录成功时返回值为 `1`;若插入操作失败,返回值可能为 `0` 或其他错误码(依据数据库的具体实现和操作情况而定)。
+ *
+ * @param record 要插入到数据库的用户记录对象,包含了完整的用户信息,其属性将被映射到数据库表中的相应字段进行插入操作,数据类型为 `User`。
+
+ * @return 返回一个整数,表示插入操作影响的行数,用于判断插入操作是否成功以及成功插入的记录数量。
+ */
int insert(User record);
+ /**
+ * 选择性插入用户记录的方法,与 `insert` 方法类似,但只会将 `User` 对象中不为 `null` 的属性值插入到数据库中。
+ * 这种方式可以避免插入不必要的 `null` 值到数据库,提高数据的准确性和存储效率,同时也能根据实际需求灵活插入部分属性值,
+ * 插入成功后同样返回受影响的行数,成功插入一条记录时通常返回 `1`;若插入操作失败,返回值可能为 `0` 或其他错误码(取决于数据库的实现和操作结果)。
+ *
+ * @param record 要插入的用户记录对象,包含了用户的各种信息,但只有不为 `null` 的属性值会被用于插入操作,根据对象属性与数据库表字段的映射关系将非 `null` 值插入到对应的字段,数据类型为 `User`。
+ * @return 返回一个整数,表示选择性插入操作影响的行数,用于指示操作是否成功以及成功插入的记录数量。
+ */
int insertSelective(User record);
+ /**
+ * 根据用户主键 `id` 从数据库中查询并获取对应的用户记录的方法。
+ * 它会在数据库用户表中查找主键值为指定 `id` 的记录,如果找到匹配的记录,则返回包含该用户详细信息的 `User` 对象(详细信息包括用户名、密码、邮箱、创建时间等,具体取决于 `User` 类的定义和数据库表中的字段);
+ * 如果未找到匹配的记录,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个空的 `User` 对象,需根据实际情况确定)。
+ *
+ * @param id 要查询的用户记录的主键值,作为查询条件用于在数据库中精准定位要获取的用户记录,数据类型为 `Integer`。
+ * @return 返回一个 `User` 对象,表示查询到的用户记录,如果未找到则可能返回 `null` 或者空的 `User` 对象(根据具体配置)。
+ */
User selectByPrimaryKey(Integer id);
+ /**
+ * 根据用户主键 `id` 选择性更新用户记录的方法,只会更新 `User` 对象中不为 `null` 的属性值到数据库中对应的记录。
+ * 这样可以在更新用户信息时,仅修改实际需要更新的字段,而保持其他字段不变,更新成功后返回受影响的行数,通常成功更新一条记录时返回值为 `1`;
+ * 如果更新操作失败,返回值可能为 `0` 或其他错误码(取决于数据库的实现和操作结果)。
+ *
+ * @param record 包含要更新的用户信息的对象,只有不为 `null` 的属性值会被用于更新数据库中对应 `id` 的用户记录,根据对象属性与数据库表字段的映射关系进行更新操作,数据类型为 `User`。
+ * @return 返回一个整数,表示选择性更新操作影响的行数,用于判断更新操作是否成功以及成功更新的记录数量。
+ */
int updateByPrimaryKeySelective(User record);
+ /**
+ * 根据用户主键 `id` 更新用户记录的方法,会将 `User` 对象中的所有属性值更新到数据库中对应的记录。
+ * 此方法会根据 `User` 对象的属性与数据库表字段的映射关系,将所有属性值覆盖更新到数据库中对应的字段,更新成功后返回受影响的行数,
+ * 成功更新一条记录时通常返回 `1`;若更新操作失败,返回值可能为 `0` 或其他错误码(取决于数据库的实现和操作结果)。
+ *
+ * @param record 要更新的用户记录对象,包含了完整的用户信息,其所有属性值将被更新到数据库中对应 `id` 的用户记录,根据对象属性与数据库表字段的映射关系进行更新操作,数据类型为 `User`。
+ * @return 返回一个整数,表示更新操作影响的行数,用于指示更新操作是否成功以及成功更新的记录数量。
+ */
int updateByPrimaryKey(User record);
+ /**
+ * 根据用户名在数据库中查询对应的用户 `id` 的方法。
+ * 它会在数据库用户表中查找指定用户名的记录,并返回该用户的 `id`,如果找到匹配的用户名,返回对应的 `id` 值;
+ * 如果未找到匹配的用户名,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个默认值,需根据实际情况确定)。
+ * 此方法在一些业务场景中(如根据用户名获取用户详细信息时,可能需要先获取 `id` 再进行进一步查询)非常有用,用于快速定位用户记录的 `id`。
+ *
+ * @param username 要查询的用户名,作为查询条件用于在数据库中定位用户记录,数据类型为 `String`。
+ * @return 返回一个整数,表示查询到的用户 `id`,如果未找到则可能返回 `null` 或者默认值(根据具体配置)。
+ */
Integer selectByUsername(String username);
- User selectByUsernameAndPasswd(@Param("username") String username,@Param("password") String md5Passwd);
+ /**
+ * 根据用户名和密码(密码为经过 `MD5` 加密后的字符串)在数据库中查询用户记录的方法。
+ * 它会在数据库用户表中查找指定用户名和加密密码匹配的记录,如果找到匹配的记录,则返回包含该用户详细信息的 `User` 对象(详细信息如用户名、密码、邮箱等,取决于 `User` 类的定义和数据库表中的字段);
+ * 如果未找到匹配的记录,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个空的 `User` 对象,需根据实际情况确定)。
+ * 该方法主要用于用户登录验证场景,通过传入用户名和加密后的密码,在数据库中查找是否存在匹配的用户记录,以确定用户登录是否合法。
+ *
+ * @param username 要查询的用户名,作为查询条件之一用于在数据库中定位用户记录,数据类型为 `String`。
+ * @param md5Passwd 经过 `MD5` 加密后的用户密码,作为查询条件之一与用户名一起用于在数据库中查找匹配的用户记录,确保密码在数据库中的安全性(数据库中存储的密码也应为加密形式),数据类型为 `String`。
+ * @return 返回一个 `User` 对象,表示查询到的用户记录,如果未找到则可能返回 `null` 或者空的 `User` 对象(根据具体配置)。
+ */
+ User selectByUsernameAndPasswd(@Param("username") String username, @Param("password") String md5Passwd);
+ /**
+ * 根据邮箱在数据库中查询对应的用户 `id` 的方法。
+ * 它会在数据库用户表中查找指定邮箱的记录,并返回该用户的 `id`,如果找到匹配的邮箱,返回对应的 `id` 值;
+ * 如果未找到匹配的邮箱,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个默认值,需根据实际情况确定)。
+ * 此方法在用户注册或其他需要验证邮箱唯一性的业务场景中非常有用,用于快速确定邮箱是否已被注册(通过查询是否存在对应的 `id`)。
+ *
+ * @param str 要查询的邮箱地址,作为查询条件用于在数据库中定位用户记录,数据类型为 `String`。
+ * @return 返回一个整数,表示查询到的用户 `id`,如果未找到则可能返回 `null` 或者默认值(根据具体配置)。
+ */
Integer selectByEmail(String str);
+ /**
+ * 根据用户名在数据库中获取用户详细信息的方法。
+ * 它会在数据库用户表中查找指定用户名的记录,并返回包含该用户详细信息的 `User` 对象(详细信息如用户名、密码、邮箱、创建时间等,取决于 `User` 类的定义和数据库表中的字段);
+ * 如果未找到匹配的用户名,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个空的 `User` 对象,需根据实际情况确定)。
+ * 该方法在需要展示用户详细信息或根据用户名获取用户完整信息的业务场景中使用,例如用户个人信息页面展示、用户信息更新前获取原始信息等。
+ *
+ * @param username 要查询的用户名,作为查询条件用于在数据库中定位用户记录,数据类型为 `String`。
+ * @return 返回一个 `User` 对象,表示查询到的用户详细信息,如果未找到则可能返回 `null` 或者空的 `User` 对象(根据具体配置)。
+ */
User getUserByUsername(String username);
+ /**
+ * 根据用户名、问题和答案在数据库中获取用户详细信息的方法。
+ * 它会在数据库用户表中查找指定用户名、问题和答案(通常用于忘记密码场景下的验证)匹配的记录,如果找到匹配的记录,则返回包含该用户详细信息的 `User` 对象(详细信息如用户名、密码、邮箱等,取决于 `User` 类的定义和数据库表中的字段);
+ * 如果未找到匹配的记录,返回值可能为 `null`(具体取决于 `MyBatis` 的配置和查询结果处理方式,也有可能返回一个空的 `User` 对象,需根据实际情况确定)。
+ * 该方法主要用于忘记密码流程中,根据用户提供的用户名、问题和答案来验证用户身份,并获取用户详细信息,以便后续进行密码重置等操作。
+ *
+ * @param username 要查询的用户名,作为查询条件之一用于在数据库中定位用户记录,数据类型为 `String`。
+ * @param question 要查询的问题,作为查询条件之一与用户名一起用于在数据库中定位用户记录,通常是用户在忘记密码时设置的安全问题,数据类型为 `String`。
+ * @param answer 要查询的答案,作为查询条件之一与用户名和问题一起用于在数据库中定位用户记录,是用户对安全问题的回答,用于验证用户身份,数据类型为 `String`。
+ * @return 返回一个 `User` 对象,表示查询到的用户详细信息,如果未找到则可能返回 `null` 或者空的 `User` 对象(根据具体配置)。
+ */
User getUserByUsernameQuestionAnswer(String username, String question, String answer);
+ /**
+ * 检查邮箱有效性的方法,根据传入的邮箱地址和可选的用户 `id`(用于在特定场景下排除自身邮箱检查),在数据库中检查该邮箱是否已被其他用户使用。
+ * 如果邮箱未被其他用户使用(或者在更新邮箱时,排除了当前用户自己的邮箱且该邮箱是当前用户自己的邮箱),则返回 `1`,表示邮箱有效;
+ * 如果邮箱已被其他用户使用,则返回 `0`,表示邮箱无效。此方法在用户注册和更新邮箱等业务场景中用于验证邮箱的唯一性。
+ *
+ * @param email 要检查的邮箱地址,作为检查条件用于在数据库中查找是否存在其他用户使用该邮箱,数据类型为 `String`。
+ * @param userId 用户 `id`(可选参数),在更新邮箱场景中用于排除当前用户自己的邮箱检查,如果传入 `null`,则表示不考虑用户 `id`,直接检查邮箱的唯一性;
+ * 如果传入具体的用户 `id`,则在检查邮箱唯一性时会排除该 `id` 对应的用户自己的邮箱(假设该用户正在更新自己的邮箱),数据类型为 `Integer`,并通过 `@Param` 注解指定参数名为 `userId` 且非必需(`required = false`)。
+ * @return 返回一个整数,`1` 表示邮箱有效,`0` 表示邮箱无效,用于指示邮箱检查的结果。
+ */
Integer checkEmailValid(@Param("email") String email,@Param("userId") Integer userId);
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/entity/User.java b/snailmall-user-service/src/main/java/com/njupt/swg/entity/User.java
index 4354b81..847ac67 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/entity/User.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/entity/User.java
@@ -9,35 +9,54 @@ import java.io.Serializable;
import java.util.Date;
/**
+ * 这个类 `User` 是项目中的用户实体类,用于封装与用户相关的各种属性信息,它对应着数据库中存储用户数据的表结构,
+ * 在整个项目的不同层(如数据访问层、业务逻辑层、控制层等)之间传递用户相关的数据,是实现面向对象编程与数据库交互的重要基础。
+ * 通过使用 `Lombok` 注解,简化了代码中对常规方法(如构造函数、`Getter` 和 `Setter` 方法、`toString` 方法等)的编写,提高了代码的简洁性和可读性。
+ *
* @Author swg.
* @Date 2018/12/31 21:01
* @CONTACT 317758022@qq.com
* @DESC 用户实体类
*/
+// `@Data` 注解是 `Lombok` 提供的一个便捷注解,它会自动为类中的所有非静态、非 `final` 的字段生成 `Getter`、`Setter` 方法,
+// 以及 `equals`、`hashCode` 和 `toString` 方法(不过这里因为又单独使用了 `@ToString` 注解,所以 `toString` 方法会按照 `@ToString` 注解的配置来生成),方便对类中属性的访问和操作,减少了大量重复的代码编写工作。
@Data
+// `@NoArgsConstructor` 注解由 `Lombok` 提供,它会为该类生成一个无参的构造函数,这在一些情况下(比如使用某些框架进行对象实例化时要求类必须有默认的无参构造函数)是很有用的,使得创建 `User` 类的实例更加方便灵活。
@NoArgsConstructor
+// `@AllArgsConstructor` 注解同样来自 `Lombok`,它会为类生成一个包含所有字段的全参构造函数,这样在初始化 `User` 类对象时,可以通过传入所有属性的值一次性完成对象的创建,便于在需要完整初始化对象的场景中使用。
@AllArgsConstructor
+// `@ToString` 注解用于生成一个方便查看对象信息的 `toString` 方法,默认情况下它会输出类名以及所有字段的名称和对应的值,便于在调试代码、打印对象信息等场景中直观地了解对象的状态。
@ToString
+// 实现 `Serializable` 接口表示该类的对象可以被序列化,意味着可以将 `User` 类的对象转换为字节流进行存储(比如保存到文件中或者在网络传输过程中进行传递等),
+// 并且在需要的时候能够从字节流中反序列化还原出原来的对象,这在很多涉及数据持久化和分布式系统的场景中是非常必要的操作。
public class User implements Serializable {
+ // 用户的唯一标识符,通常对应数据库表中的主键字段,用于在系统中唯一确定一个用户,其数据类型为 `Integer`,可以存储整数形式的用户 `ID` 值。
private Integer id;
+ // 用户名,用于用户登录以及在系统中标识用户的身份,是一个字符串类型的属性,存储用户在注册时设定的用户名信息。
private String username;
+ // 用户密码,存储用户登录时需要输入验证的密码信息,以加密后的字符串形式存储(在实际应用中应该采用安全的加密算法进行加密处理,比如 `MD5` 等常见加密方式),保证用户密码的安全性,数据类型为 `String`。
private String password;
+ // 用户的电子邮箱地址,可用于接收系统发送的通知邮件、找回密码等功能相关的邮件信息,是一个字符串类型的属性,存储符合邮箱格式规范的邮箱地址。
private String email;
+ // 用户的电话号码,可用于联系用户或者作为一些验证操作(如手机验证码登录等功能,如果有相关拓展功能的话)的依据,数据类型为 `String`,存储用户的手机号码等电话号码信息。
private String phone;
+ // 安全问题,通常用于用户忘记密码时,通过回答预先设置的安全问题来验证身份,进而进行密码重置等操作,是一个字符串类型的属性,存储用户自己设置的安全问题内容。
private String question;
+ // 安全问题的答案,与 `question` 属性相对应,用于在忘记密码验证身份环节,用户输入答案与数据库中存储的答案进行比对验证,数据类型为 `String`,存储用户针对所设置安全问题的答案内容。
private String answer;
- //角色0-管理员,1-普通用户
+ // 用户角色字段,用于区分不同权限的用户,这里定义了两种角色,`0` 表示管理员,拥有系统的高级管理权限(如用户管理、系统配置等权限),`1` 表示普通用户,只具有普通的使用系统功能的权限,数据类型为 `Integer`。
private Integer role;
+ // 用户创建时间,记录用户账号在系统中被创建的具体时间点,数据类型为 `Date`,可以准确记录创建时间的日期和时间信息,方便后续进行数据分析、用户行为统计等操作。
private Date createTime;
+ // 用户信息更新时间,每当用户的相关信息(如密码、邮箱、电话号码等)发生修改时,会更新这个字段的值,记录最后一次更新的时间点,数据类型为 `Date`,同样便于跟踪用户信息的变更情况以及进行相关的数据统计和分析。
private Date updateTime;
-
}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/service/IUserService.java b/snailmall-user-service/src/main/java/com/njupt/swg/service/IUserService.java
index e50c449..f3329b5 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/service/IUserService.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/service/IUserService.java
@@ -5,35 +5,123 @@ import com.njupt.swg.entity.User;
import com.njupt.swg.vo.UserResVO;
/**
+ * 该接口 `IUserService` 定义了一系列与用户相关的业务逻辑方法,作为业务逻辑层对外提供服务的契约,
+ * 它规定了处理用户登录、注册、信息验证、密码重置、信息更新以及获取用户详细信息等功能的方法签名,
+ * 不同的业务实现类需要实现该接口来具体完成这些方法对应的业务逻辑,从而实现业务逻辑与控制层、数据访问层的解耦,
+ * 方便代码的维护、扩展以及单元测试等操作,提高整个项目的可维护性和可扩展性。
+ *
* @Author swg.
* @Date 2018/12/31 21:07
* @CONTACT 317758022@qq.com
* @DESC
*/
public interface IUserService {
- /** 用户登陆 **/
- ServerResponse login(String username,String password);
- /** 用户注册 **/
+ /**
+ * 用户登录方法,用于处理用户登录的业务逻辑。
+ * 接收用户名和密码作为参数,在业务逻辑层内部会调用数据访问层(如 `UserMapper` 等相关数据操作接口)去数据库中验证用户名和密码是否匹配,
+ * 同时可能还会进行一些其他与登录相关的逻辑处理(比如记录登录日志、更新用户登录相关状态等),
+ * 最后返回一个 `ServerResponse` 类型的对象,其中 `ServerResponse` 用于封装操作的响应结果(包含成功或失败状态码、提示信息等),
+ * 如果登录成功,`UserResVO` 则可能包含一些与登录用户相关的详细信息(比如用户角色、权限信息、额外的用户资料等,具体取决于 `UserResVO` 的定义),用于后续的业务处理或者返回给客户端展示。
+ *
+ * @param username 要登录的用户名,是用户登录操作必不可少的关键参数,由客户端传入,用于在数据库中查找对应的用户记录进行密码验证,数据类型为 `String`。
+ * @param password 要登录的用户密码,同样是登录操作的核心参数之一,需要与传入的用户名一起进行匹配验证,数据类型为 `String`,并且在实际应用中通常应该是经过加密处理后的密码字符串(比如采用 `MD5` 等加密方式)。
+ * @return 返回 `ServerResponse` 类型的对象,包含登录操作的响应结果以及登录成功时对应的用户详细信息(如果成功),客户端可根据此对象判断登录是否成功并获取相应的用户信息。
+ */
+ ServerResponse login(String username, String password);
+
+ /**
+ * 用户注册方法,用于处理用户注册的业务逻辑。
+ * 接收一个 `User` 类型的对象作为参数,该对象包含了用户注册时填写的各种详细信息(如用户名、密码、邮箱、电话号码等,具体取决于 `User` 类的定义),
+ * 在业务逻辑层内部会调用数据访问层进行一系列的注册相关操作,比如验证用户名和邮箱是否已被使用(通常会涉及到数据库查询操作,确保唯一性)、将用户信息插入到数据库中,
+ * 同时可能还会有一些其他与注册相关的逻辑处理(比如发送注册成功邮件、初始化用户相关的默认配置等),最后返回一个 `ServerResponse` 类型的对象,用于封装注册操作的响应结果(包含成功或失败状态码、提示信息等),告知客户端注册是否成功。
+ *
+ * @param user 包含用户注册详细信息的 `User` 对象,作为注册操作的数据来源,其属性将被用于在数据库中创建新的用户记录,数据类型为 `User`。
+ * @return 返回 `ServerResponse` 类型的对象,包含注册操作的响应结果,客户端可根据此对象得知注册是否成功以及获取相应的提示信息。
+ */
ServerResponse register(User user);
- /** 判断用户名和邮箱是否重复 **/
+ /**
+ * 判断用户名和邮箱是否重复方法,用于处理验证用户名和邮箱唯一性的业务逻辑。
+ * 接收要验证的字符串和类型作为参数,根据传入的 `type` 参数来确定 `str` 代表的是用户名还是邮箱,然后调用数据访问层去数据库中查询验证,
+ * 判断传入的用户名或者邮箱在系统中是否已经存在,最后返回一个 `ServerResponse` 类型的对象,用于封装验证操作的响应结果(包含验证是否通过、相应的提示信息等),告知客户端验证结果。
+ *
+ * @param str 要验证的用户名或者邮箱内容,是验证操作的关键输入数据,客户端传入此参数,其具体代表的是用户名还是邮箱由 `type` 参数决定,数据类型为 `String`。
+ * @param type 用于指定 `str` 参数类型的字符串,取值应该是能够明确表示 `str` 代表用户名还是邮箱的标识(比如可能取值为 "username" 表示用户名,"email" 表示邮箱等),数据类型为 `String`,用于业务逻辑层准确判断要验证的内容并进行相应的数据库查询验证操作。
+ * @return 返回 `ServerResponse` 类型的对象,包含验证操作的响应结果,客户端可根据此对象得知用户名或邮箱是否可用以及获取相应的提示信息。
+ */
ServerResponse checkValid(String str, String type);
- /** 根据用户名去获取设置的忘记密码的问题 **/
+ /**
+ * 根据用户名去获取设置的忘记密码的问题方法,用于处理忘记密码流程中获取用户预先设置的安全问题的业务逻辑。
+ * 接收用户名作为唯一参数,在业务逻辑层内部会调用数据访问层根据传入的用户名去数据库中查找对应的用户记录,并获取该用户设置的忘记密码相关的安全问题(通常是用户在注册或者个人信息设置阶段设置好的),
+ * 最后返回一个 `ServerResponse` 类型的对象,用于封装获取问题操作的响应结果(包含是否获取到问题、相应的提示信息等),告知客户端获取问题的情况,若获取成功则可以返回问题内容供客户端展示给用户进行后续的答案填写操作。
+ *
+ * @param username 要获取忘记密码问题的用户名,作为查找用户记录以及获取对应问题的关键依据,由客户端传入,数据类型为 `String`。
+ * @return 返回 `ServerResponse` 类型的对象,包含获取问题操作的响应结果,客户端可根据此对象得知是否获取到问题以及获取相应的提示信息。
+ */
ServerResponse getQuestionByUsername(String username);
- /** 校验问题对应的答案是否正确 **/
+ /**
+ * 校验问题对应的答案是否正确方法,用于处理忘记密码流程中验证用户输入的答案是否正确的业务逻辑。
+ * 接收用户名、问题和答案作为参数,在业务逻辑层内部会调用数据访问层根据传入的用户名、问题和答案去数据库中查找对应的用户记录,并验证用户输入的答案与数据库中存储的该用户对应问题的答案是否一致,
+ * 最后返回一个 `ServerResponse` 类型的对象,用于封装校验答案操作的响应结果(包含答案是否正确、相应的提示信息等),告知客户端验证结果,若答案正确则可以继续后续的密码重置等操作。
+ *
+ * @param username 要校验答案对应的用户名,用于在数据库中定位用户记录进行答案验证,由客户端传入,数据类型为 `String`。
+ * @param question 要校验答案对应的问题,同样用于在数据库中准确查找用户记录以及对应存储的答案进行比对验证,由客户端传入,数据类型为 `String`。
+ * @param answer 用户输入的要校验的答案,与数据库中存储的答案进行比对,由客户端传入,数据类型为 `String`。
+ * @return 返回 `ServerResponse` 类型的对象,包含校验答案操作的响应结果,客户端可根据此对象得知答案是否正确以及获取相应的提示信息。
+ */
ServerResponse checkAnswer(String username, String question, String answer);
- /** 重置密码 **/
+ /**
+ * 重置密码方法(用于忘记密码的场景),用于处理忘记密码后重置密码的业务逻辑。
+ * 接收用户名、新密码和前端保存的 `forgetToken`(可能是在忘记密码流程中前面步骤生成的用于验证身份等用途的令牌)作为参数,
+ * 在业务逻辑层内部会调用数据访问层以及进行一些其他相关的验证逻辑(比如验证 `forgetToken` 的有效性、通过用户名等信息进一步确认用户身份等),然后将新密码更新到数据库中对应的用户记录,
+ * 最后返回一个 `ServerResponse` 类型的对象,用于封装密码重置操作的响应结果(包含密码是否重置成功、相应的提示信息等),告知客户端重置密码的情况。
+ *
+ * @param username 要重置密码的用户名,用于在数据库中定位用户记录进行密码更新操作,由客户端传入,数据类型为 `String`。
+ * @param passwordNew 要设置的新密码,作为重置密码操作的关键数据,将被更新到数据库中对应的用户记录,由客户端传入,数据类型为 `String`。
+ * @param forgetToken 前端保存的用于忘记密码流程中验证身份等用途的令牌,用于确保密码重置操作的合法性和安全性,由客户端传入,数据类型为 `String`。
+ * @return 返回 `ServerResponse` 类型的对象,包含密码重置操作的响应结果,客户端可根据此对象得知密码是否重置成功以及获取相应的提示信息。
+ */
ServerResponse forgetResetPasswd(String username, String passwordNew, String forgetToken);
- /** 登陆状态下重置密码 **/
+ /**
+ * 登陆状态下重置密码方法,用于处理已登录用户重置密码的业务逻辑。
+ * 接收老密码、新密码和用户 `ID` 作为参数,在业务逻辑层内部会先验证老密码是否正确(通过调用数据访问层根据用户 `ID` 等信息查找数据库中的用户记录并比对密码),
+ * 若老密码验证通过,则将新密码更新到数据库中对应的用户记录,同时可能还会进行一些其他与密码更新相关的逻辑处理(比如记录密码更新日志、更新相关的登录状态等),
+ * 最后返回一个 `ServerResponse` 类型的对象,用于封装密码重置操作的响应结果(包含密码是否重置成功、相应的提示信息等),告知客户端重置密码的情况。
+ *
+ * @param passwordOld 已登录用户当前使用的老密码,用于验证用户身份,确保只有知道当前密码的用户才能进行密码重置操作,由客户端传入,数据类型为 `String`。
+ * @param passwordNew 要设置的新密码,作为重置密码操作的关键数据,将被更新到数据库中对应的用户记录,由客户端传入,数据类型为 `String`。
+ * @param userId 已登录用户的唯一标识符,用于在数据库中准确查找对应的用户记录进行密码验证和更新操作,由客户端传入,数据类型为 `int`。
+ * @return 返回 `ServerResponse` 类型的对象,包含密码重置操作的响应结果,客户端可根据此对象得知密码是否重置成功以及获取相应的提示信息。
+ */
ServerResponse resetPasswd(String passwordOld, String passwordNew, int userId);
- /** 登陆状态下更新个人信息(更新策略为:如果用户某一项不填,表示保持原样不变) **/
+ /**
+ * 登陆状态下更新个人信息方法(更新策略为:如果用户某一项不填,表示保持原样不变),用于处理已登录用户更新自身部分信息(如邮箱、电话、安全问题、答案等)的业务逻辑。
+ * 接收邮箱、电话、安全问题、答案以及用户 `ID` 作为参数,在业务逻辑层内部会根据传入的参数以及更新策略,调用数据访问层去数据库中查找对应的用户记录,
+ * 然后只更新用户传入了新值的那些字段(对于未传入值的字段保持数据库中原有的值不变),同时可能还会进行一些其他与信息更新相关的逻辑处理(比如记录信息更新日志、更新相关的更新时间字段等),
+ * 最后返回一个 `ServerResponse` 类型的对象,用于封装信息更新操作的响应结果(包含信息是否更新成功、相应的提示信息等),告知客户端更新信息的情况。
+ *
+ * @param email 用户要更新的邮箱地址,如果不填则表示保持原有邮箱地址不变,由客户端传入,数据类型为 `String`。
+ * @param phone 用户要更新的电话号码,如果不填则表示保持原有电话号码不变,由客户端传入,数据类型为 `String`。
+ * @param question 用户要更新的安全问题,如果不填则表示保持原有安全问题不变,由客户端传入,数据类型为 `String`。
+ * @param answer 用户要更新的安全问题的答案,如果不填则表示保持原有答案不变,由客户端传入,数据类型为 `String`。
+ * @param userId 要更新信息的已登录用户的唯一标识符,用于在数据库中准确查找对应的用户记录进行信息更新操作,由客户端传入,数据类型为 `Integer`。
+ * @return 返回 `ServerResponse` 类型的对象,包含信息更新操作的响应结果,客户端可根据此对象得知信息是否更新成功以及获取相应的提示信息。
+ */
ServerResponse updateInfomation(String email, String phone, String question, String answer, Integer userId);
+ /**
+ * 根据用户 `ID` 从数据库中获取用户详细信息的方法,用于在需要获取已登录用户或者指定 `ID` 用户的详细信息的业务场景中(比如展示用户个人信息页面等)进行数据查询操作。
+ * 在业务逻辑层内部会调用数据访问层根据传入的用户 `ID` 去数据库中查找对应的用户记录,然后将获取到的用户信息封装到 `UserResVO` 类型的对象中返回(`UserResVO` 可能包含了更适合展示或者业务使用的用户信息格式和内容,具体取决于其定义),
+ * 如果未找到对应的用户记录,则可能返回 `null` 或者一个默认的空 `UserResVO` 对象(具体取决于业务逻辑层的实现方式)。
+ *
+ * @param userId 要获取详细信息的用户的唯一标识符,作为在数据库中查找用户记录的关键依据,由客户端传入,数据类型为 `Integer`。
+ * @return 返回 `UserResVO` 类型的对象,包含查询到的用户详细信息,如果未找到对应用户记录则可能返回 `null` 或者空的 `UserResVO` 对象(根据具体实现),用于后续的业务处理或者展示给客户端。
+ */
UserResVO getUserInfoFromDB(Integer userId);
-}
+}
\ No newline at end of file
diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/service/UserServiceImpl.java b/snailmall-user-service/src/main/java/com/njupt/swg/service/UserServiceImpl.java
index e2cc35b..e3d085c 100644
--- a/snailmall-user-service/src/main/java/com/njupt/swg/service/UserServiceImpl.java
+++ b/snailmall-user-service/src/main/java/com/njupt/swg/service/UserServiceImpl.java
@@ -8,6 +8,7 @@ import com.njupt.swg.common.utils.MD5Util;
import com.njupt.swg.dao.UserMapper;
import com.njupt.swg.entity.User;
import com.njupt.swg.vo.UserResVO;
+import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;