diff --git a/snailmall-order-service/pom.xml b/snailmall-order-service/pom.xml
index 34a8262..415b73d 100644
--- a/snailmall-order-service/pom.xml
+++ b/snailmall-order-service/pom.xml
@@ -128,6 +128,16 @@
com.google.zxing
core
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.projectlombok
+ lombok
+ provided
+
@@ -148,4 +158,5 @@
+
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/cache/CommonCacheUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/cache/CommonCacheUtil.java
index 66c6499..5f42c45 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/cache/CommonCacheUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/cache/CommonCacheUtil.java
@@ -12,35 +12,50 @@ import redis.clients.jedis.JedisPool;
* @Date 2019/1/1 15:03
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类是一个工具类,用于操作Redis缓存,提供了缓存设置、获取、设置过期时间以及删除缓存等常用功能。
+ * 它被标记为Spring的组件@Component,方便在Spring容器中进行管理和依赖注入,并且使用了Slf4j进行日志记录。
*/
@Component
@Slf4j
public class CommonCacheUtil {
-
- @Autowired
+ // 依赖注入JedisPoolWrapper,通过它来获取JedisPool,进而操作Redis
private JedisPoolWrapper jedisPoolWrapper;
-
/**
* 缓存永久key
+ * 功能:将给定的键值对存入Redis中,作为永久有效的缓存数据(这里选择的是Redis的0号数据库,可根据实际需求调整)。
+ * 参数:
+ * - key:要存入Redis的键,类型为字符串。
+ * - value:要存入Redis对应键的值,类型为字符串。
*/
public void cache(String key, String value) {
try {
+ // 通过JedisPoolWrapper获取JedisPool对象,它是管理Jedis连接的资源池
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
+ // 从资源池中获取一个Jedis实例,使用try-with-resources语句确保使用完后自动关闭连接,释放资源
try (Jedis Jedis = pool.getResource()) {
+ // 选择Redis的0号数据库
Jedis.select(0);
+ // 将键值对存入Redis中
Jedis.set(key, value);
}
}
} catch (Exception e) {
+ // 如果出现异常,使用日志记录错误信息,记录的内容为"redis存值失败"以及具体的异常堆栈信息
log.error("redis存值失败", e);
+ // 抛出一个自定义的业务异常SnailmallException,提示"redis报错",方便上层调用者统一处理异常情况
throw new SnailmallException("redis报错");
}
}
/**
* 获取缓存key
+ * 功能:根据给定的键从Redis中获取对应的值。
+ * 参数:
+ * - key:要从Redis中获取值的键,类型为字符串。
+ * 返回值:
+ * 返回从Redis中获取到的对应键的值,如果不存在则返回null,类型为字符串。
*/
public String getCacheValue(String key) {
String value = null;
@@ -49,6 +64,7 @@ public class CommonCacheUtil {
if (pool != null) {
try (Jedis Jedis = pool.getResource()) {
Jedis.select(0);
+ // 从Redis中获取指定键的值,并赋值给value变量
value = Jedis.get(key);
}
}
@@ -61,6 +77,13 @@ public class CommonCacheUtil {
/**
* 过期key
+ * 功能:向Redis中存入键值对,并设置该键的过期时间(以秒为单位),同时保证键的原子性设置(使用SETNX命令,只有键不存在时才设置成功)。
+ * 参数:
+ * - key:要存入Redis的键,类型为字符串。
+ * - value:要存入Redis对应键的值,类型为字符串。
+ * - expire:设置键的过期时间,单位为秒,类型为整数。
+ * 返回值:
+ * 返回SETNX命令的执行结果,1表示设置成功(键不存在时),0表示设置失败(键已存在),类型为长整型。
*/
public long cacheNxExpire(String key, String value, int expire) {
long result = 0;
@@ -69,7 +92,9 @@ public class CommonCacheUtil {
if (pool != null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
+ // 使用SETNX命令向Redis中设置键值对,只有键不存在时才设置成功,返回结果赋值给result
result = jedis.setnx(key, value);
+ // 如果设置成功(SETNX返回1),则设置该键的过期时间
jedis.expire(key, expire);
}
}
@@ -83,6 +108,9 @@ public class CommonCacheUtil {
/**
* 删除缓存key
+ * 功能:从Redis中删除指定的键。
+ * 参数:
+ * - key:要从Redis中删除的键,类型为字符串。
*/
public void delKey(String key) {
JedisPool pool = jedisPoolWrapper.getJedisPool();
@@ -90,6 +118,7 @@ public class CommonCacheUtil {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
try {
+ // 调用Jedis的del方法从Redis中删除指定的键
jedis.del(key);
} catch (Exception e) {
log.error("从redis中删除失败", e);
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java b/snailmall-order-service/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java
index addfe24..1d98f00 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java
@@ -13,29 +13,54 @@ import javax.annotation.PostConstruct;
* @Date 2019/1/1 15:00
* @CONTACT 317758022@qq.com
* @DESC 只做了单个redis,但是课程中实现的redis客户端集群,要掌握一致性hash算法
+ * 这个类主要用于封装JedisPool(Jedis连接池)相关的操作,负责初始化Jedis连接池,并提供获取连接池的方法。
+ * 它被标记为Spring的组件@Component,方便在Spring容器中进行管理,同时使用了Slf4j进行日志记录。
*/
@Component
@Slf4j
public class JedisPoolWrapper {
- @Autowired
+
+ // 这里应该是自定义的一个类,用于存放Redis相关的配置参数,比如最大连接数、主机地址、端口等信息,
+ // 但从当前代码来看,没有看到其具体定义,假设它有对应的getter方法来获取相关配置值。
+
private Parameters parameters;
+ // 用于存放初始化后的JedisPool对象,初始值为null,代表还未进行初始化操作
private JedisPool jedisPool = null;
+ /**
+ * @PostConstruct注解修饰的方法会在对象依赖注入完成后自动被调用,用于执行一些初始化的操作。
+ * 在这里,此方法的主要功能就是初始化Jedis连接池(JedisPool),通过配置相关参数来创建JedisPool实例。
+ */
+
@PostConstruct
public void init(){
try {
+ // 创建一个JedisPoolConfig对象,它用于配置Jedis连接池的各种属性。
JedisPoolConfig config = new JedisPoolConfig();
+ // 设置连接池允许的最大连接数,通过Parameters类的对应方法获取配置值,这个值决定了连接池最多能创建多少个Jedis连接。
config.setMaxTotal(parameters.getRedisMaxTotal());
+ // 设置连接池中空闲连接的最大数量,即当连接池中闲置的连接达到这个数量时,新的空闲连接将被释放,同样通过Parameters获取配置值。
config.setMaxIdle(parameters.getRedisMaxIdle());
+ // 设置获取连接时的最大等待时间(单位是毫秒),如果超过这个时间还获取不到连接,将会抛出异常,也是从Parameters获取对应配置。
config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
+ // 使用配置好的JedisPoolConfig以及其他必要的参数(如Redis主机地址、端口、超时时间、密码等)来创建JedisPool实例。
+ // 这里的参数 "xxx" 推测是Redis的密码(实际应用中应避免硬编码密码,可通过配置文件等更安全的方式传入),超时时间设置为2000毫秒。
jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000,"xxx");
+ // 如果初始化成功,使用日志记录一条信息,提示初始化redis连接池成功,方便查看运行状态和排查问题。
log.info("【初始化redis连接池成功】");
}catch (Exception e){
+ // 如果在初始化过程中出现任何异常,使用日志记录错误信息,提示初始化redis连接池失败,并打印出具体的异常堆栈信息,方便定位问题所在。
log.error("【初始化redis连接池失败】",e);
}
}
+ /**
+ * 对外提供获取JedisPool(Jedis连接池)的方法,外部类可以通过调用这个方法来获取已经初始化好的JedisPool实例,
+ * 进而从连接池中获取Jedis连接来操作Redis数据库。
+ * 返回值:
+ * 返回已经初始化的JedisPool对象,如果初始化失败(init方法执行出错)则返回null。
+ */
public JedisPool getJedisPool() {
return jedisPool;
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/cache/Parameters.java b/snailmall-order-service/src/main/java/com/njupt/swg/cache/Parameters.java
index 9121848..872bc29 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/cache/Parameters.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/cache/Parameters.java
@@ -9,24 +9,56 @@ import org.springframework.stereotype.Component;
* @Date 2019/1/1 14:27
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类(Parameters)主要用于存放应用程序中相关组件(如Redis、Curator等)的配置参数信息。
+ * 它被标记为Spring的组件@Component,意味着Spring容器会对其进行管理,可以进行依赖注入等操作。
+ * 同时使用了Lombok的@Data注解,这样就无需手动编写Getter、Setter、toString等方法,Lombok会在编译阶段自动帮我们生成这些常用方法,方便对类中的属性进行操作和展示。
*/
@Component
@Data
public class Parameters {
+ // Redis配置相关部分开始
/*****redis config start*******/
+
+ /**
+ * @Value("${redis.host}")注解用于从Spring的配置文件(比如application.properties或application.yml等)中读取名为"redis.host"的配置属性值,
+ * 并将其注入到这个私有属性(redisHost)中,这个属性用于存放Redis服务器的主机地址,类型为字符串。
+ */
@Value("${redis.host}")
private String redisHost;
+
+ /**
+ * 同样通过@Value注解从配置文件中读取名为"redis.port"的配置属性值,将其注入到这个属性中,用于存放Redis服务器的端口号,类型为整型。
+ */
@Value("${redis.port}")
private int redisPort;
+
+ /**
+ * 这里原本代码可能存在命名混淆(按照语义更像是应该对应'redis.max-total',但命名却是'redisMaxTotal'),不过按照@Value的作用来看,
+ * 它是从配置文件中获取名为"redis.max-total"的配置属性值,注入到这个属性中,该属性用于存放Redis连接池允许的最大连接数,类型为整型。
+ */
@Value("${redis.max-idle}")
private int redisMaxTotal;
+
+ /**
+ * 从配置文件中读取"redis.max-idle"配置属性值,注入到这个属性中,用于存放Redis连接池中空闲连接的最大数量,类型为整型。
+ */
@Value("${redis.max-total}")
private int redisMaxIdle;
+
+ /**
+ * 通过@Value注解获取配置文件中名为"redis.max-wait-millis"的配置属性值,注入到这个属性中,
+ * 此属性用于表示获取Redis连接时的最大等待时间(单位是毫秒),如果超过这个时间还获取不到连接,将会抛出异常,类型为整型。
+ */
@Value("${redis.max-wait-millis}")
private int redisMaxWaitMillis;
/*****redis config end*******/
+ // Curator(可能用于和ZooKeeper交互相关的组件)配置相关部分开始
/*****curator config start*******/
+
+ /**
+ * 从Spring配置文件中读取名为"zk.host"的配置属性值,并注入到这个属性中,用于存放ZooKeeper服务器的主机地址,类型为字符串。
+ */
@Value("${zk.host}")
private String zkHost;
/*****curator config end*******/
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/clients/CartClient.java b/snailmall-order-service/src/main/java/com/njupt/swg/clients/CartClient.java
index e40381b..d76d81c 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/clients/CartClient.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/clients/CartClient.java
@@ -9,12 +9,29 @@ import org.springframework.web.bind.annotation.RequestMapping;
* @Date 2019/1/6 20:06
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个接口(CartClient)是使用Spring Cloud OpenFeign框架定义的一个客户端接口,用于与名为"cart-service"的微服务进行交互。
+ * 通过Feign的机制,它可以以声明式的方式来调用远程的HTTP服务,就好像调用本地方法一样方便,极大地简化了微服务之间的通信代码编写。
*/
@FeignClient("cart-service")
public interface CartClient {
+
+ /**
+ * 定义了一个方法(getCartList),通过Feign客户端去调用远程"cart-service"微服务中对应的HTTP接口。
+ * 具体调用的远程接口路径是"/cart/getCartList.do",这个接口预期返回一个ServerResponse类型的响应,
+ * ServerResponse应该是项目中自定义的用于统一封装服务端响应数据的类型,包含了诸如响应状态码、消息、具体数据等信息。
+ *
+ * @return ServerResponse对象,代表从远程服务获取到的响应结果,可能包含购物车列表等相关数据。
+ */
@RequestMapping("/cart/getCartList.do")
ServerResponse getCartList();
+ /**
+ * 同样是通过Feign客户端调用远程"cart-service"微服务的一个HTTP接口的声明。
+ * 此方法对应的远程接口路径为"/cart/removeCart.do",用于请求移除购物车相关操作,
+ * 并期望从远程服务获取一个ServerResponse类型的响应,用于表示操作的结果。
+ *
+ * @return ServerResponse对象,代表从远程服务获取到的响应结果,可能包含操作是否成功等相关信息。
+ */
@RequestMapping("/cart/removeCart.do")
ServerResponse removeCart();
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/clients/KeyGenClient.java b/snailmall-order-service/src/main/java/com/njupt/swg/clients/KeyGenClient.java
index 358f5b1..fe89af3 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/clients/KeyGenClient.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/clients/KeyGenClient.java
@@ -8,9 +8,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
* @Date 2019/1/7 16:35
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个接口(KeyGenClient)是基于Spring Cloud OpenFeign框架创建的一个客户端接口,主要用于和名为"KEYGEN-SERVICE"的微服务进行交互通信。
+ * 通过使用FeignClient,能够以一种简洁、声明式的方式去调用远程微服务提供的接口,极大地简化了跨服务调用的代码编写逻辑。
*/
@FeignClient("KEYGEN-SERVICE")
public interface KeyGenClient {
+
+ /**
+ * 此方法(generateKey)通过Feign客户端向远程的"KEYGEN-SERVICE"微服务发起一个HTTP请求。
+ * 具体请求的路径是"/keygen",也就是期望调用远程微服务中对应的"/keygen"这个接口端点。
+ * 该方法的返回值类型为字符串(String),意味着它预期从远程服务的响应中获取一个字符串格式的数据,这个字符串很可能就是由"KEYGEN-SERVICE"微服务生成的某个关键信息(比如密钥之类的内容,具体取决于服务端实现)。
+ *
+ * @return 返回从远程"KEYGEN-SERVICE"微服务响应中获取到的字符串内容,可能是生成的密钥等关键信息。
+ */
@RequestMapping("/keygen")
String generateKey();
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/clients/ProductClient.java b/snailmall-order-service/src/main/java/com/njupt/swg/clients/ProductClient.java
index 46e7294..8ab65a1 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/clients/ProductClient.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/clients/ProductClient.java
@@ -10,9 +10,26 @@ import org.springframework.web.bind.annotation.RequestParam;
* @Date 2019/1/6 20:16
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个接口(ProductClient)是利用Spring Cloud OpenFeign框架构建的一个客户端接口,其作用是与名为"product-service"的微服务进行交互通信。
+ * 通过Feign的机制,能够以声明式的编程风格方便地调用远程微服务提供的接口,让跨服务间的调用代码更加简洁和易于维护。
*/
+
@FeignClient("product-service")
public interface ProductClient {
+
+ /**
+ * 此方法(queryProduct)通过Feign客户端向远程的"product-service"微服务发起一个HTTP请求,用于查询特定产品的相关信息。
+ * 具体调用的远程接口路径是"/product/queryProduct.do",意味着会请求远程微服务中对应的这个端点来获取数据。
+ *
+ * @RequestParam("productId")注解用于指定该方法的参数会作为请求参数传递给远程接口,参数名为"productId",
+ * 这里要求传入的参数类型是整数(Integer),用于明确要查询的产品的唯一标识(产品ID),这样远程的"product-service"微服务就能根据这个ID查找并返回对应的产品信息。
+ *
+ * 该方法的返回值类型是ServerResponse,这应该是项目中自定义的一个通用响应类型,用于统一封装服务端返回的各种状态信息,
+ * 在这里它会包含从远程"product-service"微服务查询到的产品相关数据。
+ *
+ * @param productId 要查询的产品的ID,类型为整数,作为请求参数传递给远程服务接口,以便服务端根据此ID查找对应的产品信息。
+ * @return ServerResponse对象,代表从远程"product-service"微服务获取到的响应结果,其中包含了所查询产品的相关信息等内容。
+ */
@RequestMapping("/product/queryProduct.do")
ServerResponse queryProduct(@RequestParam("productId") Integer productId);
}
\ No newline at end of file
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/clients/ShippingClient.java b/snailmall-order-service/src/main/java/com/njupt/swg/clients/ShippingClient.java
index 975e272..3336a17 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/clients/ShippingClient.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/clients/ShippingClient.java
@@ -10,10 +10,26 @@ import org.springframework.web.bind.annotation.RequestParam;
* @Date 2019/1/5 22:01
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个接口(ShippingClient)是基于Spring Cloud OpenFeign框架所定义的客户端接口,其主要用途是与名为"shipping-service"的微服务进行交互。
+ * 通过使用FeignClient注解,能够以一种声明式的、简洁的方式来实现对远程微服务接口的调用,使得在微服务架构下不同服务之间的通信代码编写变得更加方便和易于维护。
*/
@FeignClient("shipping-service")
public interface ShippingClient {
+ /**
+ * 此方法(getShipping)通过Feign客户端向远程的"shipping-service"微服务发起一个HTTP请求,目的是获取特定的物流信息(根据推测,从方法名来看大概率是这样的功能,具体取决于服务端实现)。
+ *
+ * @RequestMapping注解指定了要调用的远程微服务接口的路径,这里对应的路径是"/shipping/getShipping.do",也就是会向"shipping-service"微服务的这个端点发送请求。
+ *
+ * @RequestParam("shippingId")注解用于表明该方法接收的参数(shippingId)将会作为请求参数传递给远程接口,参数名就是"shippingId",并且参数类型为整数(Integer)。
+ * 这个参数用于唯一标识需要获取的那条物流信息,远程的"shipping-service"微服务会依据这个ID去查找并返回对应的物流详情等相关数据。
+ *
+ * 方法的返回值类型是ServerResponse,这通常是项目自定义的一个通用响应类型,用于对服务端返回的各种信息进行统一的封装,比如包含了响应状态、提示消息以及具体的业务数据等内容。
+ * 在此处,它将包含从远程"shipping-service"微服务获取到的关于指定物流的相关信息。
+ *
+ * @param shippingId 用于指定要获取的物流信息的唯一标识,类型为整数,该参数会作为请求参数传递给远程的"shipping-service"微服务接口,以便服务端据此查找并返回对应物流的详细内容。
+ * @return ServerResponse对象,代表从远程"shipping-service"微服务获取到的响应结果,其中包含了所请求的物流相关信息等情况。
+ */
@RequestMapping("/shipping/getShipping.do")
ServerResponse getShipping(@RequestParam("shippingId") Integer shippingId);
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/config/FeignConfiguration.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/config/FeignConfiguration.java
index 4799bf6..2680a0a 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/config/FeignConfiguration.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/config/FeignConfiguration.java
@@ -15,23 +15,41 @@ import javax.servlet.http.HttpServletRequest;
* @Date 2019/1/6 17:05
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类(FeignConfiguration)是一个Spring的配置类,用于配置Feign相关的功能。
+ * 它使用了 @Configuration 注解来表明其配置类的身份,这样Spring容器在启动时会扫描并处理这个类中的配置逻辑。
+ * 同时使用了 @Slf4j 注解,用于方便地进行日志记录,在这里主要是记录与获取Cookie相关操作过程中出现的一些情况。
*/
@Configuration
@Slf4j
public class FeignConfiguration {
+
+ /**
+ * @Bean注解用于将方法返回的对象注册为Spring容器中的一个Bean,在这里该方法定义了一个Feign的请求拦截器(RequestInterceptor)。
+ * Feign的请求拦截器可以在Feign发起请求之前对请求进行一些预处理操作,比如添加请求头、设置请求参数等。
+ * 这个特定的拦截器的主要功能是从当前的Servlet请求中获取Cookie信息,并将Cookie中的键值对添加到Feign发起的后续请求的请求头中。
+ * 这样做的目的通常是为了在微服务之间传递一些与用户认证、会话相关的信息,使得被调用的微服务能够识别请求来源等情况。
+ *
+ * @return 返回一个实现了RequestInterceptor接口的匿名内部类对象,这个对象就是定义好的Feign请求拦截器,会被Spring容器管理并在Feign请求过程中发挥作用。
+ */
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
+ // 通过RequestContextHolder获取当前线程绑定的请求相关的属性信息,这里尝试获取ServletRequestAttributes类型的属性,
+ // 它包含了与Servlet请求相关的详细信息,比如请求对象、响应对象等。如果能获取到,说明当前处于一个有效的Servlet请求上下文中。
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
+ // 从ServletRequestAttributes中获取HttpServletRequest对象,它代表了当前的Servlet请求,后续可以通过它来获取请求中的各种信息,比如Cookie等。
HttpServletRequest request = attrs.getRequest();
- // 如果在Cookie内通过如下方式取
+ // 尝试从HttpServletRequest对象中获取所有的Cookie信息,返回的是一个Cookie数组。
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
+ // 遍历获取到的Cookie数组,对于每一个Cookie对象,将其名称(cookie.getName())作为请求头的键,其值(cookie.getValue())作为请求头的值,
+ // 通过requestTemplate对象添加到Feign后续发起的请求的请求头中。这样,在Feign调用其他微服务时,这些Cookie信息就会随着请求一起发送过去。
for (Cookie cookie : cookies) {
requestTemplate.header(cookie.getName(), cookie.getValue());
}
} else {
+ // 如果没有获取到Cookie(即cookies为null或者长度为0),则记录一条警告日志,提示获取Cookie失败,方便排查可能出现的与Cookie传递相关的问题。
log.warn("FeignHeadConfiguration", "获取Cookie失败!");
}
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/constants/Constants.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/constants/Constants.java
index cf52192..5560ab9 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/constants/Constants.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/constants/Constants.java
@@ -5,33 +5,84 @@ package com.njupt.swg.common.constants;
* @Date 2019/1/1 13:19
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类(Constants)主要用于定义项目中的各种常量,将一些常用的、固定不变的值集中管理在这里,方便在整个项目中统一引用,提高代码的可读性和可维护性。
+ * 它包含了自定义的状态码、产品相关状态、订单状态枚举、支付宝回调相关常量、支付平台枚举以及支付类型枚举等不同类型的常量定义。
*/
public class Constants {
+
+ // 自定义状态码相关部分开始
/**自定义状态码 start**/
+
+ /**
+ * 定义表示请求成功的状态码常量,值为200,通常在服务端响应正常、操作成功时使用该状态码返回给客户端,方便客户端根据状态码判断请求结果。
+ */
public static final int RESP_STATUS_OK = 200;
+ /**
+ * 定义表示未授权的状态码常量,值为401,当客户端发起的请求缺少必要的认证信息或者认证失败时,服务端可以返回这个状态码,告知客户端需要重新进行认证授权操作。
+ */
public static final int RESP_STATUS_NOAUTH = 401;
+ /**
+ * 定义表示服务器内部错误的状态码常量,值为500,当服务端在处理请求过程中出现了不可预期的错误,比如代码运行时异常等情况,会使用这个状态码返回给客户端,提示客户端请求处理出现了问题。
+ */
public static final int RESP_STATUS_INTERNAL_ERROR = 500;
+ /**
+ * 定义表示请求参数错误的状态码常量,值为400,当客户端发送的请求参数不符合服务端要求,比如格式错误、缺少必要参数等情况时,服务端可以返回此状态码告知客户端请求参数有误。
+ */
public static final int RESP_STATUS_BADREQUEST = 400;
/**自定义状态码 end**/
+ // 产品状态相关部分,使用接口(一种特殊的抽象类型)来定义产品相关的状态常量,这里只是一种常量分组的方式,方便代码逻辑中针对产品状态的判断和使用。
+ /** 产品的状态 **/
+
/** 产品的状态 **/
public interface Product{
+
+ /**
+ * 定义表示产品处于开启(可用、上架等含义,具体取决于业务逻辑)状态的常量,值为1,在涉及产品状态判断和操作的代码中可以直接引用这个常量来表示相应状态。
+ */
int PRODUCT_ON = 1;
+
+ /**
+ * 定义表示产品处于关闭(不可用、下架等含义)状态的常量,值为2,用于在代码中标识产品的相应状态情况。
+ */
int PRODUCT_OFF = 2;
+
+ /**
+ * 定义表示产品已被删除的状态常量,值为3,方便在处理产品相关逻辑时判断产品是否已经被删除。
+ */
int PRODUCT_DELETED = 3;
}
-
+ // 订单状态枚举部分,使用枚举(Enum)类型来定义订单可能出现的各种状态,枚举类型本身具有更强的类型安全性和可读性,并且可以方便地进行相关逻辑处理。
public enum OrderStatusEnum{
+ /**
+ * 定义一个订单状态枚举值,表示订单已取消的状态。
+ * 参数 0 是该状态对应的代码值(可用于在数据库存储、业务逻辑判断等场景通过代码值来识别状态),"已取消"是对应的状态描述,方便直观展示给开发人员或用于日志记录等。
+ */
CANCELED(0,"已取消"),
+ /**
+ * 表示订单未支付的状态,代码值为10,描述为"未支付",在涉及订单支付流程等相关代码中可以通过这个枚举值来判断订单是否处于未支付状态。
+ */
NO_PAY(10,"未支付"),
+ /**
+ * 订单已付款的状态,代码值为20,描述为"已付款",用于标识订单已经完成支付操作的状态情况。
+ */
PAID(20,"已付款"),
+ /**
+ * 表示订单已发货的状态,代码值为40,描述为"已发货",方便在物流相关代码以及订单流转逻辑中判断订单是否已经发货。
+ */
SHIPPED(40,"已发货"),
+ /**
+ * 订单完成的状态,代码值为50,描述为"订单完成",意味着整个订单的交易流程顺利结束,所有环节都已处理完毕。
+ */
ORDER_SUCCESS(50,"订单完成"),
+ /**
+ * 订单关闭的状态,代码值为60,描述为"订单关闭",可能用于表示由于某些原因(如超时未支付等)导致订单被关闭的情况。
+ */
ORDER_CLOSE(60,"订单关闭");
@@ -39,16 +90,37 @@ public class Constants {
this.code = code;
this.value = value;
}
+ // 用于存储订单状态对应的描述信息,比如"已取消"等,是枚举值的一个属性,方便获取状态的文字描述内容。
private String value;
+ // 用于存储订单状态对应的代码值,如0、10等,也是枚举值的一个属性,便于通过代码值来识别和处理不同的订单状态。
private int code;
+ /**
+ * 获取订单状态的文字描述信息的方法,外部代码可以通过调用这个方法获取对应枚举值所代表的状态的具体描述内容。
+ *
+ * @return 返回订单状态对应的文字描述字符串,比如"已取消"等。
+ */
public String getValue() {
return value;
}
+ /**
+ * 获取订单状态对应的代码值的方法,在需要通过代码值进行逻辑判断、数据存储等操作时,可以调用此方法获取相应的值。
+ *
+ * @return 返回订单状态对应的代码值,如0、10等整数。
+ */
public int getCode() {
return code;
}
+
+ /**
+ * 根据给定的代码值查找对应的订单状态枚举值的静态方法。
+ * 它会遍历所有的枚举值(通过values()方法获取所有枚举实例),如果找到代码值匹配的枚举值,就返回该枚举值;如果遍历完都没找到匹配的,则抛出运行时异常,表示没有找到对应的枚举。
+ * 常用于根据数据库中存储的订单状态代码值来还原出对应的订单状态枚举类型,方便后续业务逻辑处理。
+ *
+ * @param code 要查找的订单状态对应的代码值,类型为整数。
+ * @return 返回匹配代码值的订单状态枚举值,如果没找到则抛出异常。
+ */
public static OrderStatusEnum codeOf(int code){
for(OrderStatusEnum orderStatusEnum : values()){
if(orderStatusEnum.getCode() == code){
@@ -59,34 +131,68 @@ public class Constants {
}
}
+ // 支付宝回调相关常量部分,使用接口来定义支付宝回调过程中涉及的一些关键字符串常量,方便在处理支付宝回调逻辑时统一使用和判断。
public interface AlipayCallback{
+ /**
+ * 定义表示支付宝交易状态为等待买家付款的常量字符串,在接收和处理支付宝回调通知时,可以通过判断这个字符串是否匹配来确定交易当前处于等待买家付款的状态。
+ */
String TRADE_STATUS_WAIT_BUYER_PAY = "WAIT_BUYER_PAY";
+ /**
+ * 定义表示支付宝交易状态为交易成功的常量字符串,用于在支付宝回调逻辑中判断交易是否已经成功完成支付操作。
+ */
String TRADE_STATUS_TRADE_SUCCESS = "TRADE_SUCCESS";
+ /**
+ * 定义表示支付宝回调响应成功的常量字符串,当服务端正确处理完支付宝回调通知后,需要按照支付宝要求返回这个字符串给支付宝服务器,表示接收和处理成功。
+ */
String RESPONSE_SUCCESS = "success";
+ /**
+ * 定义表示支付宝回调响应失败的常量字符串,若服务端处理支付宝回调通知出现问题或者不符合要求等情况,可能需要返回这个字符串给支付宝服务器,表示接收和处理失败。
+ */
String RESPONSE_FAILED = "failed";
}
+ // 支付平台枚举部分,通过枚举类型定义支付平台相关的常量信息,这里只列举了支付宝这一种支付平台示例,可根据实际业务扩展更多支付平台的枚举值。
public enum PayPlatformEnum{
+ /**
+ * 定义表示支付宝支付平台的枚举值,参数1是该支付平台对应的代码值(可用于区分不同支付平台等业务逻辑处理),"支付宝"是对应的支付平台的名称描述,方便直观展示和识别。
+ */
ALIPAY(1,"支付宝");
PayPlatformEnum(int code,String value){
this.code = code;
this.value = value;
}
+
+ // 存储支付平台的名称描述信息,如"支付宝",方便获取支付平台的文字名称。
private String value;
+ // 存储支付平台对应的代码值,如1,便于通过代码值来识别和区分不同的支付平台。
private int code;
+ /**
+ * 获取支付平台名称描述信息的方法,外部代码可以调用此方法获取对应枚举值所代表的支付平台的具体名称。
+ *
+ * @return 返回支付平台对应的文字描述字符串,如"支付宝"。
+ */
public String getValue() {
return value;
}
+ /**
+ * 获取支付平台对应的代码值的方法,在涉及根据代码值判断支付平台类型等业务逻辑时,可以调用这个方法获取相应的值。
+ *
+ * @return 返回支付平台对应的代码值,如1等整数。
+ */
public int getCode() {
return code;
}
}
+ // 支付类型枚举部分,同样使用枚举来定义支付类型相关的常量信息,这里以在线支付为例,可按需扩展其他支付类型的枚举值。
public enum PaymentTypeEnum{
+ /**
+ * 定义表示在线支付类型的枚举值,参数1是该支付类型对应的代码值,"在线支付"是对应的支付类型的文字描述,用于在业务逻辑中标识和区分不同的支付类型。
+ */
ONLINE_PAY(1,"在线支付");
PaymentTypeEnum(int code,String value){
@@ -104,7 +210,13 @@ public class Constants {
return code;
}
-
+ /**
+ * 根据给定的代码值查找对应的支付类型枚举值的静态方法,逻辑和OrderStatusEnum中的类似,遍历所有枚举值,找到代码值匹配的就返回该枚举值,没找到则抛出异常。
+ * 常用于根据业务数据中存储的支付类型代码值来还原出对应的支付类型枚举类型,便于后续业务逻辑处理。
+ *
+ * @param code 要查找的支付类型对应的代码值,类型为整数。
+ * @return 返回匹配代码值的支付类型枚举值,如果没找到则抛出异常。
+ */
public static PaymentTypeEnum codeOf(int code){
for(PaymentTypeEnum paymentTypeEnum : values()){
if(paymentTypeEnum.getCode() == code){
@@ -115,10 +227,17 @@ public class Constants {
}
}
-
+ // Redis中与产品库存相关的键的前缀常量部分,用于在使用Redis缓存产品相关数据(特别是库存数据)时,方便构建统一规范的键名。
+ /***redis product stock**/
+ /**
+ * 定义用于表示产品相关缓存键的前缀常量,在Redis中存储产品相关信息时,键名可能会以这个前缀开头,再结合具体的产品标识等信息组成完整的键名,方便区分不同用途的缓存数据。
+ */
/***redis product stock**/
public static final String PRODUCT_TOKEN_PREFIX = "product__";
+ /**
+ * 定义用于表示产品库存相关缓存键的前缀常量,在Redis中存储产品库存数据时,键名通常会以这个前缀开头,后续添加具体产品的标识等信息构成完整键名,便于对产品库存缓存数据进行管理和操作。
+ */
public static final String PRODUCT_TOKEN_STOCK_PREFIX = "product__stock_";
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/ExceptionHandlerAdvice.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/ExceptionHandlerAdvice.java
index cef87ac..ca043c1 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/ExceptionHandlerAdvice.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/ExceptionHandlerAdvice.java
@@ -13,17 +13,35 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @Date 2019/1/1 13:21
* @CONTACT 317758022@qq.com
* @DESC 全局异常处理
+ * 这个类(ExceptionHandlerAdvice)主要用于在整个应用程序范围内统一处理各种异常情况,它使用了Spring框架提供的相关注解来实现全局异常捕获和处理的功能。
+ * 通过这种方式,可以将分散在各个控制器(Controller)中的异常处理逻辑集中到一处,使代码更加清晰、易于维护,并且能保证应用程序在出现异常时返回统一格式的响应给客户端。
*/
@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionHandlerAdvice {
+
+ /**
+ * @ExceptionHandler(Exception.class)注解用于指定这个方法(handleException)要处理的异常类型为所有的Exception及其子类,也就是捕获应用程序中出现的任何未被其他更具体的异常处理器处理的异常情况。
+ * 当捕获到这类异常时,会执行这个方法中的逻辑。
+ *
+ * @param e 表示捕获到的异常对象,通过这个对象可以获取异常的相关信息,比如异常消息等内容。
+ * @return 返回一个ServerResponse对象,该对象是项目中自定义的用于统一封装服务端响应数据的类型,在这里会使用预定义的系统内部错误状态码(Constants.RESP_STATUS_INTERNAL_ERROR)以及相应的错误提示信息("系统异常,请稍后再试")来构建响应内容,告知客户端系统出现了内部错误,让客户端知晓请求处理出现了问题,并提示稍后再尝试操作。
+ */
@ExceptionHandler(Exception.class)
public ServerResponse handleException(Exception e){
+ // 使用日志记录异常的详细信息,包括异常消息(e.getMessage())以及完整的异常堆栈信息(e),方便后续排查问题,确定异常出现的原因和位置。
log.error(e.getMessage(),e);
return ServerResponse.createByErrorCodeMessage(Constants.RESP_STATUS_INTERNAL_ERROR,"系统异常,请稍后再试");
}
+ /**
+ * 同样使用了@ExceptionHandler注解,不过这里指定处理的异常类型为SnailmallException,这应该是项目中自定义的一种特定异常类型,可能用于表示业务逻辑相关的异常情况。
+ * 当捕获到SnailmallException类型的异常时,会执行这个方法来进行针对性的处理。
+ *
+ * @param e 捕获到的SnailmallException类型的异常对象,通过它可以获取该自定义异常携带的相关信息,比如异常状态码、异常消息等内容。
+ * @return 返回一个ServerResponse对象,使用从SnailmallException对象中获取的异常状态码(e.getExceptionStatus())以及异常消息(e.getMessage())来构建响应内容,将具体的业务异常相关信息返回给客户端,告知客户端出现的具体业务问题所在。
+ */
@ExceptionHandler(SnailmallException.class)
public ServerResponse handleException(SnailmallException e){
log.error(e.getMessage(),e);
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/SnailmallException.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/SnailmallException.java
index 363f19d..1d47bc1 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/SnailmallException.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/exception/SnailmallException.java
@@ -8,17 +8,39 @@ import lombok.Getter;
* @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类(SnailmallException)是自定义的一个异常类,它继承自Java的RuntimeException,属于运行时异常体系。
+ * 通常用于在项目中抛出和处理特定业务逻辑相关的异常情况,相较于受检异常(Checked Exception),运行时异常不需要在方法签名中显式声明抛出,使用起来更加灵活,符合很多业务场景下对异常处理的便利性要求。
+ * 同时,它使用了Lombok的@Getter注解,这样Lombok会在编译阶段自动为类中的属性(exceptionStatus)生成对应的Getter方法,方便外部代码获取该属性的值。
*/
@Getter
public class SnailmallException extends RuntimeException{
+
+ // 用于存储异常对应的状态码,初始值被设置为ResponseEnum.ERROR.getCode(),这里推测ResponseEnum是一个枚举类型,
+ // ERROR是其中的一个枚举值,通过调用其getCode()方法获取对应的状态码作为默认值,后续可根据不同的构造方法进行修改。
private int exceptionStatus = ResponseEnum.ERROR.getCode();
+ /**
+ * 构造方法之一,接收一个字符串类型的参数(msg),用于创建一个SnailmallException实例。
+ * 这个构造方法调用了父类(RuntimeException)的构造方法,将传入的msg作为异常消息传递给父类,以便在抛出和处理异常时能够获取到具体的错误提示信息。
+ * 此构造方法创建的异常实例会使用默认的异常状态码(即ResponseEnum.ERROR.getCode())。
+ *
+ * @param msg 异常的详细消息内容,用于描述出现异常的具体情况,比如业务逻辑中不符合规则的操作等导致的错误提示信息。
+ */
public SnailmallException(String msg){
super(msg);
}
+ /**
+ * 另一个构造方法,接收两个参数,一个整数类型的参数(code)用于指定异常的状态码,一个字符串类型的参数(msg)用于指定异常的详细消息内容。
+ * 同样,它先调用父类(RuntimeException)的构造方法,将msg作为异常消息传递给父类,然后将传入的code赋值给当前类的exceptionStatus属性,
+ * 这样就可以根据具体的业务情况自定义异常状态码和异常消息,方便在全局异常处理等地方根据不同的状态码和消息进行针对性的处理。
+ *
+ * @param code 异常对应的状态码,可根据业务规则自行定义不同的值,用于区分不同类型的业务异常情况,方便统一处理和判断。
+ * @param msg 异常的详细消息内容,描述出现异常的具体原因等信息。
+ */
public SnailmallException(int code,String msg){
super(msg);
+
exceptionStatus = code;
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ResponseEnum.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ResponseEnum.java
index e4e59c7..c3e90ec 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ResponseEnum.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ResponseEnum.java
@@ -7,17 +7,45 @@ import lombok.Getter;
* @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com
* @DESC 基本的返回状态描述
+ * 这个枚举类型(ResponseEnum)主要用于定义项目中常见的一些基本返回状态的描述信息,通过枚举的形式将不同状态对应的代码值和描述文本进行统一管理,方便在整个项目中进行引用和判断,使得返回状态的表示更加清晰、规范。
+ * 同时使用了Lombok的@Getter注解,这样Lombok会在编译阶段自动为枚举中的属性(code和desc)生成对应的Getter方法,方便外部代码获取这些属性的值,用于后续的业务逻辑处理等操作。
*/
@Getter
public enum ResponseEnum {
+ /**
+ * 定义一个表示成功的返回状态枚举值。
+ * 参数0是该状态对应的代码值,在业务逻辑中可以通过这个代码值来判断请求或操作是否成功完成,例如在接口返回数据的处理、业务流程判断等场景使用。
+ * "SUCCESS"是对应的状态描述文本,用于更直观地展示该状态的含义,比如在日志记录、给前端返回提示信息等场景使用,方便开发人员和用户理解具体的返回情况。
+ */
SUCCESS(0,"SUCCESS"),
+ /**
+ * 定义一个表示出现错误的返回状态枚举值,代码值为1,通常用于表示在业务处理过程中出现了一般性的错误情况,具体错误原因可能需要结合其他详细信息来进一步判断。
+ * 对应的描述文本为"ERROR",简单明了地传达了出现错误这一情况。
+ */
ERROR(1,"ERROR"),
+ /**
+ * 定义一个表示请求参数不合法的返回状态枚举值,代码值为2,当客户端传入的请求参数不符合服务端要求,比如格式错误、范围不符等情况时,可以使用这个枚举值来表示相应的返回状态。
+ * 描述文本为"ILLEGAL_ARGUMENTS",清晰地指出了是参数方面存在问题。
+ */
ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"),
+ /**
+ * 定义一个表示需要用户登录的返回状态枚举值,代码值为10,常用于在访问某些需要认证授权的接口或功能时,如果用户未登录,服务端就可以返回这个状态来提示客户端需要先进行登录操作才能继续访问。
+ * 描述文本为"NEED_LOGIN",明确告知了缺少登录这一前置条件。
+ */
NEED_LOGIN(10,"NEED_LOGIN");
+ // 用于存储每个枚举值对应的状态代码,方便通过Getter方法获取,在业务逻辑判断、数据传输等场景中可以依据这个代码值来识别不同的返回状态。
private int code;
+ // 用于存储每个枚举值对应的状态描述文本,同样可通过Getter方法获取,用于向客户端展示、日志记录等场景,更直观地体现返回状态的含义。
private String desc;
+ /**
+ * 枚举类型的构造方法,用于初始化每个枚举值对应的代码值(code)和描述文本(desc)属性。
+ * 在定义每个枚举值(如SUCCESS、ERROR等)时会调用这个构造方法,传入相应的参数来完成属性的赋值操作,使得每个枚举值都有其特定的代码和描述信息。
+ *
+ * @param code 对应枚举值的状态代码,类型为整数,用于在业务逻辑中区分不同的返回状态情况。
+ * @param desc 对应枚举值的状态描述文本,类型为字符串,用于直观展示返回状态的含义。
+ */
ResponseEnum(int code,String desc){
this.code = code;
this.desc = desc;
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ServerResponse.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ServerResponse.java
index 39b5f2a..25589a4 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ServerResponse.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/resp/ServerResponse.java
@@ -11,32 +11,53 @@ import java.io.Serializable;
* @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com
* @DESC 作为本项目的通用的返回封装类
+ * 这个类(ServerResponse)是项目中用于统一封装服务端返回给客户端数据的通用类,它实现了Serializable接口,意味着该类的对象可以被序列化,方便在网络传输、持久化存储等场景中使用。
+ * 通过提供多种构造方法和静态工厂方法,能够灵活地创建不同状态下带有相应信息的返回对象,使服务端返回的数据格式更加规范和易于理解。
+ * 同时使用了Lombok的@Getter注解,自动为类中的属性生成对应的Getter方法,方便外部代码获取属性值,另外还使用了Jackson相关的注解来控制JSON序列化的行为。
*/
@Getter
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class ServerResponse implements Serializable {
+ // 用于存储服务端返回的状态码,不同的状态码代表不同的返回情况,可对照项目中定义的相关状态码枚举来理解其具体含义。
private int status;
+ // 用于存储服务端返回给客户端的提示消息,通常用于向客户端传达一些简要的说明信息,比如操作成功的提示、出现错误的原因等内容。
private String msg;
+ // 泛型属性,用于存储具体的业务数据,类型可以是任意的,比如查询到的用户信息列表、商品详情等具体的业务相关数据。
private T data;
+ // 默认构造方法,主要用于在一些需要创建空对象,后续再通过其他方式设置属性值的场景,不过从当前类的设计来看,更多是通过其他特定的构造方法或静态工厂方法来创建对象。
public ServerResponse(){}
+
+ // 私有构造方法,接收一个状态码参数(status),用于创建一个只包含状态码信息的ServerResponse对象,通常在已知状态码,其他信息暂时不需要或者后续再设置的情况下使用。
private ServerResponse(int status){
this.status = status;
}
+
+ // 私有构造方法,接收状态码(status)和提示消息(msg)两个参数,用于创建一个带有状态码和提示消息的ServerResponse对象,方便在需要返回特定状态码以及对应提示消息的场景下使用。
private ServerResponse(int status,String msg){
this.status = status;
this.msg = msg;
}
+
+ // 私有构造方法,接收状态码(status)和具体业务数据(data)两个参数,用于创建一个带有状态码和具体业务数据的ServerResponse对象,适用于成功返回业务数据等情况,此时可以省略提示消息(使用默认的成功相关提示等情况)。
private ServerResponse(int status,T data){
this.status = status;
this.data = data;
}
+
+ // 私有构造方法,接收状态码(status)、提示消息(msg)和具体业务数据(data)三个参数,用于创建一个完整包含状态码、提示消息和业务数据的ServerResponse对象,可满足各种复杂的返回场景需求。
private ServerResponse(int status,String msg,T data){
this.status = status;
this.msg = msg;
this.data = data;
}
+ /**
+ * @JsonIgnore注解用于告诉Jackson在进行JSON序列化时忽略这个方法,因为这个方法主要是用于在Java代码中内部判断当前ServerResponse对象是否代表成功的返回情况,不需要将其序列化后传递给客户端。
+ * 该方法通过比较当前对象的状态码(this.status)和ResponseEnum.SUCCESS.getCode()(即表示成功的状态码)是否相等,来判断是否为成功返回,返回一个布尔值结果。
+ *
+ * @return 返回一个布尔值,true表示当前ServerResponse对象代表的是成功的返回情况(状态码与成功状态码匹配),false表示不是成功返回。
+ */
@JsonIgnore
public boolean isSuccess(){
return this.status == ResponseEnum.SUCCESS.getCode();
@@ -44,16 +65,53 @@ public class ServerResponse implements Serializable {
/**
* 成功的方法
+ * 以下是一组静态工厂方法,用于方便地创建表示成功返回情况的ServerResponse对象,遵循了工厂方法设计模式,使得创建对象的代码更加清晰、语义明确,并且符合项目中对成功返回数据格式的统一要求。
+ */
+
+ /**
+ * 创建一个表示成功的ServerResponse对象,使用ResponseEnum中定义的成功状态码(ResponseEnum.SUCCESS.getCode())以及对应的成功描述信息(ResponseEnum.SUCCESS.getDesc())来初始化对象,
+ * 通常在操作顺利完成,不需要额外传递具体业务数据和特定提示消息时使用,返回一个包含默认成功信息的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型,比如表示业务数据是整数类型等情况。
+ * @return 返回一个表示成功的ServerResponse对象,包含默认的成功状态码和描述信息,不包含具体业务数据。
*/
public static ServerResponse createBySuccess(){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc());
}
+
+ /**
+ * 创建一个表示成功且带有自定义提示消息的ServerResponse对象,使用ResponseEnum中定义的成功状态码(ResponseEnum.SUCCESS.getCode())以及传入的自定义提示消息(message)来初始化对象,
+ * 适用于操作成功但需要向客户端传递一些额外的说明信息的场景,返回一个包含成功状态码和指定提示消息的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型。
+ * @param message 自定义的提示消息内容,类型为字符串,用于向客户端传达额外的说明信息,比如操作成功后的一些温馨提示等内容。
+ * @return 返回一个表示成功且带有自定义提示消息的ServerResponse对象,包含成功状态码和指定提示消息,不包含具体业务数据。
+ */
public static ServerResponse createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message);
}
+
+ /**
+ * 创建一个表示成功且带有具体业务数据的ServerResponse对象,使用ResponseEnum中定义的成功状态码(ResponseEnum.SUCCESS.getCode())以及传入的具体业务数据(data)来初始化对象,
+ * 常用于查询操作等成功返回具体查询结果的场景,返回一个包含成功状态码和具体业务数据的ServerResponse对象,类型由调用时传入的泛型参数决定,提示消息可能使用默认的成功相关描述等情况。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型,比如可以是一个用户信息的实体类对象、商品列表等具体业务相关的数据类型。
+ * @param data 具体的业务数据,类型由泛型参数指定,用于传递给客户端的实际业务相关信息,比如查询到的用户详细信息等内容。
+ * @return 返回一个表示成功且带有具体业务数据的ServerResponse对象,包含成功状态码和具体业务数据,提示消息可能为默认值。
+ */
public static ServerResponse createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data);
}
+
+ /**
+ * 创建一个表示成功且带有自定义提示消息和具体业务数据的ServerResponse对象,使用ResponseEnum中定义的成功状态码(ResponseEnum.SUCCESS.getCode())、传入的自定义提示消息(message)以及具体业务数据(data)来初始化对象,
+ * 可满足复杂的成功返回场景,既需要传递具体业务数据又需要向客户端传达特定的提示消息,返回一个完整包含成功状态码、自定义提示消息和具体业务数据的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型。
+ * @param message 自定义的提示消息内容,类型为字符串,用于向客户端传达额外的说明信息。
+ * @param data 具体的业务数据,类型由泛型参数指定,用于传递给客户端的实际业务相关信息。
+ * @return 返回一个表示成功且带有自定义提示消息和具体业务数据的ServerResponse对象,包含成功状态码、自定义提示消息和具体业务数据。
+ */
public static ServerResponse createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data);
}
@@ -61,16 +119,40 @@ public class ServerResponse implements Serializable {
/**
* 失败的方法
*/
+
+ /**
+ * 创建一个表示失败的ServerResponse对象,使用ResponseEnum中定义的表示失败的状态码(ResponseEnum.ERROR.getCode())以及对应的失败描述信息(ResponseEnum.ERROR.getDesc())来初始化对象,
+ * 通常在出现一般性错误,不需要额外指定详细错误消息和业务数据时使用,返回一个包含默认失败信息的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型,不过在失败且无具体业务数据返回的场景下,这个泛型参数更多是一种形式上的定义。
+ * @return 返回一个表示失败的ServerResponse对象,包含默认的失败状态码和描述信息,不包含具体业务数据。
+ */
public static ServerResponse createByError(){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc());
}
+
+ /**
+ * 创建一个表示失败且带有自定义错误消息的ServerResponse对象,使用ResponseEnum中定义的表示失败的状态码(ResponseEnum.ERROR.getCode())以及传入的自定义错误消息(msg)来初始化对象,
+ * 适用于需要向客户端传达具体错误原因等情况的失败场景,返回一个包含失败状态码和指定错误消息的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型。
+ * @param msg 自定义的错误消息内容,类型为字符串,用于向客户端详细说明出现错误的原因,比如参数错误的具体情况、业务逻辑不满足的原因等内容。
+ * @return 返回一个表示失败且带有自定义错误消息的ServerResponse对象,包含失败状态码和指定错误消息,不包含具体业务数据。
+ */
public static ServerResponse createByErrorMessage(String msg){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
}
+
+ /**
+ * 创建一个表示失败且带有自定义状态码和错误消息的ServerResponse对象,使用传入的自定义状态码(code)和错误消息(msg)来初始化对象,
+ * 这种方式更加灵活,可根据不同的业务规则和具体错误情况来指定合适的状态码和错误消息,返回一个包含指定状态码和错误消息的ServerResponse对象,类型由调用时传入的泛型参数决定。
+ *
+ * @param 泛型参数,用于指定具体业务数据的类型,调用时会根据实际情况确定具体类型。
+ * @param code 自定义的状态码,类型为整数,用于明确表示具体的错误类型或不符合业务规则的情况等,可根据项目中的状态码定义规范来指定合适的值。
+ * @param msg 自定义的错误消息内容,类型为字符串,用于向客户端详细说明出现错误的原因等内容。
+ * @return 返回一个表示失败且带有自定义状态码和错误消息的ServerResponse对象,包含指定状态码和错误消息,不包含具体业务数据。
+ */
public static ServerResponse createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
}
-
-
-
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/BigDecimalUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/BigDecimalUtil.java
index 1a4397b..1662f5f 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/BigDecimalUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/BigDecimalUtil.java
@@ -7,31 +7,71 @@ import java.math.BigDecimal;
* @Date 2019/1/5 15:20
* @CONTACT 317758022@qq.com
* @DESC 将数值转换为字符串类型,然后传入BigDecimal中进行处理,防止精度丢失
+ * 这个类(BigDecimalUtil)是一个工具类,主要用于对浮点数进行精确的数学运算,避免在常规的浮点数运算中可能出现的精度丢失问题。
+ * 它提供了加、减、乘、除四种基本运算的静态方法,通过将传入的双精度浮点数(double)先转换为字符串,再构造BigDecimal对象进行运算,以此来保证运算精度。
+ * 并且将构造方法私有化,防止外部实例化该类,因为类中的方法都是静态方法,无需实例化对象就可以直接调用,符合工具类的设计模式。
*/
public class BigDecimalUtil {
+
+ // 将构造方法私有化,这样外部代码就无法通过new关键字来创建BigDecimalUtil类的实例,因为这个类的功能都是通过静态方法来提供的,不需要实例化对象去调用方法。
private BigDecimalUtil(){
}
-
+ /**
+ * 加法运算的静态方法,用于对两个双精度浮点数(double)进行精确的加法运算。
+ * 其实现原理是先把传入的两个双精度浮点数分别转换为字符串,再利用字符串构造BigDecimal对象,然后调用BigDecimal的add方法进行加法运算,最后返回运算结果。
+ * 通过这种方式可以避免直接使用双精度浮点数进行加法运算时可能出现的精度丢失问题,保证计算结果的准确性。
+ *
+ * @param v1 参与加法运算的第一个双精度浮点数。
+ * @param v2 参与加法运算的第二个双精度浮点数。
+ * @return 返回一个BigDecimal对象,表示两个输入双精度浮点数相加后的精确结果。
+ */
public static BigDecimal add(double v1, double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}
+ /**
+ * 减法运算的静态方法,功能是对两个双精度浮点数(double)进行精确的减法运算。
+ * 具体做法是先将两个传入的双精度浮点数转换为字符串形式,接着以此创建对应的BigDecimal对象,再调用BigDecimal的subtract方法来执行减法操作,最终返回计算得到的差值。
+ * 这样能有效防止在常规浮点数减法运算时出现的精度损失情况,确保减法结果的精确性。
+ *
+ * @param v1 参与减法运算的被减数,类型为双精度浮点数。
+ * @param v2 参与减法运算的减数,类型为双精度浮点数。
+ * @return 返回一个BigDecimal对象,代表两个输入双精度浮点数相减后的精确结果。
+ */
public static BigDecimal sub(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}
-
+ /**
+ * 乘法运算的静态方法,旨在对两个双精度浮点数(double)进行精确的乘法运算。
+ * 实现过程为先把要相乘的两个双精度浮点数都转换为字符串,利用这些字符串构造相应的BigDecimal对象,之后调用BigDecimal的multiply方法来执行乘法计算,返回相乘后的精确结果。
+ * 以此避免浮点数乘法运算中常见的精度丢失问题,保证乘法运算结果的准确性。
+ *
+ * @param v1 参与乘法运算的第一个双精度浮点数。
+ * @param v2 参与乘法运算的第二个双精度浮点数。
+ * @return 返回一个BigDecimal对象,它是两个输入双精度浮点数相乘后的精确结果。
+ */
public static BigDecimal mul(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}
+ /**
+ * 除法运算的静态方法,用于对两个双精度浮点数(double)进行精确的除法运算。
+ * 首先将参与除法运算的两个双精度浮点数转换为字符串,进而构建对应的BigDecimal对象,然后调用BigDecimal的divide方法进行除法操作。
+ * 这里额外指定了两个参数,其中第二个参数表示要保留的小数位数(此处设置为2位小数),第三个参数指定了舍入模式(这里采用BigDecimal.ROUND_HALF_UP,即四舍五入的舍入模式),
+ * 按照这样的设置进行除法运算并返回结果,既能保证除法运算的精度,又能按照要求对结果进行适当的舍入处理,解决除不尽时的结果表示问题。
+ *
+ * @param v1 参与除法运算的被除数,类型为双精度浮点数。
+ * @param v2 参与除法运算的除数,类型为双精度浮点数。
+ * @return 返回一个BigDecimal对象,代表两个输入双精度浮点数相除后的精确结果,按照四舍五入规则保留2位小数。
+ */
public static BigDecimal div(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/CookieUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/CookieUtil.java
index 0233e8d..3c3b3f8 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/CookieUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/CookieUtil.java
@@ -9,40 +9,65 @@ import javax.servlet.http.HttpServletResponse;
/**
* cookie读写
+ * 这个类(CookieUtil)是一个工具类,主要用于处理与Cookie相关的操作,比如在用户登录时写入登录相关的Cookie、读取登录的Cookie信息以及在用户注销时删除对应的Cookie等功能。
+ * 通过封装这些常用的Cookie操作逻辑,方便在项目的不同地方统一调用,并且使用了Slf4j进行日志记录,便于跟踪Cookie操作过程中的相关情况。
*/
@Slf4j
public class CookieUtil {
+
+ // 定义Cookie的域名,用于指定该Cookie在哪个域名下有效,这里固定设置为"oursnail.cn",意味着这个Cookie在访问该域名及其子域名下的资源时都可以被携带和使用
private final static String COOKIE_DOMAIN = "oursnail.cn";
+ // 定义Cookie的名称,这里取名为"snailmall_login_token",推测是用于存储和用户登录相关的标识信息,方便在后续的业务逻辑中通过这个名称来识别和获取对应的Cookie值。
private final static String COOKIE_NAME = "snailmall_login_token";
/**
* 登陆的时候写入cookie
- * @param response
- * @param token
+ * 这个方法(writeLoginToken)用于在用户登录成功等场景下,向客户端(浏览器)写入一个包含登录相关信息的Cookie。
+ * 它会创建一个新的Cookie对象,设置好相应的属性(如域名、路径、是否可通过脚本访问、有效期等),然后将其添加到响应(HttpServletResponse)中,使得浏览器能够接收到并保存这个Cookie。
+ *
+ * @param response 用于向客户端发送响应的对象,通过它可以添加要设置的Cookie信息,让客户端接收到并进行相应的处理(比如保存Cookie到本地等操作)。
+ * @param token 要写入Cookie的值,通常是和用户登录相关的某种标识信息(比如加密后的登录凭证等),这个值会被存储在创建的Cookie中发送给客户端。
*/
public static void writeLoginToken(HttpServletResponse response,String token){
+ // 创建一个名为COOKIE_NAME(即"snailmall_login_token"),值为传入的token的Cookie对象,这个Cookie将用于存储和传递登录相关的信息。
Cookie ck = new Cookie(COOKIE_NAME,token);
+ // 设置Cookie的域名,使其在指定的域名(COOKIE_DOMAIN,即"oursnail.cn")及其子域名下都有效,这样在访问该域名相关的网页时,浏览器会自动带上这个Cookie。
ck.setDomain(COOKIE_DOMAIN);
- ck.setPath("/");//设值在根目录
- ck.setHttpOnly(true);//不允许通过脚本访问cookie,避免脚本攻击
- ck.setMaxAge(60*60*24*365);//一年,-1表示永久,单位是秒,maxage不设置的话,cookie就不会写入硬盘,只会写在内存,只在当前页面有效
+ // 设置Cookie的路径为根目录("/"),表示这个Cookie在该域名下的所有页面请求中都会被发送给服务器,覆盖范围较广,如果设置为其他具体路径,则只在访问该路径及子路径下的页面时才会携带这个Cookie。
+ ck.setPath("/");
+ //不允许通过脚本访问cookie,避免脚本攻击
+ ck.setHttpOnly(true);
+ //一年,-1表示永久,单位是秒,maxage不设置的话,cookie就不会写入硬盘,只会写在内存,只在当前页面有效
+ ck.setMaxAge(60*60*24*365);
+ // 使用日志记录要写入的Cookie的名称和值,方便后续查看Cookie写入操作的相关情况,比如排查写入的值是否正确等问题,利于调试和跟踪。
log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
+ // 将创建并设置好属性的Cookie添加到响应对象中,这样浏览器在接收到响应后就会根据设置来保存这个Cookie,以便后续请求时携带它。
response.addCookie(ck);
}
/**
* 读取登陆的cookie
- * @param request
- * @return
+ * 该方法(readLoginToken)用于从客户端发送的请求(HttpServletRequest)中读取之前写入的、与登录相关的Cookie值(名称为COOKIE_NAME的Cookie),如果存在则返回其值,不存在则返回null。
+ * 它会遍历请求中的所有Cookie,通过比较名称来查找目标Cookie,找到后返回其值。
+ *
+ * @param request 用于接收客户端请求的对象,通过它可以获取客户端发送过来的各种信息,包括Cookie信息,这里主要是从中查找登录相关的Cookie。
+ * @return 返回从请求中读取到的名为"snailmall_login_token"的Cookie的值,如果没有找到符合要求的Cookie,则返回null。
*/
public static String readLoginToken(HttpServletRequest request){
+
+ // 从请求对象中获取所有的Cookie数组,这些Cookie是客户端随请求一起发送过来的,可能包含了之前服务器设置的各种Cookie信息。
Cookie[] cks = request.getCookies();
if(cks != null){
+ // 遍历获取到的Cookie数组,对每个Cookie进行检查
for(Cookie ck:cks){
+ // 使用日志记录每个Cookie的名称和值,方便查看请求中携带的Cookie情况,比如排查是否有预期的Cookie等问题,便于调试和跟踪。
log.info("cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
+ // 通过StringUtils工具类的equals方法(它能更好地处理null情况等)比较当前Cookie的名称和预定义的登录相关Cookie名称(COOKIE_NAME)是否相等,如果相等则说明找到了目标Cookie。
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
+ // 使用日志记录找到的目标Cookie的名称和值,方便确认读取到的内容是否正确等情况,利于调试和跟踪。
log.info("return cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
+ // 返回找到的目标Cookie的值,也就是获取到的登录相关的Cookie中存储的具体信息(比如登录凭证等内容)。
return ck.getValue();
}
}
@@ -52,18 +77,28 @@ public class CookieUtil {
/**
* 注销的时候进行删除
- * @param request
- * @param response
+ * 此方法(delLoginToken)用于在用户注销操作时,从客户端删除之前设置的与登录相关的Cookie(名称为COOKIE_NAME的Cookie)。
+ * 它同样会先遍历请求中的Cookie,找到目标Cookie后,通过设置其有效期为0等属性来通知客户端删除这个Cookie,实现注销时清除登录相关Cookie的功能。
+ *
+ * @param request 用于接收客户端请求的对象,通过它获取当前客户端携带的Cookie信息,从中查找要删除的目标Cookie。
+ * @param response 用于向客户端发送响应的对象,通过它可以设置要删除的Cookie的相关属性(如有效期等),让客户端接收到指令后删除对应的Cookie。
*/
public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
+ // 从请求对象中获取客户端发送过来的所有Cookie数组,以便从中查找要删除的登录相关Cookie。
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck:cks) {
+ // 通过比较Cookie名称,查找是否有名为COOKIE_NAME("snailmall_login_token")的Cookie,找到了就进行删除相关的操作。
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
+ // 设置Cookie的域名,保持和之前设置一致(COOKIE_DOMAIN,即"oursnail.cn"),确保操作的是同一个域名下的对应Cookie。
ck.setDomain(COOKIE_DOMAIN);
+ // 设置Cookie的路径为根目录("/"),同样要和之前设置相符,确保准确删除对应的Cookie。
ck.setPath("/");
- ck.setMaxAge(0);//0表示消除此cookie
+ // 设置Cookie的有效期为0,表示让客户端立即删除这个Cookie,下次请求时就不会再携带它了,实现了注销时清除登录相关Cookie的功能。
+ ck.setMaxAge(0);
+ // 使用日志记录要删除的Cookie的名称和值,方便查看注销操作时删除Cookie的相关情况,利于调试和跟踪。
log.info("del cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
+ // 将设置好属性(主要是有效期为0)的Cookie添加到响应对象中,通知客户端删除这个Cookie,这样客户端接收到响应后就会按照要求删除对应的Cookie。
response.addCookie(ck);
return;
}
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/DateTimeUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/DateTimeUtil.java
index 8655b2a..c70cdea 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/DateTimeUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/DateTimeUtil.java
@@ -4,65 +4,110 @@ import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
-
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @DESC 时间转换的工具类
+ * 这个类(DateTimeUtil)主要提供了一系列用于日期和时间格式转换的工具方法,能够方便地将字符串类型的日期时间与Date类型进行相互转换,还可以将Date类型转换为时间戳。
+ * 它使用了Joda-Time库来进行日期时间相关的操作,Joda-Time提供了比Java原生日期时间类更方便、功能更丰富的处理方式,同时也保留了部分使用Java原生类(如SimpleDateFormat)的方法来实现特定功能(如转换为时间戳)。
*/
public class DateTimeUtil {
- //joda-time
- //str->Date
- //Date->str
+ // joda-time
+ // 定义一个常量字符串,表示日期时间的标准格式,这里采用"yyyy-MM-dd HH:mm:ss"的格式,后续很多方法中如果没有指定特定格式时,就会默认使用这个标准格式进行转换操作。
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
-
-
- public static Date strToDate(String dateTimeStr, String formatStr){
+ /**
+ * 将字符串类型的日期时间转换为Date类型的方法,需要指定输入字符串的日期时间格式(formatStr)。
+ * 它利用Joda-Time库中的DateTimeFormatter根据传入的格式字符串来解析器格式化规则,然后使用该格式化规则去解析输入的日期时间字符串,得到一个DateTime对象,最后将DateTime对象转换为Java的Date类型并返回。
+ *
+ * @param dateTimeStr 要转换的字符串类型的日期时间内容,例如 "2024-12-10 15:30:00",其格式需要与传入的formatStr参数一致,否则解析会出错。
+ * @param formatStr 用于指定输入字符串的日期时间格式的字符串,例如 "yyyy-MM-dd HH:mm:ss" 等,根据实际输入的dateTimeStr的格式来传入对应的格式化字符串。
+ * @return 返回一个Date类型的对象,表示解析后的日期时间,如果解析失败会抛出相应的异常(由Joda-Time库内部处理或者向外抛出,取决于具体调用情况)。
+ */
+ public static Date strToDate(String dateTimeStr, String formatStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
- public static String dateToStr(Date date,String formatStr){
- if(date == null){
+ /**
+ * 将Date类型的日期时间转换为指定格式的字符串类型的方法。
+ * 首先判断传入的Date对象是否为null,如果是null则返回一个空字符串(通过StringUtils.EMPTY获取),避免后续出现空指针异常等问题。
+ * 如果Date对象不为null,则利用Joda-Time库创建一个DateTime对象(将Date对象传入构造方法),然后调用DateTime对象的toString方法并传入指定的格式字符串(formatStr),将Date对象按照指定格式转换为字符串并返回。
+ *
+ * @param date 要转换的Date类型的日期时间对象,如果为null则返回空字符串,不为null则按照指定格式进行转换。
+ * @param formatStr 用于指定输出字符串的日期时间格式的字符串,例如 "yyyy-MM-dd HH:mm:ss" 等,根据实际需求来传入对应的格式化字符串,决定转换后字符串的格式呈现。
+ * @return 返回一个字符串类型的日期时间内容,按照指定的格式进行转换后的结果,如果传入的date为null则返回空字符串。
+ */
+ public static String dateToStr(Date date, String formatStr) {
+ if (date == null) {
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
- //固定好格式
- public static Date strToDate(String dateTimeStr){
+ /**
+ * 将字符串类型的日期时间转换为Date类型的方法,使用类中定义的标准格式(STANDARD_FORMAT,即"yyyy-MM-dd HH:mm:ss")进行解析。
+ * 其内部逻辑与strToDate(String dateTimeStr, String formatStr)方法类似,不过这里固定使用了预定义的标准格式去解析输入的日期时间字符串,得到DateTime对象后再转换为Date类型返回。
+ *
+ * @param dateTimeStr 要转换的字符串类型的日期时间内容,其格式需要符合STANDARD_FORMAT("yyyy-MM-dd HH:mm:ss")的规范,否则解析会出错。
+ * @return 返回一个Date类型的对象,表示按照标准格式解析后的日期时间,如果解析失败会抛出相应的异常(由Joda-Time库内部处理或者向外抛出,取决于具体调用情况)。
+ */
+ public static Date strToDate(String dateTimeStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
- public static String dateToStr(Date date){
- if(date == null){
+ /**
+ * 将Date类型的日期时间转换为字符串类型的方法,使用类中定义的标准格式(STANDARD_FORMAT,即"yyyy-MM-dd HH:mm:ss")进行转换。
+ * 先判断传入的Date对象是否为null,若为null则返回空字符串,不为null则创建DateTime对象并按照标准格式将其转换为字符串后返回,逻辑与dateToStr(Date date, String formatStr)方法类似,只是这里固定用标准格式转换。
+ *
+ * @param date 要转换的Date类型的日期时间对象,如果为null则返回空字符串,不为null则按照标准格式进行转换。
+ * @return 返回一个字符串类型的日期时间内容,按照标准格式("yyyy-MM-dd HH:mm:ss")转换后的结果,如果传入的date为null则返回空字符串。
+ */
+ public static String dateToStr(Date date) {
+ if (date == null) {
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
- //Date -> 时间戳
+ /**
+ * 将Date类型的日期时间转换为时间戳(从1970年1月1日00:00:00 UTC到指定日期时间的毫秒数)的方法。
+ * 首先判断传入的Date对象是否为null,如果为null则直接返回null。如果不为null,则创建一个SimpleDateFormat对象,使用类中定义的标准格式("yyyy-MM-dd HH:mm:ss")进行格式化设置。
+ * 然后将Date对象转换为字符串,再使用SimpleDateFormat的parse方法将该字符串解析回Date类型(这里其实是为了处理时区等相关的格式化问题,确保转换准确),最后获取该Date对象对应的时间戳(通过getTime方法获取从1970年1月1日以来的毫秒数)并返回。
+ * 注意,这个方法在解析过程中如果出现格式不匹配等问题会抛出ParseException异常,需要调用者进行相应的处理。
+ *
+ * @param date 要转换为时间戳的Date类型的日期时间对象,如果为null则返回null,不为null则进行时间戳的转换操作。
+ * @return 返回一个Long类型的时间戳数值,表示从1970年1月1日00:00:00 UTC到传入的日期时间对应的毫秒数,如果传入的date为null则返回null,若解析过程出现问题会抛出ParseException异常。
+ * @throws ParseException 如果在使用SimpleDateFormat解析日期时间字符串时出现格式不匹配等解析错误情况,会抛出此异常,需要调用者进行捕获和处理。
+ */
public static Long dateToChuo(Date date) throws ParseException {
- if(date == null){
+ if (date == null) {
return null;
}
- SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.parse(String.valueOf(date)).getTime();
}
+ /**
+ * 主方法,用于简单的测试功能,这里主要演示了将一个指定格式的字符串类型的日期时间转换为Date类型,再获取其对应的时间戳并输出的过程。
+ * 不过在实际应用中,这种测试代码通常可以移到单元测试类中,使得工具类的功能更加纯粹,专注于提供日期时间转换相关的工具方法。
+ * 注意,这个方法中如果在解析日期时间字符串时出现格式不匹配等问题会抛出ParseException异常,因为调用了SimpleDateFormat的parse方法,需要在调用main方法所在的环境中进行相应的异常处理(比如在命令行运行时可能会看到异常堆栈信息等情况)。
+ *
+ * @param args 命令行参数,在这里没有实际使用到,一般用于接收外部传入的一些配置参数等信息(在命令行运行时传入),但本示例中没有相关逻辑涉及。
+ * @throws ParseException 如果在使用SimpleDateFormat解析日期时间字符串时出现格式不匹配等解析错误情况,会抛出此异常,需要进行捕获和处理,这里声明抛出,由调用者(运行main方法的环境)处理。
+ */
public static void main(String[] args) throws ParseException {
- SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
- String time="1970-01-06 11:45:55";
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ String time = "1970-01-06 11:45:55";
Date date = format.parse(time);
- System.out.print("Format To times:"+date.getTime());
+ System.out.print("Format To times:" + date.getTime());
}
-}
+}
\ No newline at end of file
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/FtpUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/FtpUtil.java
index 5963445..5559f97 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/FtpUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/FtpUtil.java
@@ -3,7 +3,6 @@ package com.njupt.swg.common.utils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -14,77 +13,141 @@ import java.util.List;
* @Date 2018/1/11 14:32
* @DESC
* @CONTACT 317758022@qq.com
+ * 这个类(FtpUtil)是一个用于操作FTP服务器的工具类,主要功能是实现向FTP服务器上传文件的操作。
+ * 它使用了Apache Commons Net库中的FTPClient来与FTP服务器进行交互,通过封装连接服务器、设置相关参数以及上传文件等操作逻辑,方便在项目中统一进行FTP文件上传相关的业务处理。
+ * 同时使用了Lombok的@Data注解来自动生成类中属性的Getter、Setter等方法,以及@Slf4j注解来方便地进行日志记录,便于跟踪FTP操作过程中出现的各种情况。
*/
@Data
@Slf4j
public class FtpUtil {
+
+ // 通过PropertiesUtil工具类(这里未展示其代码,但推测是用于读取配置文件属性的类)从配置文件中读取FTP服务器的IP地址,并赋值给这个静态变量,用于后续连接FTP服务器时指定IP。
private static String ftpIp = PropertiesUtil.getProperty("ftp.server.ip");
+ // 同样通过PropertiesUtil从配置文件中读取FTP服务器的用户名,并赋值给这个静态变量,用于登录FTP服务器时提供用户名。
private static String ftpUser = PropertiesUtil.getProperty("ftp.user");
+ // 通过PropertiesUtil从配置文件中读取FTP服务器的密码,并赋值给这个静态变量,用于登录FTP服务器时提供密码。
private static String ftpPass = PropertiesUtil.getProperty("ftp.pass");
- public FtpUtil(String ip, int port, String user, String pwd){
+ /**
+ * 构造方法,用于创建FtpUtil类的实例对象,接收FTP服务器的IP地址(ip)、端口号(port)、用户名(user)和密码(pwd)作为参数,
+ * 将这些参数赋值给对应的实例变量,方便后续在连接和操作FTP服务器时使用这些特定的配置信息(如果不是使用默认的配置,可通过这个构造方法传入不同的值来进行定制化操作)。
+ *
+ * @param ip FTP服务器的IP地址,指定要连接的服务器的网络地址。
+ * @param port FTP服务器的端口号,通常FTP默认端口是21,但也可以根据实际服务器配置传入不同的端口值。
+ * @param user 登录FTP服务器的用户名,用于身份验证,获取相应的操作权限。
+ * @param pwd 登录FTP服务器的密码,与用户名配合进行身份验证,确保有权限进行后续的文件上传等操作。
+ */
+ public FtpUtil(String ip, int port, String user, String pwd) {
this.ip = ip;
this.port = port;
this.user = user;
this.pwd = pwd;
}
+
+ /**
+ * 静态的文件上传方法,用于将给定的文件列表(fileList)上传到FTP服务器上。
+ * 它首先会创建一个FtpUtil类的实例对象,使用从配置文件中读取的默认FTP服务器IP、默认用户名和默认密码(端口号使用默认的21)来初始化这个实例,
+ * 然后调用实例对象的另一个重载的uploadFile方法(传入远程路径和文件列表参数)进行实际的文件上传操作,并记录相应的日志信息,最后返回文件上传的结果(是否成功上传所有文件)。
+ * 注意,这个方法会抛出IOException异常,调用者需要进行相应的异常处理,因为在文件上传以及与FTP服务器连接交互过程中可能出现I/O相关的异常情况。
+ *
+ * @param fileList 要上传到FTP服务器的文件列表,类型为List,可以包含多个本地文件对象,这些文件将会被逐个上传到FTP服务器指定路径下。
+ * @return 返回一个布尔值,表示文件上传操作是否成功,如果所有文件都成功上传则返回true,只要有一个文件上传失败就返回false。
+ * @throws IOException 如果在文件上传过程中,比如连接FTP服务器、读取本地文件流、向FTP服务器写入文件等操作出现I/O相关的异常时,会抛出此异常,需要调用者捕获并处理。
+ */
public static boolean uploadFile(List fileList) throws IOException {
- FtpUtil ftpUtil = new FtpUtil(ftpIp,21,ftpUser,ftpPass);
+ FtpUtil ftpUtil = new FtpUtil(ftpIp, 21, ftpUser, ftpPass);
log.info("开始连接ftp服务器");
- boolean result = ftpUtil.uploadFile("img",fileList);
+ boolean result = ftpUtil.uploadFile("img", fileList);
log.info("开始连接ftp服务器,结束上传,上传结果:{}");
return result;
}
-
- private boolean uploadFile(String remotePath,List fileList) throws IOException {
+ /**
+ * 私有方法,用于实际执行将文件列表上传到FTP服务器指定远程路径(remotePath)下的操作。
+ * 它首先初始化一个布尔变量uploaded为true,表示默认文件上传成功,然后创建一个文件输入流(FileInputStream)用于读取本地文件内容,接着尝试连接FTP服务器,
+ * 如果连接成功,会进行一系列FTP客户端相关的配置操作(如切换工作目录、设置缓冲区大小、编码、文件类型以及进入被动模式等),之后遍历文件列表,逐个将文件通过文件输入流上传到FTP服务器,
+ * 在这个过程中如果出现IOException异常,则将uploaded设置为false,并打印异常堆栈信息,最后关闭文件输入流并断开与FTP服务器的连接,无论是否成功上传,最终都会返回文件上传的整体结果(是否所有文件都成功上传)。
+ *
+ * @param remotePath 要上传文件到FTP服务器的远程路径,指定文件在FTP服务器上存放的目录位置,例如 "/img" 等表示将文件上传到FTP服务器的img目录下。
+ * @param fileList 要上传到FTP服务器的文件列表,包含了多个本地文件对象,这些文件将会被逐个上传到指定的远程路径下。
+ * @return 返回一个布尔值,表示文件上传操作是否成功,如果所有文件都成功上传则返回true,只要有一个文件上传失败就返回false,在出现异常或者部分文件上传失败时会返回false。
+ * @throws IOException 如果在连接FTP服务器、读取本地文件流、向FTP服务器写入文件等操作出现I/O相关的异常时,会抛出此异常,需要调用者捕获并处理。
+ */
+ private boolean uploadFile(String remotePath, List fileList) throws IOException {
boolean uploaded = true;
FileInputStream fis = null;
- //连接FTP服务器
- if(connectServer(this.ip,this.port,this.user,this.pwd)){
+ // 连接FTP服务器,调用connectServer方法进行连接操作,如果连接成功则继续后续的文件上传相关配置和操作,否则根据异常处理逻辑设置uploaded为false等情况。
+ if (connectServer(this.ip, this.port, this.user, this.pwd)) {
try {
+ // 切换FTP客户端的工作目录到指定的远程路径下,确保后续上传的文件会存放在这个目录中,例如将工作目录切换到FTP服务器的img目录下,方便文件分类存放等管理。
ftpClient.changeWorkingDirectory(remotePath);
+ // 设置FTP客户端的缓冲区大小为1024字节,缓冲区用于临时存储数据,合适的缓冲区大小可以在一定程度上提高文件传输效率等性能表现。
ftpClient.setBufferSize(1024);
+ // 设置FTP客户端的控制编码为"UTF-8",确保在与FTP服务器交互过程中,命令和相关文本信息的编码统一,避免出现乱码等问题,特别是处理包含中文等多字节字符的文件名等情况。
ftpClient.setControlEncoding("UTF-8");
+ // 设置FTP客户端传输的文件类型为二进制文件类型(FTPClient.BINARY_FILE_TYPE),适用于上传各种类型的文件(如图像、视频、文档等),与ASCII文件类型区分开,确保文件内容准确传输,不会出现格式损坏等情况。
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ // 使FTP客户端进入本地被动模式,这是一种常见的FTP数据传输模式,在很多网络环境下(特别是客户端处于防火墙或NAT之后等情况),可以更好地保证数据传输的顺利进行,避免连接问题。
ftpClient.enterLocalPassiveMode();
- for(File fileItem : fileList){
+ for (File fileItem : fileList) {
+ // 为每个要上传的文件创建一个文件输入流,用于读取本地文件的内容,以便后续通过FTP客户端将文件内容发送到FTP服务器上进行存储。
fis = new FileInputStream(fileItem);
- ftpClient.storeFile(fileItem.getName(),fis);
+ // 使用FTP客户端将本地文件上传到FTP服务器,以文件的名称(fileItem.getName())作为在FTP服务器上存储的文件名,通过文件输入流(fis)将文件内容传输过去进行存储。
+ ftpClient.storeFile(fileItem.getName(), fis);
}
} catch (IOException e) {
- log.error("上传文件异常",e);
+ // 如果在文件上传过程中出现I/O相关的异常情况,使用日志记录异常信息,方便后续排查问题,确定是哪个环节出现了错误导致文件上传失败。
+ log.error("上传文件异常", e);
uploaded = false;
e.printStackTrace();
} finally {
- fis.close();
+ // 无论文件上传是否成功,都需要关闭文件输入流,释放相关的系统资源,避免资源泄漏等问题。
+ if (fis!= null) {
+ fis.close();
+ }
+ // 断开与FTP服务器的连接,释放与FTP服务器连接相关的资源,结束本次文件上传操作对应的FTP连接会话。
ftpClient.disconnect();
}
}
return uploaded;
}
-
-
- private boolean connectServer(String ip,int port,String user,String pwd){
+ /**
+ * 私有方法,用于连接FTP服务器,根据传入的服务器IP地址(ip)、端口号(port)、用户名(user)和密码(pwd)进行连接和登录操作。
+ * 它首先创建一个FTPClient对象,然后尝试使用提供的IP地址连接FTP服务器,连接成功后再使用用户名和密码进行登录操作,根据登录结果返回一个布尔值表示是否连接成功,
+ * 如果在连接或登录过程中出现IOException异常,会记录异常信息到日志中,方便排查FTP服务器连接相关的问题。
+ *
+ * @param ip FTP服务器的IP地址,指定要连接的服务器的网络地址。
+ * @param port FTP服务器的端口号,通常FTP默认端口是21,但也可以根据实际服务器配置传入不同的端口值。
+ * @param user 登录FTP服务器的用户名,用于身份验证,获取相应的操作权限。
+ * @param pwd 登录FTP服务器的密码,与用户名配合进行身份验证,确保有权限进行后续的文件上传等操作。
+ * @return 返回一个布尔值,表示是否成功连接并登录到FTP服务器,如果连接和登录都成功则返回true,否则返回false。
+ */
+ private boolean connectServer(String ip, int port, String user, String pwd) {
boolean isSuccess = false;
ftpClient = new FTPClient();
try {
+ // 使用FTPClient对象的connect方法尝试连接指定IP地址的FTP服务器,连接的端口号通过传入的port参数指定。
ftpClient.connect(ip);
- isSuccess = ftpClient.login(user,pwd);
+ // 使用FTPClient对象的login方法,传入用户名(user)和密码(pwd)进行登录操作,登录成功则将isSuccess设置为true,否则保持为false。
+ isSuccess = ftpClient.login(user, pwd);
} catch (IOException e) {
- log.error("连接FTP服务器异常",e);
+ // 如果在连接FTP服务器或者登录过程中出现I/O相关的异常情况,使用日志记录异常信息,方便后续排查问题,确定是网络连接问题还是用户名密码等认证问题导致无法连接FTP服务器。
+ log.error("连接FTP服务器异常", e);
}
return isSuccess;
}
-
-
+ // 用于存储FTP服务器的IP地址,通过构造方法或者默认配置赋值,在连接和操作FTP服务器时使用这个IP地址进行网络连接。
private String ip;
+ // 用于存储FTP服务器的端口号,通过构造方法传入或者使用默认值(如常见的21端口),在连接FTP服务器时指定要连接的端口。
private int port;
+ // 用于存储登录FTP服务器的用户名,通过构造方法传入或者使用从配置文件中读取的默认用户名,用于在连接FTP服务器时进行身份验证,获取操作权限。
private String user;
+ // 用于存储登录FTP服务器的密码,通过构造方法传入或者使用从配置文件中读取的默认密码,与用户名配合进行身份验证,确保有权限进行文件上传等操作。
private String pwd;
+ // FTPClient对象,用于与FTP服务器进行实际的交互操作,比如连接服务器、切换目录、上传文件等功能都是通过这个对象来实现的,它是Apache Commons Net库中提供的用于操作FTP服务器的核心类。
private FTPClient ftpClient;
-}
+}
\ No newline at end of file
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
index 12daca4..716aff1 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/JsonUtil.java
@@ -8,119 +8,162 @@ 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)是一个工具类,主要用于处理JSON数据的序列化(将Java对象转换为JSON字符串)和反序列化(将JSON字符串转换为Java对象)操作。
+ * 它基于Jackson库(这里使用的是Codehaus的Jackson版本)进行相关功能实现,通过配置ObjectMapper对象的一些属性来定制序列化和反序列化的行为,使得在项目中可以按照统一的规则进行JSON数据处理,方便且易于维护。
+ * 同时使用了Slf4j进行日志记录,便于在序列化或反序列化出现错误时记录相关信息,方便排查问题。
*/
@Slf4j
public class JsonUtil {
+
+ // 创建一个ObjectMapper对象,它是Jackson库中用于进行JSON序列化和反序列化的核心类,后续的各种配置以及序列化、反序列化操作都是基于这个对象来完成的。
+ // 由于ObjectMapper是线程安全的,所以整个应用中可以共享使用这一个实例对象来进行JSON处理,提高性能和资源利用率。
private static ObjectMapper objectMapper = new ObjectMapper();
+ // 静态代码块,在类加载时执行,用于对ObjectMapper对象进行一系列的配置,以定制JSON序列化和反序列化的行为,确保符合项目的需求和规范。
static {
- //所有字段都列入进行转换
+ // 所有字段都列入进行转换
+ // 设置序列化时包含所有的字段,即无论字段的值是否为null,都会被包含在生成的JSON字符串中,通过指定JsonSerialize.Inclusion.ALWAYS来实现这一配置,
+ // 这样可以保证对象的完整结构都能在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);
- //统一时间的格式
+
+ // 取消默认转换timestamp形式
+ // 配置ObjectMapper在序列化日期类型的属性时,不将其转换为时间戳的形式(默认情况下Jackson可能会把日期转换为时间戳),而是按照后续设置的日期格式进行转换,
+ // 通过将SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS设置为false来取消默认的时间戳转换行为,使得日期在JSON中以更易读的格式化字符串形式呈现。
+ objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
+
+ // 忽略空bean转json的错误
+ // 配置ObjectMapper在尝试将一个空的Java Bean(即没有任何属性值的对象)转换为JSON字符串时,忽略可能出现的错误,直接返回一个空的JSON对象({})或者空数组([])等合适的表示形式,
+ // 通过将SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS设置为false来避免因为空Bean转换导致的序列化失败情况,增强程序的健壮性。
+ objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
+
+ // 统一时间的格式
+ // 设置日期的格式化格式,使用了项目中可能自定义的标准日期时间格式(DateTimeUtil.STANDARD_FORMAT,推测是类似"yyyy-MM-dd HH:mm:ss"这样的格式),
+ // 通过创建SimpleDateFormat对象并将其设置给ObjectMapper,使得在序列化和反序列化日期类型数据时,都按照这个统一的格式进行处理,便于数据的一致性和可读性。
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
- //忽略json存在属性,但是java对象不存在属性的错误
- objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
+
+ // 忽略json存在属性,但是java对象不存在属性的错误
+ // 配置ObjectMapper在进行反序列化时,如果JSON字符串中存在一些Java对象中没有对应的属性(比如JSON数据多了一些额外字段),忽略这样的属性不匹配错误,正常进行反序列化操作,
+ // 通过将DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES设置为false来实现,避免因为JSON数据结构和Java对象不完全匹配而导致反序列化失败,提高对不同来源JSON数据的兼容性。
+ objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* 序列化方法,将对象转为字符串
- * @param obj
- * @param
- * @return
+ * 这个方法(obj2String)用于将给定的Java对象转换为JSON字符串表示形式。
+ * 如果传入的对象为null,则直接返回null;如果对象本身就是字符串类型(通过instanceof判断),则直接返回该字符串对象;否则使用配置好的ObjectMapper对象将其转换为JSON字符串,
+ * 在转换过程中如果出现IOException异常(比如对象的属性类型不支持序列化等情况),则记录警告日志,并返回null,表示转换失败。
+ *
+ * @param obj 要转换为JSON字符串的Java对象,可以是任意类型的Java对象,例如POJO(普通Java对象)、集合、Map等,只要其属性类型都能被Jackson正确序列化即可。
+ * @param 泛型参数,用于表示传入对象的类型,在方法返回时也确保返回的JSON字符串对应的Java对象类型符合传入时的类型约束。
+ * @return 返回一个JSON字符串,表示传入的Java对象的JSON序列化结果,如果传入对象为null或者序列化过程出现异常则返回null。
*/
- 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
+ * 此方法(obj2StringPretty)与obj2String方法功能类似,也是将Java对象转换为JSON字符串,但它会输出美化后的JSON字符串格式(通常有缩进、换行等,更易读),方便在测试阶段查看JSON数据的结构和内容。
+ * 如果传入对象为null则返回null,对象为字符串类型时直接返回该字符串,否则使用配置好的ObjectMapper对象的writerWithDefaultPrettyPrinter方法获取一个美化格式的写入器,再将对象转换为美化后的JSON字符串,
+ * 同样在出现IOException异常时记录警告日志并返回null表示转换失败。
+ *
+ * @param obj 要转换为美化格式JSON字符串的Java对象,类型要求与obj2String方法中的obj参数一致,可以是各种Java对象类型。
+ * @param 泛型参数,用于表示传入对象的类型以及返回的JSON字符串对应的Java对象类型约束。
+ * @return 返回一个美化格式的JSON字符串,表示传入的Java对象的JSON序列化结果,如果传入对象为null或者序列化过程出现异常则返回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
+ * 该方法(String2Obj)用于将一个JSON字符串转换为指定类型(通过clazz参数指定)的Java对象,实现了简单的反序列化功能。
+ * 首先会判断传入的JSON字符串是否为空以及指定的目标Java类(clazz)是否为null,如果有任何一个为空则直接返回null,表示无法进行反序列化操作。
+ * 如果JSON字符串不为空且目标类也不为null,当目标类是String类型时(通过equals判断),则直接将JSON字符串强制转换为对应的类型并返回(因为本身就是字符串形式,无需进一步反序列化);
+ * 否则使用配置好的ObjectMapper对象将JSON字符串按照指定的Java类(clazz)进行反序列化操作,在出现IOException异常时记录警告日志并返回null,表示反序列化失败。
+ *
+ * @param str 要进行反序列化的JSON字符串,其内容需要符合对应Java对象的结构和数据类型要求,否则可能导致反序列化失败。
+ * @param clazz 目标Java类,用于指定要将JSON字符串反序列化为什么类型的Java对象,例如某个POJO类、基本数据类型的包装类等。
+ * @param 泛型参数,用于表示目标Java类的类型以及返回的Java对象的类型约束,确保返回的对象类型与指定的目标类类型一致。
+ * @return 返回一个指定类型(clazz类型)的Java对象,表示将JSON字符串反序列化后的结果,如果传入的JSON字符串为空、目标类为null或者反序列化过程出现异常则返回null。
*/
- 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
+ * 这个方法(Str2Obj)用于处理复杂对象的反序列化操作,适用于那些不能简单通过指定一个具体Java类(如前面String2Obj方法那样)来进行反序列化的情况,例如处理包含泛型、嵌套复杂结构的JSON数据对应的Java对象。
+ * 它通过TypeReference类型来指定复杂的目标对象类型信息,先判断传入的JSON字符串是否为空以及TypeReference是否为null,如果有任何一个为空则直接返回null,表示无法进行反序列化操作。
+ * 如果JSON字符串和TypeReference都不为空,当TypeReference表示的类型是String类型时(通过getType方法判断),则直接返回JSON字符串(因为本身就是字符串形式,无需进一步反序列化);
+ * 否则使用配置好的ObjectMapper对象将JSON字符串按照TypeReference指定的复杂类型进行反序列化操作,在出现IOException异常时记录警告日志并返回null,表示反序列化失败。
+ *
+ * @param str 要进行反序列化的JSON字符串,其内容结构需要符合TypeReference指定的复杂对象结构和数据类型要求,否则可能导致反序列化失败。
+ * @param typeReference TypeReference对象,用于指定复杂的目标对象类型信息,例如可以用来表示List、Map等带有泛型或者嵌套结构的复杂类型,方便准确地对复杂JSON数据进行反序列化。
+ * @param 泛型参数,用于表示根据TypeReference确定的目标复杂对象的类型以及返回的Java对象的类型约束,确保返回的对象类型与指定的复杂类型一致。
+ * @return 返回一个符合TypeReference指定类型的Java对象,表示将JSON字符串反序列化后的结果,如果传入的JSON字符串为空、TypeReference为null或者反序列化过程出现异常则返回null。
*/
- 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
+ * 此方法(Str2Obj)提供了另一种实现复杂对象反序列化的方式,通过传入集合类(collectionClass)以及元素类(elementClasses)的方式来构建JavaType对象,从而指定复杂对象的类型结构,再使用ObjectMapper进行反序列化操作。
+ * 它首先使用ObjectMapper的getTypeFactory方法获取类型工厂对象,然后通过constructParametricType方法根据传入的集合类和元素类构建JavaType对象,这个JavaType对象就代表了要反序列化的复杂对象类型,
+ * 接着使用ObjectMapper将传入的JSON字符串按照构建好的JavaType对象进行反序列化操作,在出现IOException异常时记录警告日志并返回null,表示反序列化失败。
+ * 这种方式适用于处理像List、Set等集合类型且集合中元素类型明确的复杂对象的反序列化场景,通过指定集合类和元素类来准确还原对应的Java对象结构。
+ *
+ * @param str 要进行反序列化的JSON字符串,其内容结构需要符合通过collectionClass和elementClasses构建的复杂对象结构和数据类型要求,否则可能导致反序列化失败。
+ * @param collectionClass 集合类,用于指定复杂对象中最外层的集合类型,例如List.class、Set.class等,表示要反序列化的对象是哪种集合类型的结构。
+ * @param elementClasses 元素类,用于指定集合中元素的类型,可以传入多个元素类来表示更复杂的嵌套集合或者带有泛型参数的集合等情况,例如对于List>,可以传入List.class和SomeClass.class来构建对应的JavaType对象。
+ * @param 泛型参数,用于表示根据传入的集合类和元素类构建的复杂对象的类型以及返回的Java对象的类型约束,确保返回的对象类型与构建的复杂类型一致。
+ * @return 返回一个符合通过collectionClass和elementClasses构建的复杂对象类型的Java对象,表示将JSON字符串反序列化后的结果,如果在反序列化过程中出现IOException异常则返回null。
*/
- 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-order-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
index e6e5c8a..60b3216 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/MD5Util.java
@@ -4,8 +4,19 @@ import java.security.MessageDigest;
/**
* MD5加密工具类
+ * 这个类(MD5Util)主要用于对字符串进行MD5加密操作,提供了相应的方法将输入的字符串转换为其MD5加密后的字符串表示形式,常用于对敏感信息(如密码等)进行加密处理,以保障数据的安全性。
+ * 它内部实现了一些辅助方法用于字节数组与十六进制字符串之间的转换以及具体的MD5加密逻辑,并且提供了方便调用的对外公开方法,以按照特定编码(如UTF-8)进行MD5加密。
*/
public class MD5Util {
+
+ /**
+ * 将字节数组转换为十六进制字符串的私有方法。
+ * 它通过遍历字节数组中的每个字节,调用byteToHexString方法将每个字节转换为对应的两位十六进制字符串表示形式,然后将这些十六进制字符串依次拼接起来,最终返回拼接后的十六进制字符串结果。
+ * 这个方法主要用于在MD5加密过程中,将加密后得到的字节数组结果转换为便于查看和存储的十六进制字符串形式。
+ *
+ * @param b 要转换为十六进制字符串的字节数组,通常是经过MD5加密算法处理后得到的字节数组数据,例如MessageDigest对象的digest方法返回的字节数组。
+ * @return 返回一个由字节数组转换而来的十六进制字符串,代表了对应字节数组的十六进制表示形式,可用于展示MD5加密后的结果等情况。
+ */
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
@@ -14,6 +25,15 @@ public class MD5Util {
return resultSb.toString();
}
+ /**
+ * 将单个字节转换为两位十六进制字符串的私有方法。
+ * 首先会对传入的字节进行处理,如果字节值小于0(在Java中字节是有符号的,范围是 -128 到 127),则将其加上256转换为无符号的整数值(使其范围变为0到255),方便后续计算十六进制表示。
+ * 然后通过除以16取整(得到高位十六进制数字对应的索引值)和取余(得到低位十六进制数字对应的索引值)操作,从预定义的十六进制数字字符数组(hexDigits)中获取对应的十六进制字符,
+ * 并将这两个十六进制字符拼接起来,返回表示该字节的两位十六进制字符串,用于在byteArrayToHexString方法中逐个字节构建完整的十六进制字符串表示。
+ *
+ * @param b 要转换为十六进制字符串的单个字节数据,通常是字节数组中的一个元素,例如经过MD5加密后字节数组中的某个字节。
+ * @return 返回一个两位的十六进制字符串,表示传入字节对应的十六进制值,例如字节值为10,会返回"0a"这样的十六进制字符串表示形式。
+ */
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
@@ -24,11 +44,15 @@ public class MD5Util {
}
/**
- * 返回大写MD5
+ * 执行MD5加密并返回大写MD5结果字符串的私有方法。
+ * 首先初始化一个结果字符串为输入的原始字符串(origin)的副本,然后通过Java的安全类MessageDigest获取一个MD5算法的实例对象,用于执行MD5加密操作。
+ * 如果传入的字符编码名称(charsetname)为null或者空字符串,则直接使用默认的字节编码方式将结果字符串转换为字节数组,再让MD5算法实例对其进行加密处理,最后将加密后得到的字节数组通过byteArrayToHexString方法转换为十六进制字符串作为结果返回;
+ * 如果传入了有效的字符编码名称(如"utf-8"等),则按照指定的字符编码将结果字符串转换为字节数组,再进行MD5加密和十六进制字符串转换操作,最终返回加密后的十六进制字符串结果,并且将结果转换为大写形式(通过toUpperCase方法)返回,方便统一格式展示等应用场景。
+ * 注意,这个方法在获取MessageDigest实例或者进行加密操作出现异常时,只是简单地忽略异常(虽然这种处理方式不太完善,在实际应用中可能需要更好的异常处理逻辑),直接返回null作为结果。
*
- * @param origin
- * @param charsetname
- * @return
+ * @param origin 要进行MD5加密的原始字符串,例如可以是用户输入的密码等需要加密存储的敏感信息字符串。
+ * @param charsetname 用于指定将原始字符串转换为字节数组时采用的字符编码名称,如"utf-8"、"gbk"等,如果为null或空字符串则使用默认编码方式,不同编码方式可能会导致转换后的字节数组不同,进而影响MD5加密结果的十六进制表示形式。
+ * @return 返回一个大写的十六进制字符串,表示原始字符串经过MD5加密后的结果,如果加密过程出现异常则返回null。
*/
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
@@ -44,16 +68,31 @@ public class MD5Util {
return resultString.toUpperCase();
}
+ /**
+ * 使用UTF-8编码对输入字符串进行MD5加密的公开静态方法,方便外部调用进行MD5加密操作,并且在这个方法中可以进行加盐等额外的安全处理操作(虽然当前代码中没有实际添加加盐逻辑,但预留了这样的扩展点)。
+ * 它直接调用了MD5Encode私有方法,并传入输入字符串(origin)和"utf-8"字符编码名称,将返回的MD5加密后的十六进制字符串结果(按照UTF-8编码进行加密)返回给调用者,用于获取特定编码下的MD5加密结果,常用于符合UTF-8编码规范的字符串加密应用场景,比如大多数Web应用中对文本信息的加密处理。
+ *
+ * @param origin 要进行MD5加密的原始字符串,其编码通常被认为是UTF-8编码(调用者需要确保传入的字符串符合UTF-8编码规范,否则可能出现编码转换错误等问题),例如用户登录密码等需要加密的信息字符串。
+ * @return 返回一个大写的十六进制字符串,表示原始字符串按照UTF-8编码经过MD5加密后的结果,如果加密过程出现异常则返回null。
+ */
public static String MD5EncodeUtf8(String origin) {
//这里可以加盐
return MD5Encode(origin, "utf-8");
}
+ /**
+ * 主方法,用于简单的测试功能,这里主要演示了对字符串"123456"进行MD5加密并输出加密后的结果(按照UTF-8编码进行MD5加密,调用MD5EncodeUtf8方法)的过程。
+ * 在实际应用中,这种测试代码通常可以移到单元测试类中,使得工具类的功能更加纯粹,专注于提供MD5加密相关的工具方法。
+ *
+ * @param args 命令行参数,在这里没有实际使用到,一般用于接收外部传入的一些配置参数等信息(在命令行运行时传入),但本示例中没有相关逻辑涉及。
+ */
public static void main(String[] args) {
System.out.println(MD5EncodeUtf8("123456"));
}
-
+ /**
+ * 预定义的十六进制数字字符数组,用于在将字节转换为十六进制字符串时,根据字节对应的十六进制值索引从这个数组中获取相应的十六进制字符,例如字节值为10对应的十六进制数字是"a",索引为10,就从这个数组中获取对应的字符进行拼接表示。
+ */
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-order-service/src/main/java/com/njupt/swg/common/utils/PropertiesUtil.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/PropertiesUtil.java
index 79f8335..7d1c527 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/PropertiesUtil.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/PropertiesUtil.java
@@ -2,7 +2,6 @@ package com.njupt.swg.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
@@ -12,35 +11,61 @@ import java.util.Properties;
* @Date 2018/1/10 14:56
* @DESC
* @CONTACT 317758022@qq.com
+ * 这个类(PropertiesUtil)是一个工具类,主要用于读取配置文件(properties文件)中的属性值。
+ * 它在类加载时会尝试加载指定的配置文件(默认为"parameter.properties"),并将文件中的键值对存储在Properties对象中,之后提供了相应的方法来获取配置文件中指定键对应的属性值,方便在项目的其他地方统一获取配置信息,增强了配置管理的便利性和代码的可维护性,同时使用Slf4j进行日志记录,便于在配置文件读取出现异常时记录相关情况。
*/
@Slf4j
public class PropertiesUtil {
+
+ // 定义一个静态的Properties对象,用于存储从配置文件中读取的键值对信息,Properties类是Java中用于处理属性文件(.properties)的常用类,它可以方便地获取、设置各种配置属性的值。
private static Properties props;
+ // 静态代码块,在类被加载到内存时执行,主要用于初始化配置文件的读取操作,加载配置文件中的属性信息到props对象中。
static {
+ // 指定要读取的配置文件的名称,这里固定设置为"parameter.properties",意味着程序会默认从这个文件名对应的配置文件中读取属性信息,如果需要读取其他配置文件,可能需要对这里进行修改或者添加相应的参数传递机制来指定不同的文件名。
String fileName = "parameter.properties";
props = new Properties();
try {
- props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8"));
+ // 通过类加载器(PropertiesUtil.class.getClassLoader())获取指定配置文件的输入流(getResourceAsStream方法),并将其包装为InputStreamReader,同时指定字符编码为"UTF-8",以确保正确读取配置文件内容(特别是包含中文等多字节字符的情况)。
+ // 然后使用Properties对象的load方法将配置文件中的键值对信息加载到props对象中,这样后续就可以通过键来获取相应的值了。
+ props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName), "UTF-8"));
} catch (IOException e) {
- log.error("配置文件读取异常",e);
+ // 如果在读取配置文件过程中出现I/O相关的异常(比如文件不存在、权限不足、编码错误等情况),使用日志记录异常信息,方便后续排查问题,确定是哪个环节导致配置文件读取失败,影响程序获取配置属性的功能。
+ log.error("配置文件读取异常", e);
}
}
- public static String getProperty(String key){
+ /**
+ * 获取配置文件中指定键对应的属性值的方法,如果属性值为空(空白字符串、null等情况)则返回null。
+ * 它首先会从之前加载好的Properties对象(props)中获取指定键(key)对应的属性值,获取前会对键进行去除首尾空白字符的处理(通过trim方法),然后判断获取到的属性值是否为空(通过StringUtils.isBlank方法,该方法能更好地处理空白字符串、null等情况),
+ * 如果为空则返回null,表示配置文件中该键没有对应的有效属性值;如果不为空,则对获取到的属性值再进行去除首尾空白字符的处理后返回,确保返回的属性值格式规范,去除了可能多余的空白字符。
+ *
+ * @param key 要获取属性值的键,在配置文件中以"键=值"的形式定义,这里传入的键需要与配置文件中定义的键一致(不区分大小写,Properties对象内部处理时通常不区分大小写),例如配置文件中有"server.port=8080",则可以传入"server.port"来获取对应的值。
+ * @return 返回配置文件中指定键对应的属性值,如果该键对应的属性值为空(空白字符串、null等情况)则返回null,否则返回去除首尾空白字符后的属性值。
+ */
+ public static String getProperty(String key) {
String value = props.getProperty(key.trim());
- if(StringUtils.isBlank(value)){
+ if (StringUtils.isBlank(value)) {
return null;
}
return value.trim();
}
- public static String getProperty(String key,String defaultValue){
+ /**
+ * 获取配置文件中指定键对应的属性值的方法,如果属性值为空(空白字符串、null等情况)则返回默认值(defaultValue)。
+ * 同样先从已加载的Properties对象(props)中获取指定键(key)对应的属性值,对键进行去除首尾空白字符处理后获取值,然后判断获取到的属性值是否为空(通过StringUtils.isBlank方法),
+ * 如果为空,则将传入的默认值(defaultValue)赋给value变量,最后对value进行去除首尾空白字符的处理后返回,这样在配置文件中没有对应键的有效属性值时,可以返回一个预设的默认值,避免出现空值导致后续业务逻辑出现问题。
+ *
+ * @param key 要获取属性值的键,与前面的getProperty方法中对键的要求一致,需要与配置文件中定义的键匹配,用于查找对应的属性值。
+ * @param defaultValue 当配置文件中指定键对应的属性值为空(空白字符串、null等情况)时要返回的默认值,由调用者根据业务需求传入合适的默认值,例如对于端口号配置,如果配置文件中没有定义,可以传入一个默认的端口号(如8080等)作为默认值返回。
+ * @return 返回配置文件中指定键对应的属性值,如果该键对应的属性值为空(空白字符串、null等情况)则返回传入的默认值,否则返回去除首尾空白字符后的属性值。
+ */
+ public static String getProperty(String key, String defaultValue) {
String value = props.getProperty(key.trim());
- if(StringUtils.isBlank(value)){
+ if (StringUtils.isBlank(value)) {
value = defaultValue;
}
return value.trim();
}
-}
+}
\ No newline at end of file
diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/RedisUtils.java b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/RedisUtils.java
index 865904d..638ff74 100644
--- a/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/RedisUtils.java
+++ b/snailmall-order-service/src/main/java/com/njupt/swg/common/utils/RedisUtils.java
@@ -15,22 +15,34 @@ import java.util.Collections;
* @Date 2019/1/7 12:23
* @CONTACT 317758022@qq.com
* @DESC
+ * 这个类(RedisUtils)是一个工具类,主要用于与Redis数据库进行交互,实现了扣减库存相关的业务逻辑功能,借助Spring的依赖注入机制以及Redis相关的操作类来完成具体操作。
+ * 它在Spring框架中被标记为一个组件(通过@Component注解),方便被Spring容器进行管理和自动注入相关依赖,常用于在项目中统一处理和Redis交互中涉及库存操作等业务场景。
*/
@Component
public class RedisUtils {
+
+ // 通过Spring的依赖注入机制,自动注入一个RedisTemplate对象,该对象是Spring Data Redis提供的用于操作Redis的核心模板类,
+ // 这里限定了操作的键(Key)和值(Value)的类型都是字符串类型(String),通过它可以方便地执行各种Redis操作,如读写数据等。
@Autowired
- private RedisTemplate redisTemplate;
+ private RedisTemplate redisTemplate;
+
+ // 通过Spring的依赖注入机制,自动注入一个JedisPoolWrapper对象(这里未展示其具体代码,但推测是对Jedis连接池进行封装的类),
+ // 用于获取Jedis连接对象等相关操作,可能在与Redis底层交互时发挥作用,比如获取原生的Jedis连接来执行一些特定的Redis命令等情况。
@Autowired
private JedisPoolWrapper jedisPoolWrapper;
-
/**
* 先查询库存,够的话再减库存
* -2:库存不存在
* -1:库存不足
* >=0:返回扣减后的库存数量,肯定大于等于0的
+ * 定义了一个静态的字符串常量(STOCK_REDUCE_LUA),它是一段用Lua脚本语言编写的代码,用于在Redis中实现扣减库存的逻辑。
+ * 这段脚本首先获取要操作的库存键(通过KEYS[1]获取传入的第一个键参数),然后将传入的扣减数量参数(从ARGV[1]获取并转换为数字类型)转换为数字,接着检查库存键对应的库存是否存在(通过调用Redis的EXISTS命令)。
+ * 如果库存存在(EXISTS命令返回1),则执行DECRBY命令对库存进行扣减操作,并获取扣减后的库存数量,之后判断扣减后的库存数量是否小于0,如果小于0则表示库存不足,需要通过INCRBY命令将扣减的数量加回去恢复库存,并返回 -1表示库存不足;
+ * 如果扣减后的库存数量不小于0,则直接返回扣减后的库存数量。如果库存不存在(EXISTS命令返回0),则直接返回 -2表示库存不存在。
+ * 这样通过在Redis中执行这段Lua脚本,可以保证扣减库存操作的原子性,避免并发情况下出现数据不一致等问题。
*/
- public static final String STOCK_REDUCE_LUA=
+ public static final String STOCK_REDUCE_LUA =
"local stock = KEYS[1] " +
"local stock_change = tonumber(ARGV[1]) " +
"local is_exists = redis.call(\"EXISTS\", stock) " +
@@ -47,16 +59,21 @@ public class RedisUtils {
"end";
/**
+ * 扣减库存
+ * 这个方法(reduceStock)用于实际执行扣减库存的操作,它通过RedisTemplate对象执行一个Redis回调(RedisCallback)来实现与Redis的交互,在回调中获取原生的Jedis连接对象,然后使用该对象执行定义好的Lua脚本(STOCK_REDUCE_LUA)来进行库存扣减操作。
+ * 传入的参数包括库存键(stockKey)和要扣减的库存数量(stockChange),将库存键包装成不可变的列表作为Lua脚本的键参数列表(通过Collections.unmodifiableList和Arrays.asList方法),将扣减数量包装成不可变的列表作为Lua脚本的参数列表,
+ * 执行完Lua脚本后,会返回相应的结果(按照之前定义的脚本逻辑,返回 -2表示库存不存在, -1表示库存不足,大于等于0表示扣减后的库存数量),最后将这个结果返回给调用者,完成库存扣减并返回相应状态的操作。
*
- * @Description 扣减库存
+ * @param stockKey 库存对应的键,在Redis中用于唯一标识某个商品或资源的库存数据,通过这个键可以找到对应的库存数量值进行操作,例如可以是商品的ID等作为库存键。
+ * @param stockChange 要扣减的库存数量,是一个字符串类型的参数,不过在Lua脚本中会将其转换为数字类型进行实际的扣减操作,传入的数量应该是符合业务逻辑的合理数值(通常是正整数等表示要减少的数量)。
+ * @return 返回一个Object类型的结果,按照Lua脚本的逻辑,返回值可能是 -2(库存不存在)、 -1(库存不足)或者大于等于0(扣减后的库存数量),具体的返回值类型取决于实际执行情况,调用者需要根据业务需求进行相应的类型判断和处理。
*/
- public Object reduceStock(String stockKey,String stockChange){
- Object result = redisTemplate.execute((RedisCallback