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) redisConnection -> { - Jedis jedis = (Jedis)redisConnection.getNativeConnection(); - return jedis.eval(STOCK_REDUCE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey)) - ,Collections.unmodifiableList(Arrays.asList(stockChange))); + public Object reduceStock(String stockKey, String stockChange) { + Object result = redisTemplate.execute((RedisCallback) redisConnection -> { + Jedis jedis = (Jedis) redisConnection.getNativeConnection(); + return jedis.eval(STOCK_REDUCE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey)), + Collections.unmodifiableList(Arrays.asList(stockChange))); }); return result; } - -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/controller/BaseController.java b/snailmall-order-service/src/main/java/com/njupt/swg/controller/BaseController.java index 921f86c..3158c55 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/controller/BaseController.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/controller/BaseController.java @@ -17,23 +17,39 @@ import javax.servlet.http.HttpServletRequest; * @Date 2019/1/5 16:19 * @CONTACT 317758022@qq.com * @DESC + * 这个类(BaseController)作为一个基础的控制器类,很可能是项目中其他具体业务控制器类的父类,用于提供一些通用的、与获取当前用户相关的基础功能。 + * 它使用了Spring的依赖注入(通过@Autowired注解)来获取CommonCacheUtil对象,用于从缓存中获取数据,同时借助了多个工具类(如CookieUtil、JsonUtil)来完成从请求中读取登录令牌、从缓存中获取用户信息以及将用户信息字符串反序列化为User对象等操作, + * 并且使用了Slf4j进行日志记录,便于在获取当前用户过程中出现异常等情况时记录相关信息,整个类旨在方便地获取当前登录用户的信息,以支持后续不同业务控制器中对登录用户相关的业务逻辑处理。 */ @Slf4j public class BaseController { + // 通过Spring的依赖注入机制,自动注入一个CommonCacheUtil对象(这里未展示其具体代码,但推测是用于操作缓存的工具类,比如操作Redis缓存等,用于存储和获取一些常用数据), + // 后续会使用这个对象从缓存中获取与当前登录用户相关的信息(比如存储在缓存中的用户对象的序列化字符串等内容)。 @Autowired private CommonCacheUtil commonCacheUtil; - User getCurrentUser(HttpServletRequest httpServletRequest){ + /** + * 获取当前用户的方法,用于从请求中获取当前登录用户的相关信息并返回对应的User对象。 + * 它首先会尝试从HttpServletRequest对象(代表客户端发来的HTTP请求)中读取登录令牌(loginToken),这个登录令牌通常是在用户登录成功后被存储在Cookie中并随后续请求一起发送过来的,通过CookieUtil.readLoginToken方法来读取这个令牌信息。 + * 如果读取到的登录令牌为空(即StringUtils.isEmpty(loginToken)判断为true,表示没有获取到有效的登录令牌),则说明用户未登录,此时会抛出一个SnailmallException异常,提示“用户未登陆,无法获取当前用户信息”,告知调用者当前请求没有对应的登录用户。 + * 如果成功获取到了登录令牌,则接着使用注入的CommonCacheUtil对象,通过调用其getCacheValue方法,尝试从缓存中获取与该登录令牌对应的用户信息字符串(userJsonStr),这个用户信息字符串可能是之前在用户登录成功后将用户对象序列化为JSON字符串并存入缓存中的。 + * 如果从缓存中获取到的用户信息字符串为null,说明缓存中不存在对应的用户信息(可能是缓存过期、数据丢失等原因),此时会抛出一个SnailmallException异常,使用ResponseEnum.NEED_LOGIN中的状态码和描述信息(表示需要用户登录的提示),提示调用者当前请求需要用户重新登录才能获取用户信息。 + * 如果成功从缓存中获取到了用户信息字符串,那么使用JsonUtil.Str2Obj方法,将这个用户信息字符串反序列化为User对象(根据传入的User.class指定反序列化的目标类型),最后返回这个反序列化后的User对象,代表当前登录用户,方便后续在具体业务控制器中使用这个用户对象进行各种与登录用户相关的业务操作。 + * + * @param httpServletRequest 代表客户端发送过来的HTTP请求对象,从中可以获取请求相关的各种信息,如请求头中的Cookie信息(用于读取登录令牌)等,是整个获取当前用户信息操作的数据源。 + * @return 返回一个User对象,表示当前登录的用户,如果在获取过程中出现用户未登录或者缓存中用户信息不存在等异常情况,则会抛出相应的异常,不会正常返回User对象。 + */ + User getCurrentUser(HttpServletRequest httpServletRequest) { String loginToken = CookieUtil.readLoginToken(httpServletRequest); - if(StringUtils.isEmpty(loginToken)){ + if (StringUtils.isEmpty(loginToken)) { throw new SnailmallException("用户未登陆,无法获取当前用户信息"); } String userJsonStr = commonCacheUtil.getCacheValue(loginToken); - if(userJsonStr == null){ - throw new SnailmallException(ResponseEnum.NEED_LOGIN.getCode(),ResponseEnum.NEED_LOGIN.getDesc()); + if (userJsonStr == null) { + throw new SnailmallException(ResponseEnum.NEED_LOGIN.getCode(), ResponseEnum.NEED_LOGIN.getDesc()); } - User user = JsonUtil.Str2Obj(userJsonStr,User.class); + User user = JsonUtil.Str2Obj(userJsonStr, User.class); return user; } -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderController.java b/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderController.java index a59aebb..39b7467 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderController.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderController.java @@ -182,4 +182,4 @@ public class OrderController extends BaseController{ return Constants.AlipayCallback.RESPONSE_FAILED; } -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderManageController.java b/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderManageController.java index 18166b8..61dbfc8 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderManageController.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/controller/OrderManageController.java @@ -14,35 +14,76 @@ import org.springframework.web.bind.annotation.RestController; * @Date 2019/1/5 21:44 * @CONTACT 317758022@qq.com * @DESC 后台订单服务 + * 这个类(OrderManageController)是一个Spring RESTful风格的控制器类,主要用于处理后台订单相关的各种业务操作请求。 + * 它被标记为@RestController,表示这个类中的所有方法返回的数据都会直接以JSON等格式响应给客户端(通常是浏览器或者其他调用API的客户端应用),不需要额外的视图解析等操作。 + * 通过@RequestMapping注解指定了类级别的请求路径前缀("/manage/order"),表明这个控制器处理的是与后台订单管理相关且路径以该前缀开头的请求,并且定义了多个方法来分别处理不同的订单相关业务操作,如获取订单列表、订单详情、订单搜索以及发货等操作, + * 每个方法通过调用注入的IOrderService接口(具体实现应该由Spring进行依赖注入并管理,这里未展示其实现类代码)来完成实际的业务逻辑处理,并将处理结果以ServerResponse类型封装后返回给客户端,遵循了项目中统一的响应格式规范,方便客户端进行结果解析和后续处理。 */ @RestController @RequestMapping("/manage/order") public class OrderManageController { + // 通过Spring的依赖注入机制,自动注入一个IOrderService接口的实现类对象,IOrderService应该定义了一系列与订单业务相关的方法(比如获取订单列表、查询订单详情等操作的抽象方法), + // 具体的业务逻辑实现在其对应的实现类中(由Spring根据配置去查找并注入合适的实现类实例),这个控制器类中的各个方法会调用IOrderService的相应方法来完成具体的订单业务处理。 @Autowired private IOrderService orderService; + /** + * 获取订单列表的方法,处理路径为"/manage/order/list.do"的GET请求(默认情况下@RequestMapping处理GET请求,如果需要处理其他请求方式可以通过method属性指定)。 + * 该方法接收两个请求参数,分别是pageNum(表示页码,通过@RequestParam注解指定参数名和默认值,默认值为"1",表示第一页)和pageSize(表示每页显示的记录数,默认值为"10",即每页显示10条记录),类型都为整数。 + * 它直接调用注入的orderService的manageList方法(这个方法在IOrderService接口中定义,在其实现类中实现具体的获取订单列表逻辑,比如从数据库中查询符合条件的订单信息,并进行分页处理等操作), + * 然后将orderService.manageList方法返回的结果(应该是一个包含分页信息的ServerResponse对象,PageInfo中包含了具体的订单列表数据以及分页相关的属性,如总页数、总记录数等)直接返回给客户端,客户端可以根据返回的JSON格式数据解析出订单列表以及分页情况等信息,用于展示或者其他后续处理。 + * + * @param pageNum 页码,用于指定要获取哪一页的订单列表信息,客户端可以根据需求传入不同的页码值来获取不同页面的订单数据,默认值为1,表示获取第一页的订单列表。 + * @param pageSize 每页显示的记录数,用于控制每页展示多少条订单信息,方便进行分页展示,默认值为10,表示每页显示10条订单记录,客户端可以根据实际情况传入合适的值来调整每页显示的记录数量。 + * @return 返回一个ServerResponse对象,ServerResponse是项目中统一的响应封装类,里面包含了状态码、提示消息以及具体的业务数据(这里是PageInfo类型的数据,包含了订单列表以及分页相关信息),用于将订单列表及分页情况等结果以规范的格式返回给客户端。 + */ @RequestMapping("list.do") - public ServerResponse orderList(@RequestParam(value = "pageNum",defaultValue = "1") int pageNum, - @RequestParam(value = "pageSize",defaultValue = "10") int pageSize){ - return orderService.manageList(pageNum,pageSize); + public ServerResponse orderList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum, + @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) { + return orderService.manageList(pageNum, pageSize); } + /** + * 获取订单详情的方法,处理路径为"/manage/order/detail.do"的GET请求。 + * 该方法接收一个请求参数orderNo(表示订单编号,类型为Long,用于唯一标识一个订单),它直接调用orderService的manageDetail方法(这个方法在IOrderService接口中定义,在其实现类中实现具体的根据订单编号查询订单详细信息的逻辑,比如从数据库中查询对应订单编号的订单详情记录等操作), + * 然后将orderService.manageDetail方法返回的结果(应该是一个包含订单详细信息的ServerResponse对象,OrderVo是用于封装订单详细信息的视图对象,包含了订单各个字段的详细内容,如商品信息、用户信息、金额等)直接返回给客户端,客户端可以根据返回的JSON格式数据解析出订单的详细信息,用于展示或者其他后续处理。 + * + * @param orderNo 订单编号,是一个Long类型的参数,用于唯一确定要查询详情的订单,客户端需要传入正确的订单编号,以便服务器能够准确地查找并返回对应的订单详细信息。 + * @return 返回一个ServerResponse对象,ServerResponse用于封装响应的状态码、提示消息以及具体的业务数据(这里是OrderVo类型的数据,包含了订单详细信息),将查询到的订单详细信息以规范的格式返回给客户端。 + */ @RequestMapping("detail.do") - public ServerResponse orderDetail(Long orderNo){ + public ServerResponse orderDetail(Long orderNo) { return orderService.manageDetail(orderNo); } - + /** + * 订单搜索的方法,处理路径为"/manage/order/search.do"的GET请求。 + * 该方法接收三个请求参数,分别是orderNo(表示订单编号,用于按照订单编号进行搜索查询,类型为Long)、pageNum(页码,默认值为"1",用于指定搜索结果的分页页码,类型为整数)和pageSize(每页显示的记录数,默认值为"10",用于控制搜索结果的分页展示数量,类型为整数)。 + * 它调用orderService的manageSearch方法(这个方法在IOrderService接口中定义,在其实现类中实现具体的根据订单编号等条件进行订单搜索以及分页处理的逻辑,比如从数据库中查询符合订单编号条件的订单信息,并进行分页整理等操作), + * 然后将orderService.manageSearch方法返回的结果(应该是一个包含搜索结果分页信息的ServerResponse对象,PageInfo中包含了符合搜索条件的订单列表数据以及分页相关的属性,如总页数、总记录数等)直接返回给客户端,客户端可以根据返回的JSON格式数据解析出搜索到的订单列表以及分页情况等信息,用于展示或者其他后续处理。 + * + * @param orderNo 订单编号,用于指定要搜索的订单的唯一标识,客户端传入要查找的订单编号,以便服务器按照这个编号进行相关的订单搜索操作,类型为Long。 + * @param pageNum 页码,用于指定要获取搜索结果中哪一页的订单列表信息,默认值为1,表示获取搜索结果的第一页订单列表,客户端可以根据需求传入不同的页码值来获取不同页面的搜索结果订单数据,类型为整数。 + * @param pageSize 每页显示的记录数,用于控制每页展示多少条搜索到的订单信息,方便进行分页展示,默认值为10,表示每页显示10条搜索结果的订单记录,客户端可以根据实际情况传入合适的值来调整每页显示的记录数量,类型为整数。 + * @return 返回一个ServerResponse对象,ServerResponse中包含了状态码、提示消息以及具体的业务数据(这里是PageInfo类型的数据,包含了符合搜索条件的订单列表以及分页相关信息),将订单搜索结果及分页情况等以规范的格式返回给客户端。 + */ @RequestMapping("search.do") - public ServerResponse orderSearch(Long orderNo,@RequestParam(value = "pageNum",defaultValue = "1") int pageNum, - @RequestParam(value = "pageSize",defaultValue = "10")int pageSize){ - return orderService.manageSearch(orderNo,pageNum,pageSize); + public ServerResponse orderSearch(Long orderNo, @RequestParam(value = "pageNum", defaultValue = "1") int pageNum, + @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) { + return orderService.manageSearch(orderNo, pageNum, pageSize); } - + /** + * 发货操作的方法,处理路径为"/manage/order/send_goods.do"的GET请求。 + * 该方法接收一个请求参数orderNo(表示订单编号,类型为Long,用于唯一标识要进行发货操作的订单),它调用orderService的manageSendGoods方法(这个方法在IOrderService接口中定义,在其实现类中实现具体的根据订单编号进行发货相关业务逻辑处理的操作,比如更新订单状态为已发货、记录发货时间等操作), + * 然后将orderService.manageSendGoods方法返回的结果(应该是一个包含操作提示消息等信息的ServerResponse对象,例如操作成功的提示信息等内容,返回的字符串可以告知客户端发货操作是否成功以及相关的说明信息)直接返回给客户端,客户端可以根据返回的JSON格式数据解析出发货操作的结果信息,用于展示或者其他后续处理。 + * + * @param orderNo 订单编号,用于确定要进行发货操作的具体订单,客户端需要传入正确的订单编号,以便服务器准确地对对应的订单执行发货相关的业务逻辑,类型为Long。 + * @return 返回一个ServerResponse对象,ServerResponse用于封装响应的状态码、提示消息以及具体的业务数据(这里是String类型的数据,通常包含发货操作的结果提示等信息),将发货操作的结果以规范的格式返回给客户端。 + */ @RequestMapping("send_goods.do") - public ServerResponse orderSendGoods(Long orderNo){ + public ServerResponse orderSendGoods(Long orderNo) { return orderService.manageSendGoods(orderNo); } -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderItemMapper.java b/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderItemMapper.java index 34b99ed..10864d4 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderItemMapper.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderItemMapper.java @@ -3,26 +3,108 @@ package com.njupt.swg.dao; import com.njupt.swg.entity.OrderItem; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; - import java.util.List; +/** + * @Mapper注解用于标记这个接口是MyBatis的Mapper接口,告诉MyBatis框架这个接口中的方法对应着数据库中的SQL操作, + * MyBatis会为这个接口自动生成代理实现类,通过这个代理类来与数据库进行交互,执行相应的增删改查等操作。 + * 这个接口(OrderItemMapper)定义了一系列针对OrderItem实体类与数据库交互的方法,用于操作数据库中与订单明细(OrderItem)相关的数据表, + * 比如插入、删除、查询、更新订单明细记录以及根据特定条件(如订单编号、用户ID等)获取订单明细列表等操作,每个方法在MyBatis中都关联着对应的SQL语句(这些SQL语句通常写在对应的XML映射文件或者通过注解方式直接写在接口方法上,此处未展示具体SQL配置部分)。 + */ @Mapper public interface OrderItemMapper { + + /** + * 根据主键(id)删除对应的订单明细记录的方法,对应数据库中的DELETE操作。 + * 它接收一个Integer类型的参数id,代表要删除的订单明细记录在数据库表中的主键值,调用这个方法会执行相应的SQL语句(由MyBatis根据配置生成)从数据库中删除主键为该值的订单明细记录, + * 并返回一个整数,表示受影响的行数,通常如果成功删除一条记录则返回1,否则返回0(比如不存在对应主键的记录时),通过返回值可以判断删除操作是否成功执行。 + * + * @param id 要删除的订单明细记录的主键值,类型为Integer,用于唯一标识数据库表中对应的订单明细记录,通过这个值可以准确地定位并删除相应记录。 + * @return 返回一个整数,表示执行删除操作后受影响的行数,成功删除一条记录返回1,未找到对应主键的记录等情况返回0,可用于判断删除操作的执行情况。 + */ int deleteByPrimaryKey(Integer id); + /** + * 插入一条完整的订单明细记录到数据库中的方法,对应数据库中的INSERT操作。 + * 它接收一个OrderItem类型的参数record,这个参数对象包含了要插入到数据库中的订单明细记录的各个字段值(如商品数量、单价、所属订单编号等信息,这些字段与数据库表中的列对应), + * MyBatis会根据配置将这个对象中的属性值提取出来,并执行相应的SQL语句(INSERT语句)把这些值插入到数据库的订单明细表中,插入成功则返回一个整数,表示受影响的行数,通常成功插入一条记录返回1,返回值可用于判断插入操作是否成功执行。 + * + * @param record 要插入到数据库中的订单明细记录对象,类型为OrderItem,其内部属性包含了对应数据库表中各列的值,通过这个对象传递要插入的数据内容,MyBatis会将其转换为合适的SQL插入语句执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败等情况返回相应的错误值(如由于数据库约束等原因无法插入时),可用于判断插入操作的执行情况。 + */ int insert(OrderItem record); + /** + * 插入一条订单明细记录到数据库中的方法,与insert方法类似,但它是有选择性地插入,对应数据库中的INSERT操作(通常会根据对象中非空属性来生成对应的SQL插入语句)。 + * 接收一个OrderItem类型的参数record,这个对象包含了要插入的订单明细记录信息,不过在生成SQL语句时,MyBatis只会将record对象中不为null的属性对应的列插入到数据库表中(避免插入不必要的默认值或者空值到数据库中), + * 插入成功后返回一个整数,表示受影响的行数,一般成功插入一条记录返回1,通过返回值可以判断插入操作是否成功执行,该方法提供了更灵活、更符合实际业务需求的插入方式,尤其是在某些字段可能允许为null或者有默认值的情况下。 + * + * @param record 要插入到数据库中的订单明细记录对象,类型为OrderItem,其内部属性包含了对应数据库表中各列的值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL插入语句执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败等情况返回相应的错误值(如由于数据库约束等原因无法插入时),可用于判断插入操作的执行情况。 + */ int insertSelective(OrderItem record); + /** + * 根据主键(id)查询对应的订单明细记录的方法,对应数据库中的SELECT操作。 + * 接收一个Integer类型的参数id,代表要查询的订单明细记录在数据库表中的主键值,MyBatis会执行相应的SQL语句(SELECT语句),从数据库中查找主键为该值的订单明细记录, + * 如果找到则返回一个OrderItem类型的对象,这个对象包含了从数据库中查询到的对应订单明细记录的各个字段值(如商品数量、单价、所属订单编号等信息),如果未找到则返回null,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param id 要查询的订单明细记录的主键值,类型为Integer,用于唯一标识数据库表中对应的订单明细记录,通过这个值可以准确地在数据库中定位并获取相应记录,若存在则返回对应的记录对象,不存在则返回null。 + * @return 返回一个OrderItem类型的对象,表示从数据库中查询到的对应主键值的订单明细记录,如果未找到对应主键的记录则返回null,用于后续业务逻辑中对查询结果的使用和处理。 + */ OrderItem selectByPrimaryKey(Integer id); + /** + * 根据主键(id)有选择性地更新订单明细记录的方法,对应数据库中的UPDATE操作(通常会根据对象中非空属性来生成对应的SQL更新语句)。 + * 接收一个OrderItem类型的参数record,这个对象包含了要更新的订单明细记录信息,MyBatis会根据record对象中不为null的属性来生成相应的SQL更新语句,只更新数据库表中对应列的值(避免将原本正确的值更新为null等情况), + * 执行更新操作后返回一个整数,表示受影响的行数,一般成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行,该方法提供了更灵活、更符合实际业务需求的更新方式,尤其是在某些字段可能允许为null或者有默认值的情况下。 + * + * @param record 要更新的订单明细记录对象,类型为OrderItem,其内部属性包含了对应数据库表中各列的新值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL更新语句执行更新操作。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKeySelective(OrderItem record); + /** + * 根据主键(id)更新完整的订单明细记录的方法,对应数据库中的UPDATE操作。 + * 接收一个OrderItem类型的参数record,这个对象包含了要更新的订单明细记录的各个字段值(无论是否为null,都会按照这个对象中的值去更新数据库表中对应列的值), + * MyBatis会执行相应的SQL语句(UPDATE语句),将数据库中主键为对应值的订单明细记录的所有列按照record对象中的值进行更新,更新成功后返回一个整数,表示受影响的行数,通常成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行。 + * + * @param record 要更新的订单明细记录对象,类型为OrderItem,其内部属性包含了对应数据库表中各列的新值,MyBatis会将其转换为合适的SQL更新语句执行更新操作,将数据库中对应记录的所有列更新为这些新值。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKey(OrderItem record); + /** + * 根据订单编号(orderNo)和用户ID(userId)获取对应的订单明细列表的方法,对应数据库中的SELECT操作。 + * 接收两个参数,分别是Long类型的订单编号(orderNo)和Integer类型的用户ID(userId),MyBatis会执行相应的SQL语句(SELECT语句),从数据库中查找符合这两个条件(订单编号为指定值且用户ID为指定值)的所有订单明细记录, + * 如果找到符合条件的记录,则返回一个包含这些订单明细记录的List集合,集合中的每个OrderItem对象代表一条订单明细记录,包含了相应的字段值(如商品数量、单价、所属订单编号等信息),如果未找到符合条件的记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * 这里使用了@Param注解来给方法的参数命名,确保在MyBatis编写对应的SQL语句时可以准确地引用这两个参数,保证参数传递的准确性和SQL语句的可读性。 + * + * @param orderNo 订单编号,类型为Long,用于筛选出属于该订单编号的订单明细记录,通过这个参数可以在数据库中定位到对应的一批订单明细数据,与userId参数共同确定要获取的具体订单明细记录集合。 + * @param userId 用户ID,类型为Integer,用于进一步筛选出属于该用户的订单明细记录,结合orderNo参数准确地获取符合特定订单和特定用户的订单明细记录集合,确保获取的数据符合业务需求。 + * @return 返回一个List类型的集合,表示从数据库中查询到的符合订单编号和用户ID条件的所有订单明细记录,如果未找到符合条件的记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List getByOrderNoUserId(@Param("orderNo") Long orderNo, @Param("userId") Integer userId); + /** + * 批量插入订单明细记录列表到数据库中的方法,对应数据库中的INSERT操作(批量插入多条记录)。 + * 接收一个List类型的参数orderItemList,这个列表中包含了多个要插入到数据库中的订单明细记录对象,每个OrderItem对象包含了对应订单明细记录的各个字段值(如商品数量、单价、所属订单编号等信息), + * MyBatis会根据配置将这个列表中的每个对象的属性值提取出来,并执行相应的批量插入SQL语句(通常是一条INSERT语句插入多条记录的形式),把这些订单明细记录批量插入到数据库的订单明细表中,方便一次性插入多条相关的订单明细数据,提高插入效率, + * 该方法没有返回值(void类型),不过在实际应用中可以根据数据库的执行情况以及业务需求,通过其他方式(如日志记录、异常处理等)来判断批量插入操作是否成功执行。 + * 这里使用了@Param注解来给方法的参数命名,确保在MyBatis编写对应的SQL语句时可以准确地引用这个参数,保证参数传递的准确性和SQL语句的可读性。 + * + * @param orderItemList 要批量插入到数据库中的订单明细记录对象列表,类型为List,列表中的每个元素都是一个包含了订单明细记录各字段值的对象,MyBatis会将这些对象批量转换为合适的SQL插入语句执行批量插入操作。 + */ void batchInsert(@Param("orderItemList") List orderItemList); + /** + * 根据订单编号(orderNo)获取对应的订单明细列表的方法,对应数据库中的SELECT操作。 + * 接收一个Long类型的参数orderNo,代表要查询的订单明细记录所属的订单编号,MyBatis会执行相应的SQL语句(SELECT语句),从数据库中查找属于该订单编号的所有订单明细记录, + * 如果找到符合条件的记录,则返回一个包含这些订单明细记录的List集合,集合中的每个OrderItem对象代表一条订单明细记录,包含了相应的字段值(如商品数量、单价、所属订单编号等信息),如果未找到符合条件的记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * 这里使用了@Param注解来给方法的参数命名,确保在MyBatis编写对应的SQL语句时可以准确地引用这个参数,保证参数传递的准确性和SQL语句的可读性。 + * + * @param orderNo 订单编号,类型为Long,用于筛选出属于该订单编号的订单明细记录,通过这个参数可以在数据库中定位到对应的一批订单明细数据,获取相应的订单明细记录集合,方便后续业务逻辑使用这些数据。 + * @return 返回一个List类型的集合,表示从数据库中查询到的符合订单编号条件的所有订单明细记录,如果未找到符合条件的记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List getByOrderNo(@Param("orderNo") Long orderNo); } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderMapper.java b/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderMapper.java index f656e55..f2070a1 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderMapper.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/dao/OrderMapper.java @@ -4,36 +4,151 @@ import com.njupt.swg.entity.Order; import com.njupt.swg.entity.OrderItem; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; - import java.util.List; +/** + * @Mapper注解用于标记这个接口是MyBatis的Mapper接口,意味着MyBatis框架会为其自动生成代理实现类, + * 通过这个代理类与数据库进行交互,执行对应的SQL语句来实现各种数据库操作,这个接口(OrderMapper)主要定义了针对Order实体类以及部分与订单相关(通过关联OrderItem等)的数据库操作方法, + * 用于操作数据库中与订单(Order)相关的数据表,涵盖了订单数据的增删改查以及一些基于特定条件(如用户ID、订单编号、订单状态、创建时间等)的查询操作,还有定时关单、关闭订单等特定业务逻辑对应的数据库操作方法,每个方法对应的SQL语句通常在对应的XML映射文件或者通过注解方式写在接口方法上(此处未展示具体SQL配置部分)。 + */ @Mapper public interface OrderMapper { + + /** + * 根据主键(id)删除对应的订单记录的方法,对应数据库中的DELETE操作。 + * 接收一个Integer类型的参数id,该参数代表要删除的订单记录在数据库表中的主键值,调用此方法后,MyBatis会执行相应的SQL语句(由MyBatis根据配置生成),从数据库中删除主键为该值的订单记录, + * 并返回一个整数,表示受影响的行数,正常情况下,如果成功删除一条订单记录则返回1,若不存在对应主键的订单记录时则返回0,通过返回值可判断删除操作是否成功执行。 + * + * @param id 要删除的订单记录的主键值,类型为Integer,用于唯一标识数据库表中对应的订单记录,凭借这个值能准确地定位并删除相应的订单记录。 + * @return 返回一个整数,用以表明执行删除操作后受影响的行数,成功删除一条记录返回1,未找到对应主键的订单记录时返回0,可据此判断删除操作的执行情况。 + */ int deleteByPrimaryKey(Integer id); + /** + * 插入一条完整的订单记录到数据库中的方法,对应数据库中的INSERT操作。 + * 接收一个Order类型的参数record,这个Order对象包含了要插入到数据库中的订单记录的各个字段值(例如订单编号、用户ID、订单金额、订单状态等信息,这些字段与数据库表中的列相对应), + * MyBatis会依据配置提取该对象中的属性值,并执行相应的SQL插入语句(INSERT语句),将这些值插入到数据库的订单表中,插入成功后返回一个整数,表示受影响的行数,通常成功插入一条订单记录返回1,返回值可用于判断插入操作是否成功执行。 + * + * @param record 要插入到数据库中的订单记录对象,类型为Order,其内部属性承载了对应数据库表中各列的值,MyBatis会把它转换为合适的SQL插入语句来执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败(例如因数据库约束等原因无法插入)时返回相应的错误值,以此判断插入操作的执行情况。 + */ int insert(Order record); + /** + * 插入一条订单记录到数据库中的方法,和insert方法类似,但它是有选择性地插入,对应数据库中的INSERT操作(会根据对象中非空属性来生成对应的SQL插入语句)。 + * 同样接收一个Order类型的参数record,此对象包含了要插入的订单记录信息,不过在生成SQL语句时,MyBatis只会把record对象中不为null的属性对应的列插入到数据库表中(这样可以避免插入不必要的默认值或者空值到数据库中), + * 插入成功后返回一个整数,表示受影响的行数,一般成功插入一条记录返回1,通过该返回值能够判断插入操作是否成功执行,这种方式在实际业务中更具灵活性,尤其适用于部分字段允许为null或者有默认值的情况。 + * + * @param record 要插入到数据库中的订单记录对象,类型为Order,其内部属性包含了对应数据库表中各列的值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL插入语句执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败等情况返回相应的错误值(比如因数据库约束等原因无法插入时),可用于判断插入操作的执行情况。 + */ int insertSelective(Order record); + /** + * 根据主键(id)查询对应的订单记录的方法,对应数据库中的SELECT操作。 + * 接收一个Integer类型的参数id,该参数代表要查询的订单记录在数据库表中的主键值,MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找主键为该值的订单记录, + * 如果找到,则返回一个Order类型的对象,这个对象包含了从数据库中查询到的对应订单记录的各个字段值(如订单编号、用户ID、订单金额、订单状态等信息),若未找到则返回null,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param id 要查询的订单记录的主键值,类型为Integer,用于唯一标识数据库表中对应的订单记录,通过这个值能准确地在数据库中定位并获取相应的订单记录,若存在则返回对应的记录对象,不存在则返回null。 + * @return 返回一个Order类型的对象,表示从数据库中查询到的对应主键值的订单记录,如果未找到对应主键的订单记录则返回null,供后续业务逻辑对查询结果进行使用和处理。 + */ Order selectByPrimaryKey(Integer id); + /** + * 根据主键(id)有选择性地更新订单记录的方法,对应数据库中的UPDATE操作(通常会根据对象中非空属性来生成对应的SQL更新语句)。 + * 接收一个Order类型的参数record,这个对象包含了要更新的订单记录信息,MyBatis会根据record对象中不为null的属性来生成相应的SQL更新语句,只更新数据库表中对应列的值(避免将原本正确的值更新为null等情况), + * 执行更新操作后返回一个整数,表示受影响的行数,一般成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行,该方法提供了更灵活、更符合实际业务需求的更新方式,特别是在某些字段可能允许为null或者有默认值的情况下。 + * + * @param record 要更新的订单记录对象,类型为Order,其内部属性包含了对应数据库表中各列的新值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL更新语句执行更新操作。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKeySelective(Order record); + /** + * 根据主键(id)更新完整的订单记录的方法,对应数据库中的UPDATE操作。 + * 接收一个Order类型的参数record,这个对象包含了要更新的订单记录的各个字段值(无论是否为null,都会按照这个对象中的值去更新数据库表中对应列的值), + * MyBatis会执行相应的SQL语句(UPDATE语句),将数据库中主键为对应值的订单记录的所有列按照record对象中的值进行更新,更新成功后返回一个整数,表示受影响的行数,通常成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行。 + * + * @param record 要更新的订单记录对象,类型为Order,其内部属性包含了对应数据库表中各列的新值,MyBatis会将其转换为合适的SQL更新语句执行更新操作,把数据库中对应记录的所有列更新为这些新值。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKey(Order record); + /** + * 根据用户ID(userId)和订单编号(orderNo)查询对应的订单记录的方法,对应数据库中的SELECT操作。 + * 接收两个参数,分别是Integer类型的用户ID(userId)和Long类型的订单编号(orderNo),MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找同时满足用户ID为指定值且订单编号为指定值的订单记录, + * 如果找到符合条件的记录,则返回一个Order类型的对象,这个对象包含了从数据库中查询到的对应订单记录的各个字段值(如订单编号、用户ID、订单金额、订单状态等信息),若未找到则返回null,方便后续业务逻辑根据查询结果进行相应处理。 + * 这里使用了@Param注解来给方法的参数命名,确保在MyBatis编写对应的SQL语句时可以准确地引用这两个参数,保证参数传递的准确性和SQL语句的可读性。 + * + * @param userId 用户ID,类型为Integer,用于筛选出属于该用户的订单记录,与orderNo参数共同确定要获取的具体订单记录,确保查询结果符合业务需求。 + * @param orderNo 订单编号,类型为Long,用于进一步筛选出具有该订单编号的订单记录,结合userId参数准确地定位到符合特定用户和特定订单编号的订单记录,若存在则返回对应的记录对象,不存在则返回null。 + * @return 返回一个Order类型的对象,表示从数据库中查询到的符合用户ID和订单编号条件的订单记录,如果未找到符合条件的记录则返回null,用于后续业务逻辑中对查询结果的使用和处理。 + */ Order selectByUserIdOrderNo(@Param("userId") Integer userId, @Param("orderNo") Long orderNo); + /** + * 根据订单编号(orderNo)查询对应的订单记录的方法,对应数据库中的SELECT操作。 + * 接收一个Long类型的参数orderNo,代表要查询的订单记录的订单编号,MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找订单编号为该值的订单记录, + * 如果找到符合条件的记录,则返回一个Order类型的对象,这个对象包含了从数据库中查询到的对应订单记录的各个字段值(如订单编号、用户ID、订单金额、订单状态等信息),若未找到则返回null,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param orderNo 订单编号,类型为Long,用于唯一标识要查询的订单记录,通过这个参数能在数据库中定位到对应的订单记录,若存在则返回对应的记录对象,不存在则返回null。 + * @return 返回一个Order类型的对象,表示从数据库中查询到的对应订单编号的订单记录,如果未找到符合条件的记录则返回null,供后续业务逻辑对查询结果进行使用和处理。 + */ Order selectByOrderNo(Long orderNo); + /** + * 根据用户ID(userId)查询对应的订单记录列表的方法,对应数据库中的SELECT操作。 + * 接收一个Integer类型的参数userId,代表要查询的订单记录所属的用户ID,MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找属于该用户的所有订单记录, + * 如果找到符合条件的记录,则返回一个包含这些订单记录的List集合,集合中的每个Order对象代表一条订单记录,包含了相应的字段值(如订单编号、用户ID、订单金额、订单状态等信息),若未找到符合条件的记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param userId 用户ID,类型为Integer,用于筛选出属于该用户的所有订单记录,通过这个参数能在数据库中定位到对应的一批订单记录,若存在则返回包含这些订单记录的集合,不存在则返回空列表。 + * @return 返回一个List类型的集合,表示从数据库中查询到的符合用户ID条件的所有订单记录,如果未找到符合条件的记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List selectByUserId(Integer userId); + /** + * 根据订单编号(orderNo)获取对应的订单明细(OrderItem)列表的方法,对应数据库中的SELECT操作。 + * 接收一个Long类型的参数orderNo,代表要查询的订单明细所属的订单编号,MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找属于该订单编号的所有订单明细记录, + * 如果找到符合条件的记录,则返回一个包含这些订单明细记录的List集合,集合中的每个OrderItem对象代表一条订单明细记录,包含了相应的字段值(如商品数量、单价、所属订单编号等信息),若未找到符合条件的记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param orderNo 订单编号,类型为Long,用于筛选出属于该订单编号的订单明细记录,通过这个参数能在数据库中定位到对应的一批订单明细数据,若存在则返回包含这些订单明细记录的集合,不存在则返回空列表。 + * @return 返回一个List类型的集合,表示从数据库中查询到的符合订单编号条件的所有订单明细记录,如果未找到符合条件的记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List getByOrderNo(Long orderNo); + /** + * 查询所有订单记录的方法,对应数据库中的SELECT操作。 + * MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中获取所有的订单记录, + * 如果数据库中有订单记录存在,则返回一个包含这些订单记录的List集合,集合中的每个Order对象代表一条订单记录,包含了相应的字段值(如订单编号、用户ID、订单金额、订单状态等信息),若数据库中没有订单记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @return 返回一个List类型的集合,表示从数据库中查询到的所有订单记录,如果数据库中没有订单记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List selectAllOrder(); - //定时关单 + /** + * 定时关单相关的查询方法,用于根据订单状态(status)和创建时间(date)查询符合条件的订单记录,对应数据库中的SELECT操作。 + * 接收两个参数,分别是Integer类型的订单状态(status)和String类型的创建时间(date),MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找状态为指定值且创建时间符合给定条件(通常是某个时间范围等,具体由date参数的格式和对应的SQL逻辑决定)的订单记录, + * 如果找到符合条件的记录,则返回一个包含这些订单记录的List集合,集合中的每个Order对象代表一条订单记录,包含了相应的字段值(如订单编号、用户ID、订单金额、订单状态等信息),若未找到符合条件的记录则返回一个空的列表,方便后续业务逻辑根据查询结果进行相应处理。 + * 这里使用了@Param注解来给方法的参数命名,确保在MyBatis编写对应的SQL语句时可以准确地引用这两个参数,保证参数传递的准确性和SQL语句的可读性。 + * + * @param status 订单状态,类型为Integer,用于筛选出处于该状态的订单记录,结合date参数确定要获取的符合特定状态和创建时间条件的订单记录集合。 + * @param date 订单创建时间,类型为String,通常以某种特定的日期时间格式(例如"yyyy-MM-dd HH:mm:ss"等,具体格式取决于业务和对应的SQL实现)传入,用于进一步筛选出在该时间条件下的订单记录,与status参数共同定位到符合要求的订单记录集合,若存在则返回包含这些订单记录的集合,不存在则返回空列表。 + * @return 返回一个List类型的集合,表示从数据库中查询到的符合订单状态和创建时间条件的所有订单记录,如果未找到符合条件的记录则返回一个空的列表,用于后续业务逻辑中对查询结果的使用和处理。 + */ List selectOrderStatusByCreateTime(@Param("status") Integer status, @Param("date") String date); - //关闭订单 - int closeOrderByOrderId(Integer id); + +/** + * 关闭订单的方法,对应数据库中的UPDATE操作,用于根据订单ID(id)来更新订单记录,将其状态设置为关闭等相关操作(具体的更新逻辑由对应的SQL语句决定,此处未展示具体SQL内容)。 + * 接收一个Integer类型的参数id,代表要关闭的订单记录在数据库表中的主键值,MyBatis会执行相应的SQL语句(UPDATE语句),对主键为该值的订单记录进行关闭相关的更新操作, + * 执行更新操作后返回一个整数,表示受影响的行数,一般成功更新一条记录(即成功关闭一个订单)返回1,通过返回值可以判断关闭订单操作是否成功执行,若未找到对应主键的订单记录或者由于数据库约束等原因无法更新时返回相应的错误值。 + * + * @param id 要关闭的订单记录的主键值,类型为Integer,用于唯一标识数据库表中对应的订单记录,凭借这个值能准确地定位并对相应的订单记录执行关闭操作,通过返回值可判断操作的执行情况。 + * @return 返回一个整数,表示执行关闭订单操作后受影响的行数 + */ + + + +int closeOrderByOrderId(Integer id); +//关闭订单 } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/dao/PayInfoMapper.java b/snailmall-order-service/src/main/java/com/njupt/swg/dao/PayInfoMapper.java index 598d7b4..cb6157a 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/dao/PayInfoMapper.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/dao/PayInfoMapper.java @@ -4,22 +4,71 @@ import com.njupt.swg.entity.PayInfo; import org.apache.ibatis.annotations.Mapper; /** - * @Author swg. - * @Date 2019/1/8 10:58 - * @CONTACT 317758022@qq.com - * @DESC + * @Mapper注解用于标记这个接口是MyBatis的Mapper接口,告诉MyBatis框架该接口中的方法会对应数据库中的相关操作, + * MyBatis会自动为这个接口生成代理实现类,通过这个代理类来和数据库进行交互,执行对应的增删改查等SQL语句操作。 + * 此接口(PayInfoMapper)定义了一系列针对PayInfo实体类与数据库进行交互的方法,主要用于操作数据库中与支付信息(PayInfo)相关的数据表, + * 涵盖了基本的增删改查功能,例如插入新的支付信息记录、根据主键删除、更新支付信息记录以及按照主键查询支付信息记录等操作,每个方法对应的具体SQL语句通常会在对应的XML映射文件或者通过注解的方式写在接口方法上(此处未展示具体SQL配置部分)。 */ @Mapper public interface PayInfoMapper { + + /** + * 根据主键(id)删除对应的支付信息记录的方法,对应数据库中的DELETE操作。 + * 它接收一个Integer类型的参数id,这个参数代表要删除的支付信息记录在数据库表中的主键值,调用该方法时,MyBatis会执行相应的SQL语句(由MyBatis根据配置生成),从数据库中删除主键为该值的支付信息记录, + * 并返回一个整数,表示受影响的行数,通常情况下,如果成功删除一条支付信息记录则返回1,若不存在对应主键的记录时则返回0,通过返回值可以判断删除操作是否成功执行。 + * + * @param id 要删除的支付信息记录的主键值,类型为Integer,用于唯一标识数据库表中对应的支付信息记录,依靠这个值能准确地定位并删除相应记录。 + * @return 返回一个整数,用以表明执行删除操作后受影响的行数,成功删除一条记录返回1,未找到对应主键的记录时返回0,可据此判断删除操作的执行情况。 + */ int deleteByPrimaryKey(Integer id); + /** + * 插入一条完整的支付信息记录到数据库中的方法,对应数据库中的INSERT操作。 + * 接收一个PayInfo类型的参数record,这个PayInfo对象包含了要插入到数据库中的支付信息记录的各个字段值(比如支付金额、支付方式、支付时间、关联订单编号等信息,这些字段与数据库表中的列相对应), + * MyBatis会依据配置提取该对象中的属性值,并执行相应的SQL插入语句(INSERT语句),把这些值插入到数据库的支付信息表中,插入成功后返回一个整数,表示受影响的行数,一般成功插入一条支付信息记录返回1,返回值可用于判断插入操作是否成功执行。 + * + * @param record 要插入到数据库中的支付信息记录对象,类型为PayInfo,其内部属性承载了对应数据库表中各列的值,MyBatis会把它转换为合适的SQL插入语句来执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败(例如因数据库约束等原因无法插入)时返回相应的错误值,以此判断插入操作的执行情况。 + */ int insert(PayInfo record); + /** + * 插入一条支付信息记录到数据库中的方法,和insert方法类似,但它是有选择性地插入,对应数据库中的INSERT操作(会根据对象中非空属性来生成对应的SQL插入语句)。 + * 同样接收一个PayInfo类型的参数record,此对象包含了要插入的支付信息记录信息,不过在生成SQL语句时,MyBatis只会把record对象中不为null的属性对应的列插入到数据库表中(这样可以避免插入不必要的默认值或者空值到数据库中), + * 插入成功后返回一个整数,表示受影响的行数,通常成功插入一条记录返回1,通过该返回值能够判断插入操作是否成功执行,这种方式在实际业务中更具灵活性,尤其适用于部分字段允许为null或者有默认值的情况。 + * + * @param record 要插入到数据库中的支付信息记录对象,类型为PayInfo,其内部属性包含了对应数据库表中各列的值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL插入语句执行插入操作。 + * @return 返回一个整数,表示执行插入操作后受影响的行数,成功插入一条记录返回1,插入失败等情况返回相应的错误值(比如因数据库约束等原因无法插入时),可用于判断插入操作的执行情况。 + */ int insertSelective(PayInfo record); + /** + * 根据主键(id)查询对应的支付信息记录的方法,对应数据库中的SELECT操作。 + * 接收一个Integer类型的参数id,该参数代表要查询的支付信息记录在数据库表中的主键值,MyBatis会执行相应的SQL查询语句(SELECT语句),从数据库中查找主键为该值的支付信息记录, + * 如果找到,则返回一个PayInfo类型的对象,这个对象包含了从数据库中查询到的对应支付信息记录的各个字段值(如支付金额、支付方式、支付时间、关联订单编号等信息),若未找到则返回null,方便后续业务逻辑根据查询结果进行相应处理。 + * + * @param id 要查询的支付信息记录的主键值,类型为Integer,用于唯一标识数据库表中对应的支付信息记录,通过这个值能准确地在数据库中定位并获取相应的支付信息记录,若存在则返回对应的记录对象,不存在则返回null。 + * @return 返回一个PayInfo类型的对象,表示从数据库中查询到的对应主键值的支付信息记录,如果未找到对应主键的支付信息记录则返回null,供后续业务逻辑对查询结果进行使用和处理。 + */ PayInfo selectByPrimaryKey(Integer id); + /** + * 根据主键(id)有选择性地更新支付信息记录的方法,对应数据库中的UPDATE操作(通常会根据对象中非空属性来生成对应的SQL更新语句)。 + * 接收一个PayInfo类型的参数record,这个对象包含了要更新的支付信息记录信息,MyBatis会根据record对象中不为null的属性来生成相应的SQL更新语句,只更新数据库表中对应列的值(避免将原本正确的值更新为null等情况), + * 执行更新操作后返回一个整数,表示受影响的行数,一般成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行,该方法提供了更灵活、更符合实际业务需求的更新方式,特别是在某些字段可能允许为null或者有默认值的情况下。 + * + * @param record 要更新的支付信息记录对象,类型为PayInfo,其内部属性包含了对应数据库表中各列的新值,MyBatis会根据属性是否为null来选择性地将其转换为合适的SQL更新语句执行更新操作。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKeySelective(PayInfo record); + /** + * 根据主键(id)更新完整的支付信息记录的方法,对应数据库中的UPDATE操作。 + * 接收一个PayInfo类型的参数record,这个对象包含了要更新的支付信息记录的各个字段值(无论是否为null,都会按照这个对象中的值去更新数据库表中对应列的值), + * MyBatis会执行相应的SQL语句(UPDATE语句),将数据库中主键为对应值的支付信息记录的所有列按照record对象中的值进行更新,更新成功后返回一个整数,表示受影响的行数,通常成功更新一条记录返回1,通过返回值可以判断更新操作是否成功执行。 + * + * @param record 要更新的支付信息记录对象,类型为PayInfo,其内部属性包含了对应数据库表中各列的新值,MyBatis会将其转换为合适的SQL更新语句执行更新操作,把数据库中对应记录的所有列更新为这些新值。 + * @return 返回一个整数,表示执行更新操作后受影响的行数,成功更新一条记录返回1,未找到对应主键的记录或者由于数据库约束等原因无法更新时返回相应的错误值,可用于判断更新操作的执行情况。 + */ int updateByPrimaryKey(PayInfo record); -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Cart.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Cart.java index 7a323d4..bc444f5 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Cart.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Cart.java @@ -4,26 +4,55 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.util.Date; +/** + * 这个类(Cart)代表购物车实体类,用于在Java程序中映射数据库中与购物车相关的数据表结构或者在业务逻辑中传递购物车相关的信息。 + * 它使用了Lombok库提供的注解来简化代码编写,通过 @Data 注解自动生成了常用的Getter、Setter、toString、equals、hashCode等方法,方便对类中的属性进行操作和展示; + * @AllArgsConstructor 注解会自动生成一个包含所有参数的构造函数,方便在创建对象时一次性传入所有属性值进行初始化; + * @NoArgsConstructor 注解则生成一个无参构造函数,在某些框架(如JSON序列化、反序列化等场景)或者自定义初始化逻辑需要无参构造函数时会用到。 + */ @Data @AllArgsConstructor @NoArgsConstructor public class Cart { + + /** + * 购物车记录的唯一标识符,通常对应数据库表中的主键字段,用于唯一标识一条购物车记录,类型为Integer,方便在数据库操作以及业务逻辑处理中对不同的购物车记录进行区分和查找。 + */ private Integer id; + /** + * 关联的用户ID,用于表明这个购物车记录属于哪个用户,通过这个字段可以将购物车与具体的用户进行关联,在多用户的应用场景中,不同用户拥有各自独立的购物车数据,类型为Integer,与数据库中存储用户ID的字段类型相对应。 + */ private Integer userId; + /** + * 商品ID,代表购物车中存放的商品的唯一标识,通过这个字段可以关联到具体的商品信息(比如商品名称、价格、描述等),确定购物车中是哪个商品,类型为Integer,对应数据库中存储商品ID的字段类型,方便进行商品相关的查询和业务操作。 + */ private Integer productId; + /** + * 商品数量,用于记录购物车中对应商品的购买数量,用户可以根据自己的需求在购物车中增加或减少商品数量,类型为Integer,例如用户将某商品加入购物车后,可以通过修改这个数量来调整购买的件数,在数据库中也会相应地存储和更新该数量值。 + */ private Integer quantity; + /** + * 用于标识商品在购物车中是否被选中的状态,可能取值(通常约定为0表示未选中,1表示选中等情况),类型为Integer,在进行批量操作(如批量结算、批量删除选中商品等)时会根据这个字段的值来确定具体操作哪些商品,方便购物车相关业务逻辑中对商品选中状态的判断和处理。 + */ private Integer checked; + /** + * 购物车记录的创建时间,使用了 @JsonFormat 注解来指定日期时间的格式化格式,在将对象转换为JSON字符串(比如在进行Web API响应返回、数据传输等场景)时,会按照指定的格式("yyyy-MM-dd HH:mm:ss.SSS",精确到毫秒的日期时间格式)进行格式化输出, + * 方便前端或者其他接收方能够以统一、易读的格式解析和展示创建时间信息,同时在数据库中存储和读取这个时间字段时也遵循相应的时间数据格式要求,类型为Date,用于准确记录购物车记录创建的具体时间点。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date createTime; + /** + * 购物车记录的更新时间,同样使用了 @JsonFormat 注解来指定日期时间的格式化格式,作用和createTime字段类似,在对象序列化、反序列化以及与数据库交互时按照"yyyy-MM-dd HH:mm:ss.SSS"格式处理, + * 用于记录购物车记录最后一次被更新(比如修改商品数量、选中状态等操作时)的具体时间点,方便跟踪购物车数据的变化情况,类型为Date。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date updateTime; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Order.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Order.java index b7ff0bc..bf0c609 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Order.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Order.java @@ -4,40 +4,89 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.math.BigDecimal; import java.util.Date; +/** + * 这个类(Order)代表订单实体类,用于在Java应用程序中映射数据库里与订单相关的数据表结构,同时也用于在业务逻辑处理过程中传递订单相关的信息。 + * 它借助了Lombok库提供的注解来简化代码编写,通过 @Data 注解自动生成了像Getter、Setter、toString、equals、hashCode等常用方法,使得对类中各个属性的获取、设置以及对象的展示、比较等操作更加便捷; + * @AllArgsConstructor 注解会自动生成一个包含所有参数的构造函数,便于在创建订单对象时能一次性传入所有属性值进行初始化; + * @NoArgsConstructor 注解则生成一个无参构造函数,在一些特定场景下(比如JSON序列化、反序列化操作,或者某些框架初始化对象时需要调用无参构造函数等情况)会发挥作用,确保对象能正常被创建和使用。 + */ @Data @AllArgsConstructor @NoArgsConstructor public class Order { + + /** + * 订单记录在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的订单记录,类型为Integer,方便在数据库操作(如查询、更新、删除等)以及整个业务逻辑处理中准确地定位和处理具体的某条订单数据。 + */ private Integer id; + /** + * 订单编号,通常是一个具有唯一性的长整型数值,用于在业务系统中唯一标识一个订单,在与外部系统交互、订单查询展示等场景中常作为关键的标识信息,类型为Long,比如在用户查询自己的某个订单、商家处理对应订单等操作时,都会通过这个订单编号来定位到具体的订单,方便业务流转和数据处理。 + */ private Long orderNo; + /** + * 关联的用户ID,表明这个订单是由哪个用户创建的,通过这个字段可以将订单与具体的用户进行关联,从而实现基于用户维度的订单查询、统计等业务操作,类型为Integer,对应数据库中存储用户ID的字段类型,确保了数据的一致性和关联性。 + */ private Integer userId; + /** + * 物流ID,可能是关联到具体的物流配送信息(例如对应某个快递公司、物流单号等相关信息)的标识符,用于确定该订单采用的是哪种物流配送方式以及后续跟踪物流状态等操作,类型为Integer,方便与物流相关的业务模块进行对接和数据交互,确保订单的商品能准确无误地配送到用户手中。 + */ private Integer shippingId; + /** + * 订单的支付金额,使用BigDecimal类型来精确表示金额数值,能够处理包含小数部分的金额,并且可以避免浮点数运算带来的精度问题,用于记录用户为该订单实际支付的费用,比如购买商品的总价、加上运费(如果运费单独计算且包含在支付金额内)等的总和,在订单结算、财务统计等业务场景中起着关键作用。 + */ private BigDecimal payment; + /** + * 支付类型的标识符,用于表示用户采用的是哪种支付方式来完成该订单的支付,例如可能取值为1表示微信支付、2表示支付宝支付、3表示银行卡支付等(具体的取值和对应的支付方式根据业务定义来确定),类型为Integer,方便在业务逻辑中对不同支付方式的订单进行分类、统计以及相关的处理操作。 + */ private Integer paymentType; + /** + * 运费金额,同样使用Integer类型来表示运费的数值(也可以根据业务需求使用BigDecimal等更精确的类型,如果运费需要精确到小数部分),用于记录该订单产生的物流运输费用,在订单费用明细、成本核算等业务操作中会涉及到对这个运费金额的处理和统计。 + */ private Integer postage; + /** + * 订单状态的标识符,代表该订单当前所处的状态,例如可能取值为0表示已下单未支付、1表示已支付待发货、2表示已发货、3表示已签收、4表示已取消等(具体的状态值和对应的含义由业务规则来设定),类型为Integer,在整个订单处理流程中,通过判断这个状态字段来决定后续可以进行哪些业务操作,比如只有已支付的订单才能发货等,是订单业务逻辑控制的关键因素之一。 + */ private Integer status; + /** + * 支付时间,记录订单完成支付操作的具体时间点,使用Date类型来存储日期和时间信息,并且通过 @JsonFormat 注解指定了格式化的格式为"yyyy-MM-dd HH:mm:ss.SSS"(精确到毫秒的日期时间格式),这样在将订单对象转换为JSON字符串(比如在Web API响应返回订单数据、前后端数据传输等场景)时,能以统一、规范且易读的格式展示支付时间信息,方便前端或者其他接收方进行解析和展示,同时也确保了在数据库存储和读取该时间字段时遵循相应的格式要求。 + */ private Date paymentTime; + /** + * 发货时间,用于记录订单对应的商品开始进行物流配送的具体时间,类型为Date,同样应用了 @JsonFormat 注解来规范其在JSON序列化等场景下的格式,在业务流程中,通过这个时间可以查看订单的发货及时性、统计发货效率等,是跟踪订单物流状态的重要时间节点之一。 + */ private Date sendTime; + /** + * 订单结束时间,可能表示订单完全完成(比如用户确认收货等操作后)的时间点,类型为Date,其具体含义和使用场景根据业务规则而定,例如可以用于统计订单的完成周期、计算业务绩效等相关业务操作,通过 @JsonFormat 注解保证其格式的规范性,便于数据的处理和展示。 + */ private Date endTime; + /** + * 订单关闭时间,常用于记录订单由于各种原因(如用户主动取消、超时未支付自动关闭等情况)被关闭的具体时间,类型为Date,在订单状态管理、数据分析(比如统计订单关闭率等)等业务场景中会用到这个时间字段,借助 @JsonFormat 注解确保其格式符合要求,方便数据交互和处理。 + */ private Date closeTime; + /** + * 订单创建时间,用于记录该订单最初被创建的具体时间点,类型为Date,是订单生命周期开始的重要标志,在查询订单历史记录、统计不同时间段的订单创建数量等业务操作中起着基础性的作用,通过 @JsonFormat 注解设定其格式为"yyyy-MM-dd HH:mm:ss.SSS",保证在各种数据处理场景下能以统一的格式呈现,便于使用和分析。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date createTime; + + /** + * 订单更新时间,用于记录订单信息最后一次被更新(比如修改收货地址、更新支付状态等任何对订单信息有修改操作时)的具体时间点,类型为Date,在数据跟踪、日志记录以及判断订单数据的时效性等业务场景中有着重要意义,同样借助 @JsonFormat 注解确保其格式为"yyyy-MM-dd HH:mm:ss.SSS",便于统一处理和展示。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date updateTime; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/OrderItem.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/OrderItem.java index f0b3b1d..1184116 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/OrderItem.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/OrderItem.java @@ -4,34 +4,74 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.math.BigDecimal; import java.util.Date; +/** + * 这个类(OrderItem)代表订单明细实体类,用于在Java程序中映射数据库里与订单明细相关的数据表结构,同时也在业务逻辑处理过程中用于传递订单中具体商品项的详细信息。 + * 它借助了Lombok库的相关注解来简化代码编写,其中 @Data 注解会自动帮我们生成像Getter、Setter、toString、equals、hashCode等常用方法,方便对类中的各个属性进行获取、设置以及对象整体的展示、比较等操作; + * @AllArgsConstructor 注解会生成一个包含所有参数的构造函数,这样在创建订单明细对象时,可以一次性传入所有属性值来完成初始化; + * @NoArgsConstructor 注解则生成一个无参构造函数,在一些特定场景下(例如JSON序列化、反序列化操作,或者某些框架在初始化对象时需要调用无参构造函数等情况)会起到作用,以确保对象能够正常被创建和使用。 + */ @Data @AllArgsConstructor @NoArgsConstructor public class OrderItem { + + /** + * 订单明细记录在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的订单明细记录,方便在数据库操作(如查询、更新、删除等)以及业务逻辑处理中准确地定位和处理具体的某条订单明细数据,类型为Integer。 + */ private Integer id; + /** + * 关联的用户ID,表明这条订单明细所属的用户,通过这个字段可以将订单明细与具体的用户进行关联,便于实现基于用户维度的订单明细查询、统计等业务操作,其类型为Integer,与数据库中存储用户ID的字段类型相对应,确保了数据的关联性和一致性。 + */ private Integer userId; + /** + * 所属订单的编号,用于明确这条订单明细是属于哪个订单的,通过这个字段可以将订单明细与对应的订单进行关联,从而构成完整的订单结构,在查询某个订单包含哪些商品明细、统计订单的商品总价等业务操作中起着关键作用,类型为Long,通常与订单实体类中的订单编号字段对应匹配。 + */ private Long orderNo; + /** + * 商品ID,代表该订单明细中所涉及商品的唯一标识,通过这个字段可以关联到具体的商品信息(比如商品的详细规格、库存等情况),用于确定是哪个具体商品的明细记录,类型为Integer,方便进行商品相关的业务操作以及数据查询等,例如查找商品详情、核对商品库存等。 + */ private Integer productId; + /** + * 商品名称,用于展示订单明细中商品的具体名称,方便用户或者业务人员直观地知晓购买的是什么商品,类型为String,例如“智能手机”“纯棉T恤”等,在订单展示、打印以及商品信息统计等业务场景中会使用到这个字段。 + */ private String productName; + /** + * 商品图片的路径或链接(具体取决于业务系统的存储和使用方式),可以用于在前端页面展示商品的缩略图等图片信息,让用户更直观地看到所购买商品的外观样子,类型为String,方便实现商品图片的展示功能,提升用户体验,比如在订单详情页面展示商品图片。 + */ private String productImage; + /** + * 当前商品的单价,使用BigDecimal类型来精确表示价格数值,因为商品价格通常可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题,用于记录在生成该订单明细时商品的实际单价情况,在计算商品总价、统计订单金额等业务操作中是重要的基础数据,类型为BigDecimal。 + */ private BigDecimal currentUnitPrice; + /** + * 商品的购买数量,用于记录在这个订单明细中对应商品购买了多少件,类型为Integer,用户可以根据自己的需求选择购买不同数量的商品,在计算商品总价(通过单价乘以数量)以及库存扣减等业务操作中会用到这个数量信息。 + */ private Integer quantity; + /** + * 商品的总价,同样使用BigDecimal类型来精确表示金额数值,它是通过当前商品的单价(currentUnitPrice)乘以购买数量(quantity)计算得出的,用于准确记录该订单明细中商品的总金额,在订单金额统计、财务核算等业务场景中起着关键作用,类型为BigDecimal。 + */ private BigDecimal totalPrice; + /** + * 订单明细记录的创建时间,使用 @JsonFormat 注解指定了日期时间的格式化格式为"yyyy-MM-dd HH:mm:ss.SSS"(精确到毫秒的日期时间格式),这样在将订单明细对象转换为JSON字符串(比如在Web API响应返回订单明细数据、前后端数据传输等场景)时,能够以统一、规范且易读的格式展示创建时间信息,方便前端或者其他接收方进行解析和展示,同时也确保了在数据库存储和读取该时间字段时遵循相应的格式要求,类型为Date。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date createTime; + + /** + * 订单明细记录的更新时间,其作用和创建时间类似,也是通过 @JsonFormat 注解设定格式为"yyyy-MM-dd HH:mm:ss.SSS",用于记录订单明细信息最后一次被更新(比如商品单价调整、数量修改等情况)的具体时间点,在数据跟踪、日志记录以及判断订单明细数据的时效性等业务场景中有着重要意义,类型为Date。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date updateTime; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/PayInfo.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/PayInfo.java index 96c437e..d630e6d 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/PayInfo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/PayInfo.java @@ -3,32 +3,57 @@ package com.njupt.swg.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.util.Date; /** - * @Author swg. - * @Date 2019/1/8 10:56 - * @CONTACT 317758022@qq.com - * @DESC + * 这个类(PayInfo)代表支付信息实体类,主要用于在Java程序中映射数据库里与支付相关的数据表结构,同时也在业务逻辑处理过程中承载和传递支付相关的各种信息。 + * 它借助了Lombok库提供的注解来简化代码编写,其中: + * - @Data 注解会自动生成常用的Getter、Setter、toString、equals、hashCode等方法,方便对类中的属性进行获取、设置以及在需要展示对象内容、比较对象等场景下使用,减少了手动编写这些重复代码的工作量。 + * - @NoArgsConstructor 注解会生成一个无参构造函数,在某些情况下(比如JSON序列化、反序列化时,部分框架初始化对象要求有可访问的无参构造函数等)是很有必要的,能确保对象可以正常被创建。 + * - @AllArgsConstructor 注解则会生成一个包含所有参数的构造函数,方便在创建PayInfo对象时,可以一次性传入所有属性值来完成初始化,适用于已知所有属性值的情况下快速创建对象。 */ @Data @NoArgsConstructor @AllArgsConstructor public class PayInfo { + + /** + * 支付信息记录在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的支付信息记录,方便在数据库操作(如查询、更新、删除等)以及整个业务逻辑处理中准确地定位和处理具体的某条支付信息数据,类型为Integer。 + */ private Integer id; + /** + * 关联的用户ID,用于表明这条支付信息是属于哪个用户的支付操作记录,通过这个字段可以将支付信息与具体的用户进行关联,便于后续基于用户维度进行支付相关的查询、统计等业务操作,类型为Integer,与数据库中存储用户ID的字段类型相对应,确保了数据的关联性和一致性。 + */ private Integer userId; + /** + * 关联的订单编号,用于明确这条支付信息是对应哪个订单的支付情况,通过这个字段可以将支付信息与对应的订单关联起来,从而在业务中能清晰地知晓某个订单的支付详情,比如查询某个订单是通过何种方式支付的等操作,类型为Long,通常与订单实体类中的订单编号字段相匹配。 + */ private Long orderNo; + /** + * 支付平台的标识符,用于表示该支付操作是通过哪个支付平台完成的,例如可能取值为1表示微信支付平台、2表示支付宝支付平台、3表示银联支付平台等(具体的取值和对应的支付平台根据业务定义来确定),类型为Integer,方便在业务逻辑中对不同支付平台的支付信息进行分类、统计以及相关的处理操作。 + */ private Integer payPlatform; + /** + * 支付平台生成的支付流水号等唯一标识该笔支付操作在对应支付平台上的编号,类型为String,这个编号可以用于在与支付平台进行对账、查询支付详情、处理退款等业务操作时,在支付平台端准确地定位到该笔支付记录,不同支付平台会有各自不同格式和规则的平台编号。 + */ private String platformNumber; + /** + * 支付平台返回的支付状态信息,通常是一个字符串描述,比如可能取值为"SUCCESS"表示支付成功、"FAIL"表示支付失败、"PROCESSING"表示支付处理中(具体的取值和对应的含义由支付平台的接口文档以及业务规则来确定),类型为String,用于在业务系统中获取并展示支付的实时状态,方便后续根据支付状态进行相应的业务处理,比如支付成功后更新订单状态等。 + */ private String platformStatus; + /** + * 支付信息记录的创建时间,用于记录这条支付信息最初被创建的具体时间点,在查询支付历史记录、统计不同时间段的支付情况等业务操作中会用到这个时间信息,类型为Date,是支付信息生命周期开始的重要标志,同时也可以用于分析支付业务的时间分布等情况。 + */ private Date createTime; + /** + * 支付信息记录的更新时间,用于记录支付信息最后一次被更新(比如支付状态发生变化后进行数据更新、补充一些支付相关的备注信息等情况)的具体时间点,类型为Date,在数据跟踪、日志记录以及判断支付信息数据的时效性等业务场景中有着重要意义。 + */ private Date updateTime; -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Product.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Product.java index 72c0708..0bb3488 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Product.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Product.java @@ -3,35 +3,78 @@ package com.njupt.swg.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.math.BigDecimal; import java.util.Date; +/** + * 这个类(Product)代表商品实体类,用于在Java程序中映射数据库里与商品相关的数据表结构,同时也在业务逻辑处理过程中承载和传递商品相关的各种信息,是整个电商系统或者涉及商品管理业务中非常核心的一个实体类。 + * 它借助了Lombok库提供的注解来简化代码编写,具体如下: + * - @Data 注解自动生成了像Getter、Setter、toString、equals、hashCode等常用方法,使得我们在获取和设置类中各个属性值时更加便捷,并且在需要展示商品对象的整体信息、比较不同商品对象等场景下能够方便地使用这些自动生成的方法,减少了手动编写这些重复代码的工作量。 + * - @AllArgsConstructor 注解会生成一个包含所有参数的构造函数,这在创建商品对象时很有用,我们可以一次性传入所有属性值来完成对象的初始化,适用于已知完整商品信息的情况下快速构建商品对象。 + * - @NoArgsConstructor 注解生成一个无参构造函数,在一些特定场景下(比如JSON序列化、反序列化时,部分框架初始化对象要求有可访问的无参构造函数等)是必不可少的,能确保商品对象可以正常被创建和使用。 + */ @Data @AllArgsConstructor @NoArgsConstructor public class Product { + + /** + * 商品在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的商品记录,方便在数据库进行查询、更新、删除等操作以及整个业务逻辑处理中准确地定位和处理具体的某件商品数据,类型为Integer。 + */ private Integer id; + /** + * 商品所属的分类ID,用于表明该商品属于哪个分类,通过这个字段可以将商品与对应的商品分类进行关联,便于实现基于分类维度的商品查询、统计以及分类展示等业务操作,比如按照电子产品、服装、食品等分类来查找商品,类型为Integer,与数据库中存储商品分类ID的字段类型相对应,确保了数据的关联性和一致性。 + */ private Integer categoryId; + /** + * 商品的名称,用于直观地展示商品是什么,方便用户识别和选择商品,类型为String,例如“华为手机”“耐克运动鞋”等,是商品展示、搜索等业务场景中非常重要的一个属性,用户通常会通过商品名称来查找自己想要购买的商品。 + */ private String name; + /** + * 商品的副标题,一般是对商品名称的补充说明,用于提供更多关于商品特点、功能等方面的简要描述,进一步帮助用户了解商品详情,类型为String,比如商品名称是“华为手机”,副标题可能是“麒麟处理器,高清大屏,超长续航”,在商品详情页面等地方会展示副标题信息,增强对商品的宣传效果。 + */ private String subtitle; + /** + * 商品的主图片路径或链接(具体取决于业务系统的存储和使用方式),通常用于在前端页面展示商品的主要展示图片,让用户可以直观地看到商品的外观样子,类型为String,比如在商品列表页、详情页等地方展示该图片,提升用户的视觉体验,方便用户更好地了解商品外观特征。 + */ private String mainImage; + /** + * 商品的子图片列表(可以是多个图片路径或链接,以某种特定格式存储,如逗号分隔的字符串等,具体由业务决定),用于展示商品的其他相关图片,从不同角度、细节展示商品,进一步丰富用户对商品外观、功能等方面的了解,类型为String,在商品详情页面等地方可以展示这些子图片,让用户更全面地查看商品情况。 + */ private String subImages; + /** + * 商品的详细描述信息,一般包含了商品的各种详细参数、功能特点、使用说明等全面的文字介绍内容,用于给用户提供更深入、详细的商品信息,方便用户全面了解商品是否符合自己的需求,类型为String,例如手机商品的详细描述会包含处理器型号、内存大小、摄像头参数、操作系统等详细内容,在商品详情页面以较大篇幅展示该内容。 + */ private String detail; + /** + * 商品的价格,使用BigDecimal类型来精确表示价格数值,因为商品价格通常可能包含小数部分,并且BigDecimal能够避免浮点数运算带来的精度问题,用于准确记录商品的售价情况,在商品展示、购买、价格统计等业务操作中起着关键作用,类型为BigDecimal。 + */ private BigDecimal price; + /** + * 商品的库存数量,用于记录当前商品还有多少件可售卖,类型为Integer,在用户购买商品时需要根据库存情况判断是否能正常下单,同时库存数量也会随着商品的销售、补货等操作而动态变化,是商品管理和销售业务中一个重要的监控指标。 + */ private Integer stock; + /** + * 商品的状态标识符,用于表示商品当前所处的状态,例如可能取值为0表示下架、1表示上架、2表示缺货等(具体的取值和对应的含义由业务规则来设定),类型为Integer,在商品管理流程中,通过判断这个状态字段来决定后续可以进行哪些业务操作,比如只有上架状态的商品才能被用户购买等,是商品业务逻辑控制的关键因素之一。 + */ private Integer status; + /** + * 商品的创建时间,用于记录该商品信息最初被录入系统的具体时间点,在查询商品历史记录、统计不同时间段的商品上架情况等业务操作中会用到这个时间信息,类型为Date,是商品生命周期开始的重要标志,同时也可以用于分析商品业务的时间分布等情况。 + */ private Date createTime; + /** + * 商品的更新时间,用于记录商品信息最后一次被更新(比如修改商品价格、更新商品描述、调整库存数量等任何对商品信息有修改操作时)的具体时间点,类型为Date,在数据跟踪、日志记录以及判断商品数据的时效性等业务场景中有着重要意义。 + */ private Date updateTime; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Shipping.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Shipping.java index da5beca..ddd9989 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/Shipping.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/Shipping.java @@ -4,35 +4,80 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.util.Date; +/** + * 这个类(Shipping)代表收货地址实体类,用于在Java程序中映射数据库里与收货地址相关的数据表结构,同时也在业务逻辑处理过程中承载和传递收货地址相关的各种信息,是电商系统或者涉及商品配送业务中用于管理收货地址的重要实体类。 + * + * 它借助了Lombok库提供的注解来简化代码编写,各注解作用如下: + * - @Data 注解会自动生成如Getter、Setter、toString、equals、hashCode等常用方法,使得我们在获取和设置类中各个属性值时更加便捷,并且在需要展示收货地址对象整体信息、比较不同收货地址对象等场景下能够方便地使用这些自动生成的方法,减少了手动编写这些重复代码的工作量。 + * - @NoArgsConstructor 注解会生成一个无参构造函数,在一些特定场景下(比如JSON序列化、反序列化时,部分框架初始化对象要求有可访问的无参构造函数等)是必不可少的,能确保收货地址对象可以正常被创建和使用。 + * - @AllArgsConstructor 注解则会生成一个包含所有参数的构造函数,方便在创建收货地址对象时,可以一次性传入所有属性值来完成对象的初始化,适用于已知完整收货地址信息的情况下快速构建对象。 + */ @Data @NoArgsConstructor @AllArgsConstructor public class Shipping { + + /** + * 收货地址记录在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的收货地址记录,方便在数据库进行查询、更新、删除等操作以及整个业务逻辑处理中准确地定位和处理具体的某条收货地址数据,类型为Integer。 + */ private Integer id; + /** + * 关联的用户ID,用于表明这个收货地址是属于哪个用户的,通过这个字段可以将收货地址与具体的用户进行关联,便于实现基于用户维度的收货地址查询、管理等业务操作,比如用户查看自己保存的多个收货地址、设置默认收货地址等,类型为Integer,与数据库中存储用户ID的字段类型相对应,确保了数据的关联性和一致性。 + */ private Integer userId; + /** + * 收件人的姓名,用于明确商品应该寄送给谁,类型为String,是收货地址信息中很重要的一部分,快递人员会根据这个姓名来确认收件人身份,在填写收货地址、快递配送等业务场景中都会用到该信息。 + */ private String receiverName; + /** + * 收件人的固定电话号码,虽然在现在移动互联网时代,使用相对较少,但部分场景下可能仍会用到,类型为String,可作为联系收件人的一种方式,与手机号码等其他联系方式共同构成完整的收件人联系信息。 + */ private String receiverPhone; + /** + * 收件人的手机号码,是目前快递配送等业务中最常用的联系收件人的方式,快递员在配送过程中通常会拨打这个号码来通知收件人取件等,类型为String,确保收件人能及时收到商品相关通知,在收货地址相关业务操作中是关键信息之一。 + */ private String receiverMobile; + /** + * 收件人所在的省份信息,用于明确收货地址的大致区域范围,类型为String,例如“江苏省”“广东省”等,是收货地址中地理位置描述的重要部分,对于物流配送规划路线、确定大致的运输范围等有重要作用。 + */ private String receiverProvince; + /** + * 收件人所在的城市信息,在省份之下进一步细化收货地址的地理位置,类型为String,比如“南京市”“深圳市”等,能更精准地定位收货地址,方便物流将商品准确配送到相应城市区域内的具体地址,在物流配送等业务中是必不可少的信息。 + */ private String receiverCity; + /** + * 收件人所在的区(县)信息,在城市范围内再次细化收货地址,使得收货地址更加精确,类型为String,例如“鼓楼区”“福田区”等,有助于快递员更准确地找到具体的收货地点,提高配送效率,减少配送失误。 + */ private String receiverDistrict; + /** + * 收件人的详细地址信息,除了上述省、市、区等信息外,具体到街道、门牌号、具体楼层房间号等详细内容,类型为String,是快递员最终能够准确将商品送达收件人手中的关键信息,例如“XX路XX号XX小区X栋X单元X室”,完整地描述了收货的具体位置。 + */ private String receiverAddress; + /** + * 收件人所在地的邮政编码,虽然在现在的物流配送中使用频率相对没那么高,但在一些传统的邮政业务或者部分需要精确地址匹配的场景下可能会用到,类型为String,同样是收货地址信息的一部分,辅助完善收货地址的整体描述。 + */ private String receiverZip; + /** + * 收货地址记录的创建时间,使用了 @JsonFormat 注解来指定日期时间的格式化格式为“yyyy-MM-dd HH:mm:ss.SSS”(精确到毫秒的日期时间格式),这样在将收货地址对象转换为JSON字符串(比如在Web API响应返回收货地址数据、前后端数据传输等场景)时,能够以统一、规范且易读的格式展示创建时间信息,方便前端或者其他接收方进行解析和展示,同时也确保了在数据库存储和读取该时间字段时遵循相应的格式要求,类型为Date。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date createTime; + + /** + * 收货地址记录的更新时间,其作用和创建时间类似,也是通过 @JsonFormat 注解设定格式为“yyyy-MM-dd HH:mm:ss.SSS”,用于记录收货地址信息最后一次被更新(比如修改收件人电话号码、变更详细地址等情况)的具体时间点,在数据跟踪、日志记录以及判断收货地址数据的时效性等业务场景中有着重要意义,类型为Date。 + */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date updateTime; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/entity/User.java b/snailmall-order-service/src/main/java/com/njupt/swg/entity/User.java index 4354b81..6c3906a 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/entity/User.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/entity/User.java @@ -4,7 +4,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; - import java.io.Serializable; import java.util.Date; @@ -13,31 +12,68 @@ import java.util.Date; * @Date 2018/12/31 21:01 * @CONTACT 317758022@qq.com * @DESC 用户实体类 + * 这个类(User)代表用户实体,在Java程序中用于映射数据库里与用户相关的数据表结构,同时也是整个业务逻辑处理过程中传递用户相关信息的核心载体,是诸多业务操作(如登录、注册、权限管理等)围绕的重要对象。 + * 它使用了Lombok库提供的多个注解来简化代码编写,各注解作用如下: + * - @Data 注解会自动生成常用的Getter、Setter方法用于获取和设置类中的各个属性值,还会生成toString、equals、hashCode等方法,方便在展示对象信息、比较不同用户对象等场景下使用,减少了手动编写这些重复代码的工作量。 + * - @NoArgsConstructor 注解会生成一个无参构造函数,在一些特定场景下(比如JSON序列化、反序列化时,部分框架初始化对象要求有可访问的无参构造函数等)是必不可少的,能确保用户对象可以正常被创建和使用。 + * - @AllArgsConstructor 注解则会生成一个包含所有参数的构造函数,方便在已知所有用户属性值的情况下,一次性传入这些值来快速完成用户对象的初始化工作。 + * - @ToString 注解会自动重写toString方法,使得在将用户对象转换为字符串表示形式(比如在调试打印对象、日志记录等场景)时,能够以一种更易读、直观的方式展示对象包含的各个属性值及其对应内容。 + * 此外,该类还实现了Serializable接口,这意味着用户对象可以被序列化(比如转换为字节流以便存储到文件、在网络中传输等)和反序列化(从字节流恢复为对象),确保在分布式系统、持久化存储等场景下用户对象的数据能够正确地保存和恢复,提高了对象在不同环境下的通用性和交互性。 */ @Data @NoArgsConstructor @AllArgsConstructor @ToString public class User implements Serializable { + + /** + * 用户在数据库中的唯一标识符,也就是主键字段,用于唯一区分不同的用户记录,方便在数据库进行查询、更新、删除等操作以及整个业务逻辑处理中准确地定位和处理具体的某一用户的数据,类型为Integer。 + */ private Integer id; + /** + * 用户的登录用户名,用于用户在登录系统时进行身份识别,通常要求具有唯一性(在业务规则限定下),类型为String,是用户进行登录操作、系统交互等场景中非常关键的一个属性,用户需要准确输入自己的用户名才能登录系统。 + */ private String username; + /** + * 用户的登录密码,经过一定的加密处理(一般不会以明文形式存储)后存储在数据库中,用于验证用户身份的合法性,在用户登录时与输入的密码进行比对,只有匹配成功才能登录系统,类型为String,保障系统的安全性,防止用户账号被非法访问。 + */ private String password; + /** + * 用户的电子邮箱地址,可用于多种用途,比如找回密码、接收系统通知、验证账号等操作,类型为String,是与用户进行信息沟通、账号安全管理等方面的一种重要联系方式,用户可以通过邮箱接收重要的系统消息。 + */ private String email; + /** + * 用户的手机号码,同样也是重要的联系方式,常用于接收验证码、短信通知等,在如今的移动互联网环境下,很多业务操作(如登录验证、密码修改等)可能会借助手机短信验证码来增强安全性,类型为String,方便系统与用户之间进行及时、便捷的沟通。 + */ private String phone; + /** + * 用户设置的密保问题,用于在忘记密码等情况下辅助验证用户身份,用户需要正确回答自己设置的密保问题才能进行后续的密码重置等操作,类型为String,是保障账号安全、帮助用户找回密码的一种手段。 + */ private String question; + /** + * 用户针对密保问题设置的答案,只有输入正确的答案才能通过密保验证环节,进而进行如密码重置之类的操作,类型为String,与question属性相对应,共同构成密保机制的关键部分。 + */ private String answer; + /** + * 用户的角色标识符,用于区分不同权限的用户,这里约定0表示管理员角色,具有较高的系统管理权限,可进行如用户管理、系统配置等操作;1表示普通用户角色,权限相对受限,主要进行与自身相关的常规业务操作(如购物、查看订单等),类型为Integer,在权限控制、功能访问限制等业务逻辑中起着关键作用,决定了用户能在系统中执行哪些操作。 + */ //角色0-管理员,1-普通用户 private Integer role; + /** + * 用户账号的创建时间,用于记录该用户信息最初被录入系统的具体时间点,在查询用户注册历史、统计不同时间段的用户增长情况等业务操作中会用到这个时间信息,类型为Date,是用户生命周期开始的重要标志,同时也可以用于分析用户业务的时间分布等情况。 + */ private Date createTime; + /** + * 用户信息最后一次被更新(比如修改密码、更新电子邮箱地址、修改密保问题等任何对用户信息有修改操作时)的具体时间点,类型为Date,在数据跟踪、日志记录以及判断用户数据的时效性等业务场景中有着重要意义。 + */ private Date updateTime; - } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/message/MessageReceiver.java b/snailmall-order-service/src/main/java/com/njupt/swg/message/MessageReceiver.java index 3b674cb..4b04506 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/message/MessageReceiver.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/message/MessageReceiver.java @@ -18,26 +18,48 @@ import java.util.List; * @Date 2019/1/7 13:23 * @CONTACT 317758022@qq.com * @DESC + * 这个类(MessageReceiver)是一个消息接收者组件,用于处理从消息队列(RabbitMQ等消息中间件)接收到的消息,在整个系统架构中扮演着响应消息并执行相应业务逻辑的角色, + * 例如这里涉及到根据接收到的消息进行库存扣减以及下订单等操作,通过整合Spring的相关注解以及依赖注入机制,实现了消息驱动的业务流程处理,并且利用日志记录关键信息便于后续的调试和问题排查。 */ @Component @Slf4j public class MessageReceiver { + + // 通过Spring的依赖注入机制,自动注入一个IOrderService接口的实现类对象,IOrderService应该定义了一系列与订单业务相关的方法, + // 比如库存扣减与下订单的关联操作等方法(这里推测是stockAndOrderprocess方法实现了相关逻辑),具体的业务逻辑实现在其对应的实现类中,由Spring根据配置去查找并注入合适的实现类实例, + // 这个对象会在接收到消息后被调用,用于执行后续与订单相关的业务处理操作。 @Autowired private IOrderService orderService; + /** + * 这个方法使用了Spring AMQP提供的 @RabbitListener 注解,用于标记该方法作为一个消息监听器,监听特定的消息队列绑定关系,当对应的消息队列有消息到达时,该方法会被自动触发执行。 + * 具体的队列绑定配置通过 @QueueBinding 注解来指定,其中: + * - 通过 @Queue("order-queue") 创建了一个名为"order-queue"的队列,如果队列不存在则会自动创建,这个队列是实际接收消息的地方。 + * - 通过 @Exchange("order-exchange") 定义了一个名为"order-exchange"的交换机,交换机用于接收生产者发送的消息,并根据一定的规则(比如路由键等,这里未详细展示路由相关配置)将消息路由到对应的队列中,这里将消息路由到了"order-queue"队列。 + * 当有消息发送到这个绑定的队列时,该方法会接收到消息内容(以字符串形式传入,参数message就是接收到的消息字符串),然后执行后续的业务逻辑处理。 + * + * @param message 从消息队列中接收到的消息内容,以字符串的形式传入,这个消息内容通常是经过序列化后的信息,在这里需要进一步解析处理才能得到具体可用的业务数据,比如可能是包含了用户ID、商品信息等用于后续扣减库存、下订单操作的数据。 + */ @RabbitListener(bindings = @QueueBinding( value = @Queue("order-queue"), exchange = @Exchange("order-exchange") )) - public void proess(String message){ - log.info("接收到的消息为:{}",message); + public void proess(String message) { + // 使用日志记录接收到的消息内容,方便后续查看消息的原始情况,进行调试或者问题排查等操作,这里通过 {} 占位符的方式,将message变量的值填充到日志信息中,以一种格式化的方式输出消息内容。 + log.info("接收到的消息为:{}", message); + + // 调用JsonUtil工具类的Str2Obj方法,尝试将接收到的消息字符串(message)反序列化为一个List集合,集合中的元素类型为MessageVo(这里假设MessageVo是一个自定义的用于承载消息中具体业务数据的视图对象,包含了如用户ID、商品相关信息等字段), + // 通过指定List.class和MessageVo.class来明确反序列化的目标类型,以便后续能够从解析后的对象中获取具体的业务数据进行相应的业务操作,如果反序列化失败可能会抛出异常(具体的异常处理逻辑可能在工具类内部或者外层调用处有相应设置)。 List result = JsonUtil.Str2Obj(message, List.class, MessageVo.class); - log.info("【MQ解析数据,前者为userId,后者为product信息:{}】",result); - //扣减库存、下订单 - //是先扣减库存,扣减成功才可以下订单,但是这是两个数据库,那么属于跨库的事务,所以如何解决呢? - //一种方案是:利用消息队列,订单服务订阅扣减库存服务,一旦发现数据库的库存扣减成功,就去扣减插入订单; - //如果库存扣减不成功,那么订单也不会写入 + // 使用日志记录解析后的数据情况,方便查看消息解析出来的数据是否符合预期,同样通过 {} 占位符的方式将result变量的值填充到日志信息中进行格式化输出,这里简单描述了一下数据大概包含的内容(假设MessageVo中包含userId和product信息相关字段),便于在日志中快速了解解析结果。 + log.info("【MQ解析数据,前者为userId,后者为product信息:{}】", result); + + // 以下是业务逻辑相关的注释: + // 这里涉及到的业务需求是要先扣减库存,只有在库存扣减成功的情况下才可以下订单,但是库存和订单可能存储在不同的数据库中(属于跨库的情况),这种跨库操作在事务处理上存在一定复杂性,需要合适的解决方案来保证数据的一致性和业务的正确性。 + // 一种可行的方案描述如下: + // 利用消息队列的机制,让订单服务订阅扣减库存服务相关的消息,一旦通过消息机制发现数据库中的库存扣减成功(可能是扣减库存服务在完成扣减操作后发送了相应的成功消息到消息队列中,这里未展示发送消息部分代码), + // 那么订单服务就会执行后续的操作,即去进行订单的插入等相关操作;如果库存扣减不成功(同样可能通过消息或者其他机制得知),那么订单就不会被写入,以此来保证在跨库情况下库存和订单操作的业务逻辑完整性和数据一致性。 + // 这里调用orderService的stockAndOrderprocess方法,将解析后的包含业务数据的result对象传入,这个方法内部应该实现了上述的根据库存扣减情况来决定是否插入订单的具体业务逻辑,具体的逻辑实现在IOrderService的实现类中。 orderService.stockAndOrderprocess(result); } - -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartProductVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartProductVo.java index 0d7bd32..00e4e23 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartProductVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartProductVo.java @@ -3,7 +3,6 @@ package com.njupt.swg.vo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; - import java.io.Serializable; import java.math.BigDecimal; @@ -12,23 +11,77 @@ import java.math.BigDecimal; * @Date 2019/1/5 15:18 * @CONTACT 317758022@qq.com * @DESC + * 这个类(CartProductVo)是一个视图对象(Value Object,简称VO),通常用于在不同的业务层之间传递数据,特别是在涉及购物车相关业务场景下, + * 它整合了与购物车中商品展示、计算以及状态判断等相关的多个属性,将这些属性组合在一起方便进行统一的数据传递和处理,同时实现了Serializable接口使其可以方便地进行序列化操作(比如在网络传输、缓存存储等场景下), + * 并且借助Lombok库的注解简化了代码编写,自动生成常用的构造函数、Getter和Setter等方法,方便对对象中的属性进行操作。 */ @NoArgsConstructor @AllArgsConstructor @Data public class CartProductVo implements Serializable { + + /** + * 购物车商品记录的唯一标识符,可用于在特定业务场景下唯一区分不同的购物车商品记录,比如在数据库操作或者业务逻辑处理中准确地定位和处理某条具体的购物车商品相关数据,类型为Integer。 + */ private Integer id; + + /** + * 关联的用户ID,用于表明这个购物车商品记录是属于哪个用户的,通过该字段可以将购物车商品与具体用户进行关联,便于实现基于用户维度的购物车商品查询、统计以及相关业务操作,例如获取某个用户购物车中所有商品的信息等,类型为Integer,与数据库中存储用户ID的字段类型相对应,确保数据的关联性。 + */ private Integer userId; + + /** + * 商品的唯一标识符,代表购物车中该商品在整个商品体系里的编号,通过这个编号可以获取到商品的详细信息(如商品的详细描述、库存信息等),类型为Integer,方便进行商品相关的业务操作,比如查询商品详情、核对库存等,并且与其他业务模块中涉及的商品ID进行关联和匹配。 + */ private Integer productId; - private Integer quantity;//购物车中此商品的数量 + + /** + * 购物车中此商品的数量,用于记录用户在购物车中添加该商品的具体数量,用户可以根据自己的需求增加或减少商品数量,类型为Integer,在计算商品总价、判断库存是否足够等业务操作中会用到这个数量信息,是购物车商品业务逻辑中一个关键的属性。 + */ + private Integer quantity; + + /** + * 商品的名称,用于直观地展示购物车中商品是什么,方便用户识别和确认商品,类型为String,例如“华为手机”“耐克运动鞋”等,是在购物车页面展示商品基本信息给用户查看的重要内容,用户可以通过商品名称快速了解自己添加到购物车的商品是什么。 + */ private String productName; + + /** + * 商品的副标题,一般是对商品名称的补充说明,用于提供更多关于商品特点、功能等方面的简要描述,进一步帮助用户了解商品详情,类型为String,比如商品名称是“华为手机”,副标题可能是“麒麟处理器,高清大屏,超长续航”,在购物车页面等地方展示副标题信息,可以增强用户对商品的了解程度。 + */ private String productSubtitle; + + /** + * 商品的主图片路径或链接(具体取决于业务系统的存储和使用方式),通常用于在购物车页面展示商品的主要展示图片,让用户可以直观地看到商品的外观样子,类型为String,比如在购物车列表中展示该图片,提升用户的视觉体验,方便用户更好地确认商品外观特征。 + */ private String productMainImage; + + /** + * 商品的价格,使用BigDecimal类型来精确表示价格数值,因为商品价格通常可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题,用于准确记录商品的当前售价情况,在计算商品总价(通过单价乘以数量)以及展示购物车商品价格信息等业务操作中起着关键作用,类型为BigDecimal。 + */ private BigDecimal productPrice; + + /** + * 商品的状态标识符,用于表示商品当前所处的状态,例如可能取值为0表示下架、1表示上架、2表示缺货等(具体的取值和对应的含义由业务规则来设定),类型为Integer,在购物车相关业务中,通过判断这个状态字段来决定是否可以继续对该商品进行操作(比如下架商品可能就不能进行结算等操作了),是商品业务逻辑控制的关键因素之一。 + */ private Integer productStatus; + + /** + * 商品的总价,同样使用BigDecimal类型来精确表示金额数值,它是通过商品的单价(productPrice)乘以购物车中该商品的数量(quantity)计算得出的,用于准确记录购物车中该商品的总金额,在购物车金额统计、显示商品总价信息等业务场景中起着重要作用,类型为BigDecimal。 + */ private BigDecimal productTotalPrice; + + /** + * 商品的库存数量,用于记录当前商品还有多少件可售卖,在购物车业务中可以用来提示用户商品的库存剩余情况,以及判断用户添加的商品数量是否超过库存等,类型为Integer,是商品管理和购物车操作中一个重要的监控指标,并且会随着商品的销售、补货等操作而动态变化。 + */ private Integer productStock; - private Integer productChecked;//此商品是否勾选 - private String limitQuantity;//限制数量的一个返回结果 -} + /** + * 此商品是否勾选的标识,通常取值可能为0表示未勾选,1表示勾选(具体取值和含义由业务规则确定),用于在购物车中区分用户选择要进行结算等操作的商品和未选择的商品,比如在批量结算购物车商品时,只会对勾选的商品进行相关操作,类型为Integer,是购物车商品操作控制的重要属性之一。 + */ + private Integer productChecked; + + /** + * 限制数量的一个返回结果,这个属性的具体含义可能根据业务逻辑有所不同,比如可能是在添加商品到购物车时,系统根据库存、限购规则等判断后返回的一个关于商品数量限制的提示信息(例如“最多只能购买5件”等),类型为String,用于向用户反馈与商品数量限制相关的内容,方便用户了解商品购买数量方面的限制情况。 + */ + private String limitQuantity; +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartVo.java index 0ea144d..f0ac57d 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/CartVo.java @@ -1,7 +1,6 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; import java.util.List; @@ -10,11 +9,33 @@ import java.util.List; * @Date 2019/1/5 15:18 * @CONTACT 317758022@qq.com * @DESC + * 这个类(CartVo)是一个视图对象(Value Object,简称VO),主要用于在购物车相关业务场景下,整合并传递购物车整体的相关信息,方便在不同的业务层之间(比如从业务逻辑层传递到表现层用于展示等情况)进行统一的数据交互, + * 借助Lombok库的 @Data 注解简化了代码编写,自动生成了常用的Getter、Setter等方法,使得对类中的属性进行获取和设置操作更加便捷,提高了代码的简洁性和可读性。 */ @Data public class CartVo { + + /** + * 一个包含了购物车中各个商品详细信息的列表,其中每个元素都是CartProductVo类型,CartProductVo类应该包含了如商品名称、价格、数量、勾选状态等单个商品在购物车中的各项属性信息, + * 通过这个列表可以获取到购物车中所有商品的详细情况,方便在展示购物车商品列表、进行商品相关操作(如修改商品数量、勾选状态等)以及统计购物车商品信息等业务场景中使用,是购物车整体信息的重要组成部分。 + */ private List cartProductVoList; + + /** + * 购物车中所有商品的总价,使用BigDecimal类型来精确表示金额数值,因为购物车总价是通过各个商品的单价乘以数量后累加得出的,可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题, + * 这个属性用于准确展示购物车中商品的总计金额,在购物车页面展示给用户查看当前购物车商品的总花费情况,以及在结算等业务操作中作为重要的金额参考依据,是购物车核心的金额属性信息。 + */ private BigDecimal cartTotalPrice; - private Boolean allChecked;//是否已经都勾选 + + /** + * 用于表示购物车中所有商品是否都已经被勾选的状态,类型为Boolean,取值为true表示购物车中的所有商品都已被勾选,意味着用户可能准备对购物车中的全部商品进行结算等操作;取值为false则表示还有部分商品未被勾选, + * 在批量操作购物车商品(如全选、批量结算、批量删除等)以及根据勾选状态判断后续业务流程(比如只有全选的情况下才显示结算按钮等)的业务场景中起着关键的判断作用,是购物车整体状态控制的重要属性之一。 + */ + private Boolean allChecked; + + /** + * 用于存放商品图片的主机地址(可以是服务器域名、IP地址加上对应的图片资源路径前缀等形式,具体取决于业务系统的图片存储和访问方式), + * 在展示购物车商品图片时,通过这个主机地址与商品图片的相对路径(通常存储在CartProductVo中的商品图片相关属性里)拼接,可以获取到完整的商品图片访问地址,从而在前端页面正确地展示购物车中商品的图片信息,提升用户的视觉体验,方便用户更好地识别商品。 + */ private String imageHost; -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/MessageVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/MessageVo.java index 6431df6..8a54bd5 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/MessageVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/MessageVo.java @@ -7,11 +7,32 @@ import lombok.Data; * @Date 2019/1/7 16:17 * @CONTACT 317758022@qq.com * @DESC + * 这个类(MessageVo)是一个视图对象(Value Object,简称VO),通常用于在系统不同模块或者业务层之间传递特定的消息相关数据,它整合了与业务操作(可能涉及订单、购物车等相关业务流程)有关的多个关键属性, + * 借助Lombok库的 @Data 注解简化代码编写,自动生成了常用的Getter、Setter等方法,方便对类中的属性进行获取和设置操作,提高了代码的简洁性以及在业务逻辑中使用这些属性的便捷性。 */ @Data public class MessageVo { + + /** + * 用户的唯一标识符,用于明确该消息关联的是哪个用户,在涉及多用户的业务系统中,通过这个字段可以区分不同用户的相关业务操作,比如判断是哪个用户发起的订单、关联的是哪个用户的购物车等情况,类型为Integer,与数据库中存储用户ID的字段类型相对应,确保数据的关联性和准确性。 + */ private Integer userId; + + /** + * 物流相关的标识符,可能代表着具体的收货地址信息、物流配送方式等与之关联的物流相关配置的编号(具体含义取决于业务系统对物流ID的定义和使用方式), + * 在订单处理流程中,通过这个字段可以确定该消息对应的订单使用的是哪种物流服务或者对应的收货地址情况等,便于后续进行物流相关的业务操作(如查询物流状态、安排发货等),类型为Integer。 + */ private Integer shippingId; + + /** + * 购物车相关的视图对象,包含了购物车中商品的详细列表、购物车总价、全选状态以及商品图片主机地址等信息(CartVo类中定义的相关属性),通过这个属性可以获取到完整的购物车情况, + * 在业务逻辑中,比如基于购物车内容生成订单时,就需要用到购物车中的商品信息等情况,它将购物车相关的综合数据整合在一起进行传递,方便后续操作直接获取和使用购物车整体数据,类型为CartVo。 + */ private CartVo cartVo; + + /** + * 订单编号,通常是一个具有唯一性的长整型数值,用于在业务系统中唯一标识一个订单,在与外部系统交互、订单查询展示、后续订单状态跟踪等场景中常作为关键的标识信息, + * 这个字段可以明确该消息所涉及的是哪个具体的订单,方便在整个业务流程中对订单进行定位、操作以及相关数据的关联处理,类型为long。 + */ private long orderNo; -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderItemVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderItemVo.java index 4d1a652..4a86ad7 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderItemVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderItemVo.java @@ -1,7 +1,6 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; /** @@ -9,24 +8,49 @@ import java.math.BigDecimal; * @Date 2019/1/5 21:47 * @CONTACT 317758022@qq.com * @DESC + * 这个类(OrderItemVo)是一个视图对象(Value Object,简称VO),主要用于在订单明细相关的业务场景下,整合并传递订单明细的关键信息,方便在不同的业务层之间进行数据交互, + * 借助Lombok库的 @Data 注解来简化代码编写,自动生成常用的Getter、Setter等方法,使得对类中的各个属性进行获取和设置操作更加便捷,提升了代码的简洁性和可读性。 */ @Data public class OrderItemVo { + /** + * 所属订单的编号,用于明确这条订单明细是属于哪个订单的,通过这个字段可以将订单明细与对应的订单进行关联,在查询某个订单包含哪些商品明细、统计订单的商品总价等业务操作中起着关键作用,类型为Long,通常与订单相关实体类中的订单编号字段对应匹配,方便进行数据的关联和业务逻辑处理。 + */ private Long orderNo; + /** + * 商品的唯一标识符,代表该订单明细中所涉及商品在整个商品体系里的编号,通过这个编号可以关联到具体的商品信息(比如商品的详细规格、库存等情况),用于确定是哪个具体商品的明细记录,类型为Integer,方便进行商品相关的业务操作以及数据查询等,例如查找商品详情、核对商品库存等。 + */ private Integer productId; + /** + * 商品的名称,用于直观地展示订单明细中商品的具体名称,方便用户或者业务人员知晓购买的是什么商品,类型为String,例如“智能手机”“纯棉T恤”等,在订单明细展示、打印以及商品信息统计等业务场景中会使用到这个字段,是展示商品基本信息的重要属性。 + */ private String productName; + /** + * 商品图片的路径或链接(具体取决于业务系统的存储和使用方式),可以用于在前端页面展示商品的缩略图等图片信息,让用户更直观地看到所购买商品的外观样子,类型为String,方便实现商品图片的展示功能,提升用户体验,比如在订单详情页面展示商品图片,帮助用户确认商品外观特征。 + */ private String productImage; + /** + * 当前商品的单价,使用BigDecimal类型来精确表示价格数值,因为商品价格通常可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题,用于记录在生成该订单明细时商品的实际单价情况,在计算商品总价(通过单价乘以数量)、统计订单金额等业务操作中是重要的基础数据,类型为BigDecimal。 + */ private BigDecimal currentUnitPrice; + /** + * 商品的购买数量,用于记录在这个订单明细中对应商品购买了多少件,类型为Integer,用户可以根据自己的需求选择购买不同数量的商品,在计算商品总价(通过单价乘以数量)以及库存扣减等业务操作中会用到这个数量信息,是订单明细中关于商品数量的关键属性。 + */ private Integer quantity; + /** + * 商品的总价,同样使用BigDecimal类型来精确表示金额数值,它是通过当前商品的单价(currentUnitPrice)乘以购买数量(quantity)计算得出的,用于准确记录该订单明细中商品的总金额,在订单金额统计、财务核算等业务场景中起着关键作用,类型为BigDecimal。 + */ private BigDecimal totalPrice; + /** + * 订单明细记录的创建时间,以字符串的形式存储(具体的日期时间格式应该遵循业务系统中统一的约定或者展示需求),用于记录这条订单明细最初被创建的时间点,在查询订单明细历史记录、统计不同时间段的订单明细生成情况等业务操作中会用到这个时间信息,类型为String,方便进行时间相关的业务处理以及展示给用户查看订单明细的创建时间情况。 + */ private String createTime; - -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderProductVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderProductVo.java index a537936..7cccf93 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderProductVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderProductVo.java @@ -1,7 +1,6 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; import java.util.List; @@ -10,10 +9,27 @@ import java.util.List; * @Date 2019/1/8 10:20 * @CONTACT 317758022@qq.com * @DESC + * 这个类(OrderProductVo)是一个视图对象(Value Object,简称VO),通常应用在与订单及订单商品相关的业务场景中,用于整合和传递涉及订单商品整体情况的相关数据,方便在不同业务层之间进行统一的数据交互, + * 借助Lombok库的 @Data 注解简化代码编写,自动生成常用的Getter、Setter等方法,使得对类中属性进行获取和设置操作更加便捷,提高了代码的简洁性与可读性。 */ @Data public class OrderProductVo { + + /** + * 一个包含了多个订单明细视图对象(OrderItemVo)的列表,OrderItemVo中包含了如商品名称、单价、数量、总价等单个商品在订单中的详细信息, + * 通过这个列表可以获取到该订单下所有商品明细的具体情况,方便在展示订单商品列表、统计订单商品相关信息(如各商品明细汇总等)以及后续基于商品明细进行其他业务操作等业务场景中使用,是描述订单商品组成情况的重要属性。 + */ private List orderItemVoList; + + /** + * 订单中所有商品的总价,使用BigDecimal类型来精确表示金额数值,因为这个总价是通过汇总订单中各个商品明细的总价(在OrderItemVo中计算得出的单个商品总价)得来的,可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题, + * 该属性用于准确展示订单中商品的总计金额,在订单页面展示给用户查看当前订单商品的总花费情况,以及在财务统计、订单金额相关业务操作等场景中作为重要的金额参考依据,是订单商品相关金额统计方面的关键属性。 + */ private BigDecimal productTotalPrice; + + /** + * 用于存放商品图片的主机地址(可以是服务器域名、IP地址加上对应的图片资源路径前缀等形式,具体取决于业务系统的图片存储和访问方式), + * 在展示订单中商品图片时,通过这个主机地址与各个商品明细(OrderItemVo中的商品图片相关属性)里的图片相对路径拼接,可以获取到完整的商品图片访问地址,从而在前端页面正确地展示订单中商品的图片信息,提升用户体验,方便用户更好地查看所购买商品的外观样子。 + */ private String imageHost; } \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderVo.java index 8d380dc..35523a1 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/OrderVo.java @@ -1,7 +1,6 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; import java.util.List; @@ -10,42 +9,99 @@ import java.util.List; * @Date 2019/1/5 21:47 * @CONTACT 317758022@qq.com * @DESC + * 这个类(OrderVo)是一个视图对象(Value Object,简称VO),主要用于在订单相关的业务场景下,整合并传递订单整体的详细信息,方便在不同的业务层之间(比如从业务逻辑层传递到展示层用于展示给用户查看订单详情等情况)进行数据交互, + * 借助Lombok库的 @Data 注解简化代码编写,自动生成常用的Getter、Setter等方法,使得对类中的各个属性进行获取和设置操作更加便捷,提升了代码的简洁性和可读性。 */ @Data public class OrderVo { + /** + * 订单编号,通常是一个具有唯一性的长整型数值,用于在业务系统中唯一标识一个订单,在与外部系统交互、订单查询展示、后续订单状态跟踪等场景中常作为关键的标识信息, + * 通过这个字段可以明确该订单的唯一性,方便在整个业务流程中对订单进行定位、操作以及相关数据的关联处理,类型为Long。 + */ private Long orderNo; + /** + * 订单的支付金额,使用BigDecimal类型来精确表示金额数值,因为订单支付金额通常可能包含小数部分,且BigDecimal能够避免浮点数运算带来的精度问题,用于准确记录用户为该订单实际支付的费用,比如购买商品的总价、加上运费(如果运费单独计算且包含在支付金额内)等的总和,在订单结算、财务统计等业务场景中起着关键作用,类型为BigDecimal。 + */ private BigDecimal payment; + /** + * 支付类型的标识符,用于表示用户采用的是哪种支付方式来完成该订单的支付,例如可能取值为1表示微信支付、2表示支付宝支付、3表示银行卡支付等(具体的取值和对应的支付方式根据业务定义来确定),类型为Integer,方便在业务逻辑中对不同支付方式的订单进行分类、统计以及相关的处理操作。 + */ private Integer paymentType; + /** + * 支付类型的描述信息,以字符串形式呈现,用于更直观地展示支付类型对应的具体支付方式名称等内容,比如“微信支付”“支付宝支付”等,相比于paymentType的数字标识符,这个属性能让用户更清晰地了解订单使用的支付手段,类型为String,在订单展示等场景中增强支付信息的可读性。 + */ private String paymentTypeDesc; + /** + * 运费金额,同样使用Integer类型来表示运费的数值(也可以根据业务需求使用BigDecimal等更精确的类型,如果运费需要精确到小数部分),用于记录该订单产生的物流运输费用,在订单费用明细、成本核算等业务操作中会涉及到对这个运费金额的处理和统计,类型为Integer。 + */ private Integer postage; + /** + * 订单状态的标识符,代表该订单当前所处的状态,例如可能取值为0表示已下单未支付、1表示已支付待发货、2表示已发货、3表示已签收、4表示已取消等(具体的状态值和对应的含义由业务规则来设定),类型为Integer,在整个订单处理流程中,通过判断这个状态字段来决定后续可以进行哪些业务操作,比如只有已支付的订单才能发货等,是订单业务逻辑控制的关键因素之一。 + */ private Integer status; - + /** + * 订单状态的描述信息,以字符串形式存储,用于更直观地展示订单当前状态对应的具体情况,比如“已支付待发货”“已发货”等,相较于status的数字标识符,这个属性方便用户更清晰地知晓订单所处的实际阶段,类型为String,在订单展示等业务场景中提升状态信息的可读性。 + */ private String statusDesc; + /** + * 支付时间,以字符串的形式存储(具体的日期时间格式应该遵循业务系统中统一的约定或者展示需求),用于记录订单完成支付操作的具体时间点,在查询订单支付历史记录、统计不同时间段的订单支付情况等业务操作中会用到这个时间信息,同时也便于展示给用户查看订单的支付时间情况,类型为String。 + */ private String paymentTime; - + /** + * 发货时间,同样以字符串形式存储,用于记录订单对应的商品开始进行物流配送的具体时间,在业务流程中,通过这个时间可以查看订单的发货及时性、统计发货效率等,是跟踪订单物流状态的重要时间节点之一,在展示订单物流相关信息给用户查看时也会用到,类型为String。 + */ private String sendTime; + /** + * 订单结束时间,以字符串形式保存,可能表示订单完全完成(比如用户确认收货等操作后)的时间点,其具体含义和使用场景根据业务规则而定,例如可以用于统计订单的完成周期、计算业务绩效等相关业务操作,在向用户展示订单整体流程的时间节点信息时会涉及到,类型为String。 + */ private String endTime; + /** + * 订单关闭时间,也是以字符串形式存在,常用于记录订单由于各种原因(如用户主动取消、超时未支付自动关闭等情况)被关闭的具体时间,在订单状态管理、数据分析(比如统计订单关闭率等)等业务场景中会用到这个时间字段,同时便于展示给用户查看订单关闭的相关时间信息,类型为String。 + */ private String closeTime; + /** + * 订单创建时间,以字符串格式存储,用于记录该订单最初被创建的具体时间点,在查询订单历史记录、统计不同时间段的订单创建数量等业务操作中起着基础性的作用,也是向用户展示订单起始时间的重要信息,类型为String。 + */ private String createTime; + /** + * 订单的明细,是一个包含了多个订单明细视图对象(OrderItemVo)的列表,OrderItemVo中包含了如商品名称、单价、数量、总价等单个商品在订单中的详细信息, + * 通过这个列表可以获取到该订单下所有商品明细的具体情况,方便在展示订单商品列表、统计订单商品相关信息(如各商品明细汇总等)以及后续基于商品明细进行其他业务操作(比如退货时根据明细处理等)等业务场景中使用,是描述订单商品组成情况的重要属性,类型为List。 + */ //订单的明细 private List orderItemVoList; + /** + * 用于存放商品图片的主机地址(可以是服务器域名、IP地址加上对应的图片资源路径前缀等形式,具体取决于业务系统的图片存储和访问方式), + * 在展示订单中商品图片时,通过这个主机地址与各个商品明细(OrderItemVo中的商品图片相关属性)里的图片相对路径拼接,可以获取到完整的商品图片访问地址,从而在前端页面正确地展示订单中商品的图片信息,提升用户体验,方便用户更好地查看所购买商品的外观样子,类型为String。 + */ private String imageHost; + + /** + * 物流相关的标识符,可能代表着具体的收货地址信息、物流配送方式等与之关联的物流相关配置的编号(具体含义取决于业务系统对物流ID的定义和使用方式), + * 在订单处理流程中,通过这个字段可以确定该订单使用的是哪种物流服务或者对应的收货地址情况等,便于后续进行物流相关的业务操作(如查询物流状态、安排发货等),类型为Integer。 + */ private Integer shippingId; + + /** + * 收件人的姓名,用于明确商品应该寄送给谁,类型为String,是收货地址信息中很重要的一部分,快递人员会根据这个姓名来确认收件人身份,在填写收货地址、快递配送等业务场景中都会用到该信息,在展示订单相关的收货信息给用户查看时也会涉及到,类型为String。 + */ private String receiverName; + /** + * 收货地址相关的视图对象(ShippingVo),应该包含了如收件人手机号、所在地区、详细地址等完整的收货地址相关信息,通过这个属性可以获取到订单对应的详细收货地址情况,方便在展示订单收货地址详情以及进行物流相关操作(比如根据地址安排配送等)等业务场景中使用,类型为ShippingVo。 + */ private ShippingVo shippingVo; -} +} \ No newline at end of file diff --git a/snailmall-order-service/src/main/java/com/njupt/swg/vo/ShippingVo.java b/snailmall-order-service/src/main/java/com/njupt/swg/vo/ShippingVo.java index 6d5d528..681e5f9 100644 --- a/snailmall-order-service/src/main/java/com/njupt/swg/vo/ShippingVo.java +++ b/snailmall-order-service/src/main/java/com/njupt/swg/vo/ShippingVo.java @@ -7,22 +7,49 @@ import lombok.Data; * @Date 2019/1/5 21:48 * @CONTACT 317758022@qq.com * @DESC + * 这个类(ShippingVo)是一个视图对象(Value Object,简称VO),主要用于在收货地址相关的业务场景下,整合并传递收货地址的关键信息,方便在不同的业务层之间(比如从业务逻辑层传递到展示层用于展示给用户查看收货地址详情等情况)进行数据交互, + * 借助Lombok库的 @Data 注解简化代码编写,自动生成常用的Getter、Setter等方法,使得对类中的各个属性进行获取和设置操作更加便捷,提升了代码的简洁性和可读性。 */ @Data public class ShippingVo { + + /** + * 收件人的姓名,用于明确商品应该寄送给谁,类型为String,是收货地址信息中很重要的一部分,快递人员会根据这个姓名来确认收件人身份,在填写收货地址、快递配送等业务场景中都会用到该信息,在向用户展示收货地址相关内容时,这也是一个关键展示项,方便用户核对收件人信息是否准确。 + */ private String receiverName; + /** + * 收件人的固定电话号码,虽然在现在移动互联网时代,使用相对较少,但部分场景下可能仍会用到,类型为String,可作为联系收件人的一种方式,与手机号码等其他联系方式共同构成完整的收件人联系信息,在收货地址信息记录以及必要的电话沟通场景中发挥作用。 + */ private String receiverPhone; + /** + * 收件人的手机号码,是目前快递配送等业务中最常用的联系收件人的方式,快递员在配送过程中通常会拨打这个号码来通知收件人取件等,类型为String,确保收件人能及时收到商品相关通知,在收货地址相关业务操作中是关键信息之一,也是用户在设置收货地址时必填的重要内容。 + */ private String receiverMobile; + /** + * 收件人所在的省份信息,用于明确收货地址的大致区域范围,类型为String,例如“江苏省”“广东省”等,是收货地址中地理位置描述的重要部分,对于物流配送规划路线、确定大致的运输范围等有重要作用,同时在展示收货地址给用户查看时,能让用户清晰知晓大致的收货地区域。 + */ private String receiverProvince; + /** + * 收件人所在的城市信息,在省份之下进一步细化收货地址的地理位置,类型为String,比如“南京市”“深圳市”等,能更精准地定位收货地址,方便物流将商品准确配送到相应城市区域内的具体地址,在物流配送等业务中是必不可少的信息,也是收货地址展示时的重要组成部分,便于用户确认具体所在城市。 + */ private String receiverCity; + /** + * 收件人所在的区(县)信息,在城市范围内再次细化收货地址,使得收货地址更加精确,类型为String,例如“鼓楼区”“福田区”等,有助于快递员更准确地找到具体的收货地点,提高配送效率,减少配送失误,同样是向用户展示完整收货地址时不可或缺的部分,方便用户明确更具体的收货区域。 + */ private String receiverDistrict; + /** + * 收件人的详细地址信息,除了上述省、市、区等信息外,具体到街道、门牌号、具体楼层房间号等详细内容,类型为String,是快递员最终能够准确将商品送达收件人手中的关键信息,例如“XX路XX号XX小区X栋X单元X室”,完整地描述了收货的具体位置,在收货地址填写以及展示时是最为核心的内容,确保商品能准确送达指定地点。 + */ private String receiverAddress; + /** + * 收件人所在地的邮政编码,虽然在现在的物流配送中使用频率相对没那么高,但在一些传统的邮政业务或者部分需要精确地址匹配的场景下可能会用到,类型为String,同样是收货地址信息的一部分,辅助完善收货地址的整体描述,在一些特定的地址验证或者邮政相关操作中可能会涉及到该信息。 + */ private String receiverZip; -} +} \ No newline at end of file diff --git a/snailmall-product-service/pom.xml b/snailmall-product-service/pom.xml index e3c6cba..46af888 100644 --- a/snailmall-product-service/pom.xml +++ b/snailmall-product-service/pom.xml @@ -122,6 +122,10 @@ commons-net commons-net + + org.springframework + spring-context + diff --git a/snailmall-product-service/src/main/java/com/njupt/swg/service/FileServiceImpl.java b/snailmall-product-service/src/main/java/com/njupt/swg/service/FileServiceImpl.java index db9681a..e3d6bbb 100644 --- a/snailmall-product-service/src/main/java/com/njupt/swg/service/FileServiceImpl.java +++ b/snailmall-product-service/src/main/java/com/njupt/swg/service/FileServiceImpl.java @@ -14,47 +14,71 @@ import java.util.UUID; * @Author swg. * @Date 2019/1/3 19:13 * @CONTACT 317758022@qq.com - * @DESC + * @DESC 该类实现了IFileService接口,主要用于处理文件上传相关的业务逻辑, + * 包括将上传的文件保存到本地临时目录,再上传到FTP服务器,最后删除本地临时文件等操作。 */ @Service @Slf4j -public class FileServiceImpl implements IFileService{ +public class FileServiceImpl implements IFileService { + /** + * 实现文件上传的方法,接收一个MultipartFile类型的文件对象和一个文件存储路径参数。 + * + * @param file 要上传的文件对象,由前端传入的文件数据封装而成。 + * @param path 文件存储的路径,指定了文件应该存储在本地的哪个目录下,后续也会基于此路径操作FTP上传等。 + * @return 返回上传到FTP服务器后的文件名,如果上传过程出现异常则返回null。 + */ @Override public String upload(MultipartFile file, String path) { + // 获取上传文件的原始文件名,例如:abc.jpg String fileName = file.getOriginalFilename(); - //扩展名 - //abc.jpg - String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1); - String uploadFileName = UUID.randomUUID().toString()+"."+fileExtensionName; - log.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName); + // 提取文件的扩展名,即从文件名中获取最后一个点(.)之后的部分,比如对于abc.jpg,获取到的是jpg。 + // 作用是后续生成新文件名时用于保持文件类型一致。 + // abc.jpg + String fileExtensionName = fileName.substring(fileName.lastIndexOf(".") + 1); + // 使用UUID(通用唯一识别码)生成一个唯一的字符串,并拼接上文件扩展名,形成新的文件名。 + // 这样可以保证每次上传的文件名都是唯一的,避免文件名冲突问题。 + String uploadFileName = UUID.randomUUID().toString() + "." + fileExtensionName; + log.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}", fileName, path, uploadFileName); + + // 根据传入的路径创建一个File对象,代表本地的文件目录。 File fileDir = new File(path); - if(!fileDir.exists()){ + // 判断该目录是否存在,如果不存在则进行创建目录操作。 + // 先设置目录可写权限(有些系统可能需要显式设置),然后创建多层级目录(mkdirs会创建所有不存在的父目录)。 + if (!fileDir.exists()) { fileDir.setWritable(true); fileDir.mkdirs(); } - log.info("【文件上传路径为:{}】",fileDir); - File targetFile = new File(path,uploadFileName); + log.info("【文件上传路径为:{}】", fileDir); + + // 创建一个代表目标文件的File对象,其路径是由前面传入的path和新生成的文件名组成, + // 这个文件就是即将要把上传的文件保存到本地的目标文件。 + File targetFile = new File(path, uploadFileName); try { + // 将上传的MultipartFile文件内容传输并保存到本地的目标文件中,完成本地临时文件的保存。 file.transferTo(targetFile); - //文件已经上传成功了 + // 文件已经上传成功保存到本地临时服务器了,记录日志信息。 log.info("【文件上传本地服务器成功】"); - + // 调用FtpUtil工具类的uploadFile方法,将本地保存好的文件(这里包装成一个包含该文件的列表)上传到FTP服务器上。 + // 具体FtpUtil类内部如何实现FTP上传逻辑这里暂不详细展示,但它完成了向FTP服务器传输文件的功能。 FtpUtil.uploadFile(Lists.newArrayList(targetFile)); - //已经上传到ftp服务器上 + // 文件已经成功上传到FTP文件服务器了,记录日志信息。 log.info("【文件上传到文件服务器成功】"); + // 删除本地已经上传到FTP服务器的临时文件,释放本地磁盘空间,因为已经不需要在本地保留该文件副本了。 targetFile.delete(); log.info("【删除本地文件】"); } catch (IOException e) { - log.error("上传文件异常",e); + // 如果在文件上传过程(包括保存到本地、上传到FTP服务器等操作涉及到的IO操作)出现异常, + // 则记录错误日志信息,并返回null表示上传失败。 + log.error("上传文件异常", e); return null; } - //A:abc.jpg - //B:abc.jpg + // 返回上传到FTP服务器后的文件名,这个文件名可能在后续业务中用于记录、关联等操作。 + // 例如,在数据库中记录该文件在FTP服务器上的存储名称等情况。 return targetFile.getName(); } -} +} \ No newline at end of file diff --git a/snailmall-product-service/src/main/java/com/njupt/swg/service/IFileService.java b/snailmall-product-service/src/main/java/com/njupt/swg/service/IFileService.java index 17e707c..b113fa3 100644 --- a/snailmall-product-service/src/main/java/com/njupt/swg/service/IFileService.java +++ b/snailmall-product-service/src/main/java/com/njupt/swg/service/IFileService.java @@ -6,8 +6,20 @@ import org.springframework.web.multipart.MultipartFile; * @Author swg. * @Date 2019/1/3 19:12 * @CONTACT 317758022@qq.com - * @DESC + * @DESC 该接口定义了文件服务相关的操作规范,用于规定实现类需要实现的文件处理方法, + * 目前主要定义了文件上传相关的抽象方法,旨在提供一种统一的文件服务操作接口, + * 方便不同的实现类根据具体业务需求来实现相应逻辑,例如可以有基于不同存储方式(本地、云存储等)的实现。 */ public interface IFileService { + /** + * 定义文件上传的抽象方法,规定了实现类需要遵循的文件上传的基本规范。 + * + * @param file 要上传的文件对象,通过Spring的MultipartFile类型接收,它能够处理来自前端表单提交的文件数据, + * 可以获取文件的各种属性(如文件名、文件内容等)。 + * @param path 文件上传的目标路径,用于指定文件应该存储到哪个具体的位置,具体的实现类可以根据业务需求来解析和使用这个路径, + * 比如可以是本地文件系统的某个目录路径,或者是云存储对应的某个存储桶下的路径等。 + * @return 返回一个字符串,一般用于表示上传成功后的相关标识,例如上传后的文件名或者文件在存储系统中的唯一标识符等, + * 具体返回值的含义由具体实现类根据业务逻辑来确定,若上传失败,实现类应该按照相应规则返回合适的表示失败的值(如null等)。 + */ String upload(MultipartFile file, String path); -} +} \ No newline at end of file diff --git a/snailmall-product-service/src/main/java/com/njupt/swg/service/ProductServiceImpl.java b/snailmall-product-service/src/main/java/com/njupt/swg/service/ProductServiceImpl.java index 57d1713..6147fc5 100644 --- a/snailmall-product-service/src/main/java/com/njupt/swg/service/ProductServiceImpl.java +++ b/snailmall-product-service/src/main/java/com/njupt/swg/service/ProductServiceImpl.java @@ -32,7 +32,7 @@ import java.util.List; */ @Service @Slf4j -public class ProductServiceImpl implements IProductService{ +public class ProductServiceImpl implements IProductService { @Autowired private ProductMapper productMapper; @Autowired @@ -44,10 +44,10 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse list(int pageNum, int pageSize) { //1.pagehelper对下一行取出的集合进行分页 - PageHelper.startPage(pageNum,pageSize); + PageHelper.startPage(pageNum, pageSize); List productList = productMapper.selectList(); List productListVoList = Lists.newArrayList(); - for(Product product:productList){ + for (Product product : productList) { ProductListVo productListVo = assembleProductListVo(product); productListVoList.add(productListVo); } @@ -60,15 +60,15 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse search(String productName, Integer productId, int pageNum, int pageSize) { //开始准备分页 - PageHelper.startPage(pageNum,pageSize); + PageHelper.startPage(pageNum, pageSize); //如果有内容,可以先在这里封装好,直接传到sql中去 - if(StringUtils.isNotBlank(productName)){ + if (StringUtils.isNotBlank(productName)) { productName = new StringBuilder().append("%").append(productName).append("%").toString(); } - List productList = productMapper.selectProductByNameAndId(productName,productId); + List productList = productMapper.selectProductByNameAndId(productName, productId); //转换一下传给前端显示 List productListVoList = Lists.newArrayList(); - for(Product product:productList){ + for (Product product : productList) { ProductListVo productListVo = assembleProductListVo(product); productListVoList.add(productListVo); } @@ -80,12 +80,12 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse detail(Integer productId) { //1,校验参数 - if(productId == null){ + if (productId == null) { throw new SnailmallException("参数不正确"); } //1-在售 2-下架 3-删除 Product product = productMapper.selectByPrimaryKey(productId); - if(product == null){ + if (product == null) { return ServerResponse.createByErrorMessage("商品不存在"); } ProductDetailVo productDetailVo = assembleProductDetailVo(product); @@ -95,7 +95,7 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse set_sale_status(Integer productId, Integer status) { //1.校验参数 - if(productId == null || status == null){ + if (productId == null || status == null) { return ServerResponse.createByErrorMessage("参数不正确"); } //2.更新状态 @@ -105,8 +105,8 @@ public class ProductServiceImpl implements IProductService{ //3.删除该商品缓存 commonCacheUtil.delKey(Constants.PRODUCT_TOKEN_PREFIX); int rowCount = productMapper.updateByPrimaryKeySelective(product); - if(rowCount > 0){ - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+productId,JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); + if (rowCount > 0) { + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + productId, JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); return ServerResponse.createBySuccessMessage("更新产品状态成功"); } return ServerResponse.createByErrorMessage("更新产品状态失败"); @@ -115,33 +115,33 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse saveOrUpdateProduct(Product product) { //1.校验参数 - if(product == null || product.getCategoryId()==null){ + if (product == null || product.getCategoryId() == null) { return ServerResponse.createByErrorMessage("参数不正确"); } //2.设置一下主图,主图为子图的第一个图 - if(StringUtils.isNotBlank(product.getSubImages())){ + if (StringUtils.isNotBlank(product.getSubImages())) { String[] subImages = product.getSubImages().split(","); - if(subImages.length > 0){ + if (subImages.length > 0) { product.setMainImage(subImages[0]); } } //3.看前端传过来的产品id是否存在,存在则为更新,否则为新增 - if(product.getId() != null){ + if (product.getId() != null) { //删除该商品缓存 commonCacheUtil.delKey(Constants.PRODUCT_TOKEN_PREFIX); int rowCount = productMapper.updateByPrimaryKeySelective(product); - if(rowCount > 0){ - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+product.getId(),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); + if (rowCount > 0) { + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + product.getId(), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); return ServerResponse.createBySuccessMessage("更新产品成功"); } return ServerResponse.createByErrorMessage("更新产品失败"); - }else { + } else { //新增 product.setCreateTime(new Date());//这两句可能多余,因为xml中已经保证了,就先放这里 product.setUpdateTime(new Date()); int rowCount = productMapper.insert(product); - if(rowCount > 0){ - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+product.getId(),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); + if (rowCount > 0) { + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + product.getId(), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); return ServerResponse.createBySuccessMessage("新增产品成功"); } return ServerResponse.createByErrorMessage("新增产品失败"); @@ -149,37 +149,62 @@ public class ProductServiceImpl implements IProductService{ } - + /** + * 实现前台门户获取产品详细信息的业务方法,用于根据传入的产品ID查询并返回该产品的详细信息给前台展示, + * 在查询过程中会进行参数校验、产品是否存在校验以及产品状态校验等操作,只有满足条件的产品才会将其详细信息封装后返回。 + * + * @param productId 产品的唯一标识符,用于从数据库中精确查询对应的产品详细信息,不能为空,若为空则直接返回参数错误提示信息。 + * @return 返回一个ServerResponse对象,ServerResponse用于封装响应的整体情况(成功、失败等), + * 内部泛型ProductDetailVo用于承载产品的详细信息(如名称、价格、库存、状态等),若产品存在且状态正常则包含具体的产品详情数据,若出现参数错误、产品不存在或已下架等情况则包含相应的错误提示信息。 + */ @Override public ServerResponse getPortalProductDetail(Integer productId) { - if(productId == null){ + // 首先进行参数校验,判断传入的产品ID是否为null,如果是null,说明传入的参数不正确,不符合获取产品详情的基本要求, + // 此时直接返回一个ServerResponse对象,其内部封装了表示失败的错误消息“参数不正确”,告知前台调用方参数存在问题。 + if (productId == null) { return ServerResponse.createByErrorMessage("参数不正确"); } + + // 根据传入的产品ID,通过ProductMapper调用数据库查询方法,从数据库中获取对应的产品记录,这里尝试获取产品的完整信息,以便后续进行更多判断和封装返回详情数据。 Product product = productMapper.selectByPrimaryKey(productId); - if(product == null){ + + // 判断从数据库中查询到的产品是否为null,如果是null,说明该产品不存在,此时返回一个ServerResponse对象, + // 其内部封装了表示失败的错误消息“商品不存在”,告知前台调用方所请求查看详情的商品在系统中不存在。 + if (product == null) { return ServerResponse.createByErrorMessage("商品不存在"); } - if(product.getStatus() != Constants.Product.PRODUCT_ON){ + + // 进一步校验产品的状态,判断产品的状态是否不等于Constants.Product.PRODUCT_ON(这里Constants.Product.PRODUCT_ON应该是表示产品处于在售状态的一个常量定义), + // 如果产品状态不符合在售条件,说明产品已下架或被删除等情况,不适合展示详情给前台,此时返回一个ServerResponse对象, + // 其内部封装了表示失败的错误消息“产品已下架或删除”,告知前台调用方该产品当前不可查看详情。 + if (product.getStatus()!= Constants.Product.PRODUCT_ON) { return ServerResponse.createByErrorMessage("产品已下架或删除"); } + + // 如果前面的参数校验、产品存在校验以及产品状态校验都通过,说明该产品符合要求,可以获取并封装其详细信息返回给前台。 + // 通过调用assembleProductDetailVo方法,将从数据库查询到的Product对象转换为ProductDetailVo对象,ProductDetailVo对象包含了更适合前台展示的产品详细信息结构和内容。 ProductDetailVo productDetailVo = assembleProductDetailVo(product); + + // 最后返回一个ServerResponse对象,其内部封装了表示成功的状态以及包含产品详细信息的ProductDetailVo对象,告知前台调用方已成功获取到产品详情并返回相应数据,供前台进行展示等操作。 return ServerResponse.createBySuccess(productDetailVo); } + + @Override public ServerResponse portalList(String keyword, Integer categoryId, String orderBy, int pageNum, int pageSize) { //准备盛放categoryIds List categoryIdList = Lists.newArrayList(); //如果categoryId不为空 - if(categoryId != null){ + if (categoryId != null) { //对于这里,直接强转出错了,所以我就序列化处理了一下 ServerResponse response = categoryClient.getCategoryDetail(categoryId); Object object = response.getData(); String objStr = JsonUtil.obj2String(object); - Category category = JsonUtil.Str2Obj(objStr,Category.class); - if(category == null && StringUtils.isBlank(keyword)){ + Category category = JsonUtil.Str2Obj(objStr, Category.class); + if (category == null && StringUtils.isBlank(keyword)) { ////直接返回空 - PageHelper.startPage(pageNum,pageSize); + PageHelper.startPage(pageNum, pageSize); List productListVoList = Lists.newArrayList(); PageInfo pageInfo = new PageInfo(productListVoList); return ServerResponse.createBySuccess(pageInfo); @@ -188,24 +213,24 @@ public class ProductServiceImpl implements IProductService{ categoryIdList = (List) categoryClient.getDeepCategory(categoryId).getData(); } //如果keyword不为空 - if(StringUtils.isNotBlank(keyword)){ + if (StringUtils.isNotBlank(keyword)) { keyword = new StringBuilder().append("%").append(keyword).append("%").toString(); } //如果orderBy不为空 - if(StringUtils.isNotBlank(orderBy)){ - if(Constants.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)){ + if (StringUtils.isNotBlank(orderBy)) { + if (Constants.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)) { String[] orderByArray = orderBy.split("_"); //特定的格式 - PageHelper.orderBy(orderByArray[0]+" "+orderByArray[1]); + PageHelper.orderBy(orderByArray[0] + " " + orderByArray[1]); } } - PageHelper.startPage(pageNum,pageSize); + PageHelper.startPage(pageNum, pageSize); //模糊查询 - List productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)?null:keyword, - categoryIdList.size()==0?null:categoryIdList); + List productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword) ? null : keyword, + categoryIdList.size() == 0 ? null : categoryIdList); //封装返回对象 List productListVoList = Lists.newArrayList(); - for(Product product : productList){ + for (Product product : productList) { ProductListVo productListVo = assembleProductListVo(product); productListVoList.add(productListVo); } @@ -218,96 +243,165 @@ public class ProductServiceImpl implements IProductService{ @Override public ServerResponse queryProduct(Integer productId) { //1.校验参数 - if(productId == null){ + if (productId == null) { return ServerResponse.createByErrorMessage("参数错误"); } //2.去redis中查询,没有则把商品重新添加进redis中 - String redisProductStr = commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX+productId); - if (redisProductStr == null){ + String redisProductStr = commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX + productId); + if (redisProductStr == null) { Product product = productMapper.selectByPrimaryKey(productId); - if(product == null){ + if (product == null) { return ServerResponse.createByErrorMessage("商品不存在"); } - if(product.getStatus() != Constants.Product.PRODUCT_ON){ + if (product.getStatus() != Constants.Product.PRODUCT_ON) { return ServerResponse.createByErrorMessage("商品已经下架或者删除"); } - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+productId,JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + productId, JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); } //2.获取商品 - Product product = JsonUtil.Str2Obj(commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX+productId),Product.class); + Product product = JsonUtil.Str2Obj(commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX + productId), Product.class); return ServerResponse.createBySuccess(product); } + /** + * 该私有方法用于将Product对象转换为ProductListVo对象,主要提取产品的部分关键属性进行封装, + * 这些属性是在展示产品列表时所需要的信息,方便后续将整理好的数据传递给前端进行展示。 + * + * @param product 传入的Product对象,包含了产品的完整信息,从数据库查询等途径获取到的原始产品数据对象。 + * @return 返回一个ProductListVo对象,该对象封装了产品在列表展示场景下所需要展示的相关属性信息。 + */ private ProductListVo assembleProductListVo(Product product) { + // 创建一个ProductListVo对象,用于存放要返回的产品列表展示相关信息,该对象的属性会被逐步设置为从传入的Product对象中提取的对应属性值。 ProductListVo productListVo = new ProductListVo(); + // 将传入Product对象的ID属性值设置到ProductListVo对象中,这个ID用于唯一标识产品,方便在前端进行相关操作(如点击查看详情等)时能准确关联到对应的产品记录。 productListVo.setId(product.getId()); + // 将传入Product对象的副标题属性值设置到ProductListVo对象中,副标题可以提供产品的一些补充说明信息,辅助用户更好地了解产品特点等情况,在列表中展示给用户。 productListVo.setSubtitle(product.getSubtitle()); + // 将传入Product对象的主图片路径或URL属性值设置到ProductListVo对象中,用于在前端列表页面展示产品的主要外观形象,让用户直观看到产品样子。 productListVo.setMainImage(product.getMainImage()); + // 将传入Product对象的价格属性值设置到ProductListVo对象中,价格是用户关注的重要信息之一,展示给用户了解产品的售卖价格情况。 productListVo.setPrice(product.getPrice()); + // 将传入Product对象的所属类别ID属性值设置到ProductListVo对象中,通过这个类别ID可以关联到对应的产品分类信息,便于进行分类筛选、查找同类别下的其他产品等操作,在列表展示中体现产品的分类归属。 productListVo.setCategoryId(product.getCategoryId()); + // 将传入Product对象的名称属性值设置到ProductListVo对象中,产品名称是最直观展示给用户用于区分不同产品的关键信息,在列表中突出显示。 productListVo.setName(product.getName()); + // 将传入Product对象的状态属性值设置到ProductListVo对象中,产品状态可以表示产品当前是上架(可售卖)、下架(不可售卖)等不同情况,方便用户了解产品的可购买性等状态信息,在列表中展示。 productListVo.setStatus(product.getStatus()); - productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.snail.com/")); + // 通过PropertiesUtil工具类获取图片服务器的HTTP前缀配置信息,如果配置不存在,则使用默认值"http://image.snail.com/", + // 并将该前缀设置到ProductListVo对象的imageHost属性中,这个属性与产品的图片路径(如mainImage等属性)配合使用, + // 用于在前端正确拼接出完整的图片访问URL,使得图片能够正常展示出来。 + productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix", "http://image.snail.com/")); return productListVo; } - private ProductDetailVo assembleProductDetailVo(Product product){ + /** + * 该私有方法用于将Product对象转换为ProductDetailVo对象,提取产品的详细属性信息进行封装, + * 这些属性涵盖了产品的各个方面,是在展示产品详细信息时所需要传递给前端的数据,同时还会额外获取产品所属品类的父品类ID信息(通过与品类服务交互)。 + * + * @param product 传入的Product对象,包含了产品的完整信息,从数据库查询等途径获取到的原始产品数据对象。 + * @return 返回一个ProductDetailVo对象,该对象封装了产品在详情展示场景下所需要展示的丰富的属性信息,包括关联获取的父品类ID等。 + */ + private ProductDetailVo assembleProductDetailVo(Product product) { + // 创建一个ProductDetailVo对象,用于存放要返回的产品详细信息,该对象的属性会被逐步设置为从传入的Product对象中提取的对应属性值以及通过其他方式获取的相关信息。 ProductDetailVo productDetailVo = new ProductDetailVo(); + // 将传入Product对象的ID属性值设置到ProductDetailVo对象中,用于唯一标识产品,方便前端和后端进行与该产品相关的各种操作时准确关联到对应的产品记录。 productDetailVo.setId(product.getId()); + // 将传入Product对象的副标题属性值设置到ProductDetailVo对象中,副标题可以提供产品的一些补充说明信息,辅助用户更好地了解产品特点等情况,在详情展示中展示给用户。 productDetailVo.setSubtitle(product.getSubtitle()); + // 将传入Product对象的主图片路径或URL属性值设置到ProductDetailVo对象中,用于在前端详情页面展示产品的主要外观形象,让用户直观看到产品样子。 productDetailVo.setMainImage(product.getMainImage()); + // 将传入Product对象的子图片路径或URL集合(通常以某种格式拼接存储)属性值设置到ProductDetailVo对象中,用于展示产品的其他相关图片,从不同角度或者展示不同细节等,丰富用户对产品外观等方面的了解,在详情展示中体现。 productDetailVo.setSubImages(product.getSubImages()); + // 将传入Product对象的价格属性值设置到ProductDetailVo对象中,价格是用户关注的重要信息之一,展示给用户了解产品的售卖价格情况。 productDetailVo.setPrice(product.getPrice()); + // 将传入Product对象的所属类别ID属性值设置到ProductDetailVo对象中,通过这个类别ID可以关联到对应的产品分类信息,便于进行分类相关的综合查询、导航等操作,在详情展示中体现产品的分类归属。 productDetailVo.setCategoryId(product.getCategoryId()); + // 将传入Product对象的详细描述信息属性值设置到ProductDetailVo对象中,产品的详细描述包含了产品的功能、参数、使用方法、注意事项等详细内容,用于用户全面深入地了解产品的各种特性,在详情展示中完整呈现给用户。 productDetailVo.setDetail(product.getDetail()); + // 将传入Product对象的名称属性值设置到ProductDetailVo对象中,产品名称是最直观展示给用户用于区分不同产品的关键信息,在详情展示中突出显示。 productDetailVo.setName(product.getName()); + // 将传入Product对象的状态属性值设置到ProductDetailVo对象中,产品状态可以表示产品当前是上架(可售卖)、下架(不可售卖)等不同情况,方便用户了解产品的可购买性等状态信息,在详情展示中展示。 productDetailVo.setStatus(product.getStatus()); + // 将传入Product对象的库存数量属性值设置到ProductDetailVo对象中,库存数量用于记录当前产品还有多少可售卖的数量,用户可以据此了解产品的供货情况,在详情展示中展示给用户。 productDetailVo.setStock(product.getStock()); + // 通过PropertiesUtil工具类获取图片服务器的HTTP前缀配置信息,如果配置不存在,则使用默认值"http://image.snail.com/", + // 并将该前缀设置到ProductDetailVo对象的imageHost属性中,这个属性与产品的图片路径(如mainImage、subImages等属性)配合使用, + // 用于在前端正确拼接出完整的图片访问URL,使得图片能够正常展示出来。 + productDetailVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix", "http://image.snail.com/")); - productDetailVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.snail.com/")); - //返回给前端还需要一下该商品所处品类的父品类id,所以需要去品类服务中去查询一下,这里就要用到Feign - if(categoryClient.getCategoryDetail(product.getCategoryId()).isSuccess()){ + // 返回给前端还需要产品所处品类的父品类ID信息,所以需要通过品类客户端(categoryClient,通常基于Feign等方式实现与其他服务通信)去品类服务中查询该信息。 + if (categoryClient.getCategoryDetail(product.getCategoryId()).isSuccess()) { + // 如果获取品类详情操作成功,获取返回的ServerResponse对象,该对象封装了服务端返回的响应信息,包含了操作是否成功以及具体的数据内容等。 ServerResponse response = categoryClient.getCategoryDetail(product.getCategoryId()); + // 从ServerResponse对象中获取其携带的数据内容,这个数据应该是与产品所属品类相关的信息,具体类型需要后续转换处理。 Object object = response.getData(); + // 将获取到的对象数据转换为JSON字符串形式,方便后续进行JSON反序列化操作,将其还原为对应的Java对象。 String objStr = JsonUtil.obj2String(object); - Category category = (Category) JsonUtil.Str2Obj(objStr,Category.class); + // 通过JsonUtil工具类将JSON字符串反序列化为Category对象,该对象包含了产品所属品类的详细信息,如名称、父ID等属性。 + Category category = (Category) JsonUtil.Str2Obj(objStr, Category.class); + // 将获取到的品类对象的父ID属性值设置到ProductDetailVo对象的parentCategoryId属性中,这样前端就能获取到产品所属品类的父品类ID信息了。 productDetailVo.setParentCategoryId(category.getParentId()); - }else { + } else { + // 如果获取品类详情操作失败,设置ProductDetailVo对象的parentCategoryId属性为默认值0,表示无法获取到有效的父品类ID信息。 productDetailVo.setParentCategoryId(0); } + // 将传入Product对象的创建时间属性值转换为字符串格式,并设置到ProductDetailVo对象的createTime属性中,创建时间记录产品信息首次被录入系统的时间,以合适的格式展示给前端用户查看产品的历史信息等情况。 productDetailVo.setCreateTime(DateTimeUtil.dateToStr(product.getCreateTime())); + // 将传入Product对象的更新时间属性值转换为字符串格式,并设置到ProductDetailVo对象的updateTime属性中,更新时间记录产品信息最后一次被修改的时间,同样以合适的格式展示给前端用户了解产品信息的更新情况。 productDetailVo.setUpdateTime(DateTimeUtil.dateToStr(product.getUpdateTime())); return productDetailVo; } - - + /** + * 实现将产品库存信息预置到Redis缓存中的业务方法,通过查询所有产品列表,筛选出状态为在售(Constants.Product.PRODUCT_ON)的产品, + * 将其产品ID和库存数量以特定的键值对形式缓存到Redis中,缓存设置了过期时间,最后返回表示预置库存成功的ServerResponse对象给调用方。 + * + * @return 返回一个ServerResponse对象,其内部封装了成功状态以及包含“预置库存成功”提示消息的字符串内容,表示产品库存信息已成功预置到Redis缓存中。 + */ @Override public ServerResponse preInitProductStcokToRedis() { + // 通过ProductMapper查询获取所有产品的列表信息,返回一个包含所有产品记录的Product类型的List集合。 List productList = productMapper.selectList(); - for(Product product:productList){ + // 遍历查询到的每个产品记录,进行如下操作: + for (Product product : productList) { + // 获取产品的唯一标识符(ID),用于在缓存中作为键的一部分,以便准确区分不同产品的库存信息缓存。 Integer productId = product.getId(); + // 获取产品的库存数量,用于缓存到Redis中,方便后续业务逻辑中快速获取产品库存情况。 Integer stock = product.getStock(); - if(productId != null && stock != null && product.getStatus().equals(Constants.Product.PRODUCT_ON)){ - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_STOCK_PREFIX+String.valueOf(productId),String.valueOf(stock),Constants.PRODUCT_EXPIRE_TIME); + // 判断产品ID、库存数量是否不为null,并且产品状态是否为在售(Constants.Product.PRODUCT_ON),如果满足这些条件,则进行缓存操作。 + if (productId != null && stock != null && product.getStatus().equals(Constants.Product.PRODUCT_ON)) { + // 通过CommonCacheUtil工具类将产品库存信息缓存到Redis中,设置缓存的键(以Constants.PRODUCT_TOKEN_STOCK_PREFIX + 产品ID的字符串形式为键)、 + // 值(库存数量转换为字符串形式)以及过期时间(Constants.PRODUCT_EXPIRE_TIME),这样后续可以通过该键快速获取产品的库存信息,并且缓存数据会在过期时间后自动失效,保证数据的时效性。 + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_STOCK_PREFIX + String.valueOf(productId), String.valueOf(stock), Constants.PRODUCT_EXPIRE_TIME); } } return ServerResponse.createBySuccessMessage("预置库存成功"); } + /** + * 实现将产品信息预置到Redis缓存中的业务方法,通过查询所有产品列表,筛选出状态为在售(Constants.Product.PRODUCT_ON)的产品, + * 将其产品信息以JSON字符串形式缓存到Redis中,缓存设置了过期时间,最后返回表示预置商品信息成功的ServerResponse对象给调用方。 + * + * @return 返回一个ServerResponse对象,其内部封装了成功状态以及包含“预置商品信息成功”提示消息的字符串内容,表示产品信息已成功预置到Redis缓存中。 + */ @Override public ServerResponse preInitProductListToRedis() { + // 通过ProductMapper查询获取所有产品的列表信息,返回一个包含所有产品记录的Product类型的List集合。 List productList = productMapper.selectList(); - for(Product product:productList){ + // 遍历查询到的每个产品记录,进行如下操作: + for (Product product : productList) { + // 获取产品的唯一标识符(ID),用于在缓存中作为键的一部分,以便准确区分不同产品的信息缓存。 Integer productId = product.getId(); - if(productId != null && product.getStatus().equals(Constants.Product.PRODUCT_ON)){ - commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+String.valueOf(productId),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); + // 判断产品ID是否不为null,并且产品状态是否为在售(Constants.Product.PRODUCT_ON),如果满足这些条件,则进行缓存操作。 + if (productId != null && product.getStatus().equals(Constants.Product.PRODUCT_ON)) { + // 通过CommonCacheUtil工具类将产品信息缓存到Redis中,设置缓存的键(以Constants.PRODUCT_TOKEN_PREFIX + 产品ID的字符串形式为键)、 + // 值(将产品对象转换为JSON字符串形式,通过JsonUtil工具类进行转换)以及过期时间(Constants.PRODUCT_EXPIRE_TIME), + // 这样后续可以通过该键快速获取产品的详细信息,并且缓存数据会在过期时间后自动失效,保证数据的时效性。 + commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + String.valueOf(productId), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); } } return ServerResponse.createBySuccessMessage("预置商品信息成功"); } - - -} +} \ No newline at end of file diff --git a/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductDetailVo.java b/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductDetailVo.java index bf3f54d..c6d7317 100644 --- a/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductDetailVo.java +++ b/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductDetailVo.java @@ -1,30 +1,87 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; /** * @Author 【swg】. * @Date 2018/1/11 12:34 - * @DESC + * @DESC 该类是一个值对象(Value Object,简称 VO),用于在不同层(如表现层与业务层之间)传递产品详细信息。 + * 它将产品相关的多个属性整合在一起,方便进行数据的传递和展示等操作,避免了在不同方法调用中传递多个零散参数的麻烦。 + * 通过使用Lombok的 @Data 注解,自动生成了各属性的Getter、Setter方法以及toString、hashCode、equals等常用方法,简化了代码编写。 * @CONTACT 317758022@qq.com */ @Data public class ProductDetailVo { + /** + * 产品的唯一标识符,用于区分不同的产品,在数据库中通常对应产品表的主键字段。 + */ private Integer id; + + /** + * 产品所属的类别ID,用于标识该产品属于哪个分类,通过这个ID可以关联到对应的产品分类信息,例如查找同类别下的其他产品等。 + */ private Integer categoryId; + + /** + * 产品的名称,是对产品的一个简短称呼,用于展示给用户,方便用户识别和区分不同的产品。 + */ private String name; + + /** + * 产品的副标题,一般是对产品名称的补充说明,用于提供更多关于产品特点、用途等方面的简要描述信息,进一步帮助用户了解产品。 + */ private String subtitle; + + /** + * 产品的主图片路径或URL,用于展示产品的主要外观形象,在前端页面上通常会显示该图片来代表产品,让用户直观看到产品的样子。 + */ private String mainImage; + + /** + * 产品的子图片路径或URL集合(通常以某种格式拼接存储,比如逗号分隔的字符串等形式),用于展示产品的其他相关图片, + * 从不同角度或者展示不同细节等,丰富用户对产品外观等方面的了解。 + */ private String subImages; + + /** + * 产品的详细描述信息,包含了产品的功能、参数、使用方法、注意事项等详细内容,用于用户全面深入地了解产品的各种特性。 + */ private String detail; + + /** + * 产品的价格,使用BigDecimal类型可以精确表示货币数值,避免浮点数运算带来的精度丢失问题,存储产品当前的售卖价格。 + */ private BigDecimal price; + + /** + * 产品的库存数量,用于记录当前产品还有多少可售卖的数量,在进行购买等业务操作时会依据这个数量来判断是否有货等情况。 + */ private Integer stock; + + /** + * 产品的状态,例如可以用不同的整数值来表示产品是上架(可售卖)、下架(不可售卖)等不同状态,方便业务逻辑中对产品状态的判断和处理。 + */ private Integer status; + + /** + * 产品的创建时间,记录产品信息首次被录入系统的时间,一般格式为特定的日期时间格式字符串,可用于查询产品的历史信息、排序等操作。 + */ private String createTime; + + /** + * 产品的更新时间,记录产品信息最后一次被修改的时间,同样为日期时间格式字符串,能体现产品信息的更新情况。 + */ private String updateTime; + /** + * 图片的主机地址(或域名等相关信息),与产品的图片路径(如mainImage、subImages等属性)配合使用, + * 用于在前端正确拼接出完整的图片访问URL,使得图片能够正常展示出来。 + */ private String imageHost; + + /** + * 产品所属父类别的ID,在分类有层级结构时,用于定位产品所属的更上层分类,有助于进行分类相关的综合查询、导航等操作。 + */ private Integer parentCategoryId; -} +} \ No newline at end of file diff --git a/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductListVo.java b/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductListVo.java index 08a0408..c900836 100644 --- a/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductListVo.java +++ b/snailmall-product-service/src/main/java/com/njupt/swg/vo/ProductListVo.java @@ -1,30 +1,58 @@ package com.njupt.swg.vo; import lombok.Data; - import java.math.BigDecimal; /** * @Author swg. * @Date 2019/1/3 12:06 * @CONTACT 317758022@qq.com - * @DESC 展示商品列表时只需要以下的属性即可 + * @DESC 该类是一个值对象(Value Object,简称VO),用于在展示商品列表场景下传递商品的相关信息。 + * 相比于完整的商品详细信息,在列表展示时只需要展示部分关键属性即可,所以定义了该类来封装这些必要属性, + * 方便在不同的层(例如业务层向表现层传递数据)之间进行数据的传递与展示操作,同时借助Lombok的 @Data 注解, + * 自动生成了各属性的Getter、Setter方法以及像toString、hashCode、equals等常用方法,简化了代码编写。 */ @Data public class ProductListVo { + /** + * 商品的唯一标识符,用于在系统中准确区分不同的商品,通常对应数据库中商品表的主键字段,方便基于此ID进行各类与该商品相关的操作。 + */ private Integer id; + /** + * 商品所属的类别ID,通过这个ID可以关联到对应的商品分类信息,便于进行分类筛选、查找同类别下的其他商品等操作, + * 体现了商品在分类体系中的所属关系。 + */ private Integer categoryId; + /** + * 商品的名称,是对商品的一个简洁称呼,主要用于在列表中直观地展示给用户,帮助用户快速识别不同的商品。 + */ private String name; + /** + * 商品的副标题,一般作为商品名称的补充说明,提供一些额外的、简要的商品特点、用途等描述信息,辅助用户进一步了解商品特性。 + */ private String subtitle; + /** + * 商品的主图片路径或URL,用于在列表中展示商品的主要外观形象,让用户能够直观看到商品大致样子,增强视觉展示效果。 + */ private String mainImage; + /** + * 商品的价格,采用BigDecimal类型能够精确地表示货币数值,避免了浮点数运算可能带来的精度丢失问题,这里存储的是商品当前的售卖价格。 + */ private BigDecimal price; + /** + * 商品的状态,通常可以用不同的整数值来表示商品处于上架(可售卖)、下架(不可售卖)等不同状态,方便在业务逻辑中判断商品是否可进行交易等操作。 + */ private Integer status; + /** + * 图片的主机地址(或域名等相关信息),与商品的主图片路径(mainImage属性)配合使用, + * 用于在前端正确拼接出完整的图片访问URL,确保商品图片能够在列表展示页面中正常显示出来。 + */ private String imageHost; -} +} \ No newline at end of file