Compare commits

...

1 Commits

Author SHA1 Message Date
p5fjacpqg d6d54e571b p4
9 months ago

@ -128,6 +128,16 @@
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@ -148,4 +158,5 @@
</plugins>
</build>
</project>

@ -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
* RedisRedis0
*
* - keyRedis
* - valueRedis
*/
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
*
* - keyRedis
*
* Redisnull
*/
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
*
* - keyRedis
* - valueRedis
* - expire
*
* SETNX10
*/
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
*
* - keyRedis
*/
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);

@ -13,29 +13,54 @@ import javax.annotation.PostConstruct;
* @Date 2019/1/1 15:00
* @CONTACT 317758022@qq.com
* @DESC redisredishash
* JedisPoolJedisJedis
* Spring@Component便Spring使Slf4j
*/
@Component
@Slf4j
public class JedisPoolWrapper {
@Autowired
// 这里应该是自定义的一个类用于存放Redis相关的配置参数比如最大连接数、主机地址、端口等信息
// 但从当前代码来看没有看到其具体定义假设它有对应的getter方法来获取相关配置值。
private Parameters parameters;
// 用于存放初始化后的JedisPool对象初始值为null代表还未进行初始化操作
private JedisPool jedisPool = null;
/**
* @PostConstruct
* JedisJedisPoolJedisPool
*/
@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);
}
}
/**
* JedisPoolJedisJedisPool
* JedisRedis
*
* JedisPoolinitnull
*/
public JedisPool getJedisPool() {
return jedisPool;
}

@ -9,24 +9,56 @@ import org.springframework.stereotype.Component;
* @Date 2019/1/1 14:27
* @CONTACT 317758022@qq.com
* @DESC
* ParametersRedisCurator
* Spring@ComponentSpring
* 使Lombok@DataGetterSettertoStringLombok便
*/
@Component
@Data
public class Parameters {
// Redis配置相关部分开始
/*****redis config start*******/
/**
* @Value("${redis.host}")Springapplication.propertiesapplication.yml"redis.host"
* redisHostRedis
*/
@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*******/

@ -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"
* FeignHTTP便
*/
@FeignClient("cart-service")
public interface CartClient {
/**
* getCartListFeign"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();

@ -8,9 +8,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
* @Date 2019/1/7 16:35
* @CONTACT 317758022@qq.com
* @DESC
* KeyGenClientSpring Cloud OpenFeign"KEYGEN-SERVICE"
* 使FeignClient
*/
@FeignClient("KEYGEN-SERVICE")
public interface KeyGenClient {
/**
* generateKeyFeign"KEYGEN-SERVICE"HTTP
* "/keygen""/keygen"
* String"KEYGEN-SERVICE"
*
* @return "KEYGEN-SERVICE"
*/
@RequestMapping("/keygen")
String generateKey();
}

@ -10,9 +10,26 @@ import org.springframework.web.bind.annotation.RequestParam;
* @Date 2019/1/6 20:16
* @CONTACT 317758022@qq.com
* @DESC
* ProductClientSpring Cloud OpenFeign"product-service"
* Feign便
*/
@FeignClient("product-service")
public interface ProductClient {
/**
* queryProductFeign"product-service"HTTP
* "/product/queryProduct.do"
*
* @RequestParam("productId")"productId"
* IntegerID"product-service"ID
*
* ServerResponse
* "product-service"
*
* @param productId ID便ID
* @return ServerResponse"product-service"
*/
@RequestMapping("/product/queryProduct.do")
ServerResponse queryProduct(@RequestParam("productId") Integer productId);
}

@ -10,10 +10,26 @@ import org.springframework.web.bind.annotation.RequestParam;
* @Date 2019/1/5 22:01
* @CONTACT 317758022@qq.com
* @DESC
* ShippingClientSpring Cloud OpenFeign"shipping-service"
* 使FeignClient使便
*/
@FeignClient("shipping-service")
public interface ShippingClient {
/**
* getShippingFeign"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);

@ -15,23 +15,41 @@ import javax.servlet.http.HttpServletRequest;
* @Date 2019/1/6 17:05
* @CONTACT 317758022@qq.com
* @DESC
* FeignConfigurationSpringFeign
* 使 @Configuration Spring
* 使 @Slf4j 便Cookie
*/
@Configuration
@Slf4j
public class FeignConfiguration {
/**
* @BeanSpringBeanFeignRequestInterceptor
* FeignFeign
* ServletCookieCookieFeign
* 使
*
* @return RequestInterceptorFeignSpringFeign
*/
@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失败");
}
}

@ -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 010
*/
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_";
}

@ -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)handleExceptionException
*
*
* @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,"系统异常,请稍后再试");
}
/**
* 使@ExceptionHandlerSnailmallException
* SnailmallException
*
* @param e SnailmallException
* @return ServerResponse使SnailmallExceptione.getExceptionStatus()e.getMessage()
*/
@ExceptionHandler(SnailmallException.class)
public ServerResponse handleException(SnailmallException e){
log.error(e.getMessage(),e);

@ -8,17 +8,39 @@ import lombok.Getter;
* @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com
* @DESC
* SnailmallExceptionJavaRuntimeException
* Checked Exception使便
* 使Lombok@GetterLombokexceptionStatusGetter便
*/
@Getter
public class SnailmallException extends RuntimeException{
// 用于存储异常对应的状态码初始值被设置为ResponseEnum.ERROR.getCode()这里推测ResponseEnum是一个枚举类型
// ERROR是其中的一个枚举值通过调用其getCode()方法获取对应的状态码作为默认值,后续可根据不同的构造方法进行修改。
private int exceptionStatus = ResponseEnum.ERROR.getCode();
/**
* msgSnailmallException
* RuntimeExceptionmsg便
* 使ResponseEnum.ERROR.getCode()
*
* @param msg
*/
public SnailmallException(String msg){
super(msg);
}
/**
* codemsg
* RuntimeExceptionmsgcodeexceptionStatus
* 便
*
* @param code 便
* @param msg
*/
public SnailmallException(int code,String msg){
super(msg);
exceptionStatus = code;
}

@ -7,17 +7,45 @@ import lombok.Getter;
* @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com
* @DESC
* ResponseEnum便使
* 使Lombok@GetterLombokcodedescGetter便
*/
@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;
/**
* codedesc
* SUCCESSERROR使
*
* @param code
* @param desc
*/
ResponseEnum(int code,String desc){
this.code = code;
this.desc = desc;

@ -11,32 +11,53 @@ import java.io.Serializable;
* @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com
* @DESC
* ServerResponseSerializable便使
* 使
* 使Lombok@GetterGetter便使JacksonJSON
*/
@Getter
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class ServerResponse<T> 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;
}
/**
* @JsonIgnoreJacksonJSONJavaServerResponse
* this.statusResponseEnum.SUCCESS.getCode()
*
* @return trueServerResponsefalse
*/
@JsonIgnore
public boolean isSuccess(){
return this.status == ResponseEnum.SUCCESS.getCode();
@ -44,16 +65,53 @@ public class ServerResponse<T> implements Serializable {
/**
*
* 便ServerResponse使
*/
/**
* ServerResponse使ResponseEnumResponseEnum.SUCCESS.getCode()ResponseEnum.SUCCESS.getDesc()
* 使ServerResponse
*
* @param <T> <Integer>
* @return ServerResponse
*/
public static <T>ServerResponse<T> createBySuccess(){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc());
}
/**
* ServerResponse使ResponseEnumResponseEnum.SUCCESS.getCode()message
* ServerResponse
*
* @param <T>
* @param message
* @return ServerResponse
*/
public static <T>ServerResponse<T> createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message);
}
/**
* ServerResponse使ResponseEnumResponseEnum.SUCCESS.getCode()data
* ServerResponse使
*
* @param <T>
* @param data
* @return ServerResponse
*/
public static <T>ServerResponse<T> createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data);
}
/**
* ServerResponse使ResponseEnumResponseEnum.SUCCESS.getCode()messagedata
* ServerResponse
*
* @param <T>
* @param message
* @param data
* @return ServerResponse
*/
public static <T>ServerResponse<T> createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data);
}
@ -61,16 +119,40 @@ public class ServerResponse<T> implements Serializable {
/**
*
*/
/**
* ServerResponse使ResponseEnumResponseEnum.ERROR.getCode()ResponseEnum.ERROR.getDesc()
* 使ServerResponse
*
* @param <T>
* @return ServerResponse
*/
public static <T>ServerResponse<T> createByError(){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc());
}
/**
* ServerResponse使ResponseEnumResponseEnum.ERROR.getCode()msg
* ServerResponse
*
* @param <T>
* @param msg
* @return ServerResponse
*/
public static <T>ServerResponse<T> createByErrorMessage(String msg){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
}
/**
* ServerResponse使codemsg
* ServerResponse
*
* @param <T>
* @param code
* @param msg
* @return ServerResponse
*/
public static <T>ServerResponse<T> createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
}
}

@ -7,31 +7,71 @@ import java.math.BigDecimal;
* @Date 2019/1/5 15:20
* @CONTACT 317758022@qq.com
* @DESC BigDecimal
* BigDecimalUtil
* doubleBigDecimal
*
*/
public class BigDecimalUtil {
// 将构造方法私有化这样外部代码就无法通过new关键字来创建BigDecimalUtil类的实例因为这个类的功能都是通过静态方法来提供的不需要实例化对象去调用方法。
private BigDecimalUtil(){
}
/**
* double
* BigDecimalBigDecimaladd
* 使
*
* @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
* BigDecimalBigDecimalsubtract
*
*
* @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
* BigDecimalBigDecimalmultiply
*
*
* @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
* BigDecimalBigDecimaldivide
* 2BigDecimal.ROUND_HALF_UP
*
*
* @param v1
* @param v2
* @return BigDecimal2
*/
public static BigDecimal div(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));

@ -9,40 +9,65 @@ import javax.servlet.http.HttpServletResponse;
/**
* cookie
* CookieUtilCookieCookieCookieCookie
* 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
* writeLoginTokenCookie
* Cookie访HttpServletResponse使Cookie
*
* @param response CookieCookie
* @param token CookieCookie
*/
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
* readLoginTokenHttpServletRequestCookieCOOKIE_NAMECookienull
* CookieCookie
*
* @param request CookieCookie
* @return "snailmall_login_token"CookieCookienull
*/
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
* delLoginTokenCookieCOOKIE_NAMECookie
* CookieCookie0CookieCookie
*
* @param request CookieCookie
* @param response CookieCookie
*/
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;
}

@ -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便DateDate
* 使Joda-TimeJoda-TimeJava便使JavaSimpleDateFormat
*/
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){
/**
* DateformatStr
* Joda-TimeDateTimeFormatter使DateTimeDateTimeJavaDate
*
* @param dateTimeStr "2024-12-10 15:30:00"formatStr
* @param formatStr "yyyy-MM-dd HH:mm:ss" dateTimeStr
* @return DateJoda-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
* DatenullnullStringUtils.EMPTY
* DatenullJoda-TimeDateTimeDateDateTimetoStringformatStrDate
*
* @param date Datenullnull
* @param formatStr "yyyy-MM-dd HH:mm:ss"
* @return datenull
*/
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)使DateTimeDate
*
* @param dateTimeStr STANDARD_FORMAT"yyyy-MM-dd HH:mm:ss"
* @return DateJoda-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"
* DatenullnullnullDateTimedateToStr(Date date, String formatStr)
*
* @param date Datenullnull
* @return "yyyy-MM-dd HH:mm:ss"datenull
*/
public static String dateToStr(Date date) {
if (date == null) {
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
//Date -> 时间戳
/**
* Date19701100:00:00 UTC
* DatenullnullnullnullSimpleDateFormat使"yyyy-MM-dd HH:mm:ss"
* Date使SimpleDateFormatparseDateDategetTime197011
* ParseException
*
* @param date Datenullnullnull
* @return Long19701100:00:00 UTCdatenullnullParseException
* @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
* 使
* ParseExceptionSimpleDateFormatparsemain
*
* @param args 使
* @throws ParseException 使SimpleDateFormatmain
*/
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());
}
}

@ -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
* FtpUtilFTPFTP
* 使Apache Commons NetFTPClientFTP便FTP
* 使Lombok@DataGetterSetter@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){
/**
* FtpUtilFTPIPipportuserpwd
* 便FTP使使
*
* @param ip FTPIP
* @param port FTPFTP21
* @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;
}
/**
* fileListFTP
* FtpUtil使FTPIP使21
* uploadFile
* IOExceptionFTPI/O
*
* @param fileList FTPList<File>FTP
* @return truefalse
* @throws IOException FTPFTPI/O
*/
public static boolean uploadFile(List<File> 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<File> fileList) throws IOException {
/**
* FTPremotePath
* uploadedtrueFileInputStreamFTP
* FTPFTP
* IOExceptionuploadedfalseFTP
*
* @param remotePath FTPFTP "/img" FTPimg
* @param fileList FTP
* @return truefalsefalse
* @throws IOException FTPFTPI/O
*/
private boolean uploadFile(String remotePath, List<File> 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 {
// 无论文件上传是否成功,都需要关闭文件输入流,释放相关的系统资源,避免资源泄漏等问题。
if (fis!= null) {
fis.close();
}
// 断开与FTP服务器的连接释放与FTP服务器连接相关的资源结束本次文件上传操作对应的FTP连接会话。
ftpClient.disconnect();
}
}
return uploaded;
}
private boolean connectServer(String ip,int port,String user,String pwd){
/**
* FTPIPipportuserpwd
* FTPClient使IPFTP使
* IOException便FTP
*
* @param ip FTPIP
* @param port FTPFTP21
* @param user FTP
* @param pwd FTP
* @return FTPtruefalse
*/
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;
}

@ -8,118 +8,161 @@ 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
* JsonUtilJSONJavaJSONJSONJava
* Jackson使CodehausJacksonObjectMapper使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 <T>
* @return
* obj2StringJavaJSON
* nullnullinstanceof使ObjectMapperJSON
* IOExceptionnull
*
* @param obj JSONJavaJavaPOJOJavaMapJackson
* @param <T> JSONJava
* @return JSONJavaJSONnullnull
*/
public static <T> String obj2String(T obj){
if(obj == null){
public static <T> 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 <T>
* @return
* obj2StringPrettyobj2StringJavaJSONJSON便JSON
* nullnull使ObjectMapperwriterWithDefaultPrettyPrinterJSON
* IOExceptionnull
*
* @param obj JSONJavaobj2StringobjJava
* @param <T> JSONJava
* @return JSONJavaJSONnullnullJSON便
*/
public static <T> String obj2StringPretty(T obj){
if(obj == null){
public static <T> 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 <T>
* @return
* String2ObjJSONclazzJava
* JSONJavaclazznullnull
* JSONnullStringequalsJSON
* 使ObjectMapperJSONJavaclazzIOExceptionnull
*
* @param str JSONJava
* @param clazz JavaJSONJavaPOJO
* @param <T> JavaJava
* @return clazzJavaJSONJSONnullnull
*/
public static <T> T String2Obj(String str,Class<T> clazz){
if(StringUtils.isEmpty(str) || clazz == null){
public static <T> T String2Obj(String str, Class<T> 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 <T>
* @return
* Str2ObjJavaString2ObjJSONJava
* TypeReferenceJSONTypeReferencenullnull
* JSONTypeReferenceTypeReferenceStringgetTypeJSON
* 使ObjectMapperJSONTypeReferenceIOExceptionnull
*
* @param str JSONTypeReference
* @param typeReference TypeReferenceList<SomeClass>Map<String, AnotherClass>便JSON
* @param <T> TypeReferenceJava
* @return TypeReferenceJavaJSONJSONTypeReferencenullnull
*/
public static <T> T Str2Obj(String str, TypeReference typeReference){
if(StringUtils.isEmpty(str) || typeReference == null){
public static <T> 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 <T>
* @return
* Str2ObjcollectionClasselementClassesJavaType使ObjectMapper
* 使ObjectMappergetTypeFactoryconstructParametricTypeJavaTypeJavaType
* 使ObjectMapperJSONJavaTypeIOExceptionnull
* List<SomeClass>Set<AnotherClass>Java
*
* @param str JSONcollectionClasselementClasses
* @param collectionClass List.classSet.class
* @param elementClasses List<List<SomeClass>>List.classSomeClass.classJavaType
* @param <T> Java
* @return collectionClasselementClassesJavaJSONIOExceptionnull
*/
public static <T> T Str2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
public static <T> 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;
}
}

@ -4,8 +4,19 @@ import java.security.MessageDigest;
/**
* MD5
* MD5UtilMD5MD5
* MD5便UTF-8MD5
*/
public class MD5Util {
/**
*
* byteToHexString
* MD5便
*
* @param b MD5MessageDigestdigest
* @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();
}
/**
*
* 0Java -128 127256使0255便
* 16hexDigits
* 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
* MD5MD5
* originJavaMessageDigestMD5MD5
* charsetnamenull使MD5byteArrayToHexString
* "utf-8"MD5toUpperCase便
* MessageDigestnull
*
* @param origin
* @param charsetname
* @return
* @param origin MD5
* @param charsetname "utf-8""gbk"null使MD5
* @return MD5null
*/
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
@ -44,16 +68,31 @@ public class MD5Util {
return resultString.toUpperCase();
}
/**
* 使UTF-8MD5便MD5
* MD5Encodeorigin"utf-8"MD5UTF-8MD5UTF-8Web
*
* @param origin MD5UTF-8UTF-8
* @return UTF-8MD5null
*/
public static String MD5EncodeUtf8(String origin) {
//这里可以加盐
return MD5Encode(origin, "utf-8");
}
/**
* "123456"MD5UTF-8MD5MD5EncodeUtf8
* 使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"};
}

@ -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,33 +11,59 @@ import java.util.Properties;
* @Date 2018/1/10 14:56
* @DESC
* @CONTACT 317758022@qq.com
* PropertiesUtilproperties
* "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){
/**
* nullnull
* PropertiespropskeytrimStringUtils.isBlanknull
* null
*
* @param key "键=值"Properties"server.port=8080""server.port"
* @return nullnull
*/
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){
/**
* nulldefaultValue
* PropertiespropskeyStringUtils.isBlank
* defaultValuevaluevalue
*
* @param key getProperty
* @param defaultValue null8080
* @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();

@ -15,22 +15,34 @@ import java.util.Collections;
* @Date 2019/1/7 12:23
* @CONTACT 317758022@qq.com
* @DESC
* RedisUtilsRedisSpringRedis
* Spring@Component便SpringRedis
*/
@Component
public class RedisUtils {
// 通过Spring的依赖注入机制自动注入一个RedisTemplate对象该对象是Spring Data Redis提供的用于操作Redis的核心模板类
// 这里限定了操作的键Key和值Value的类型都是字符串类型String通过它可以方便地执行各种Redis操作如读写数据等。
@Autowired
private RedisTemplate<String,String> redisTemplate;
private RedisTemplate<String, String> redisTemplate;
// 通过Spring的依赖注入机制自动注入一个JedisPoolWrapper对象这里未展示其具体代码但推测是对Jedis连接池进行封装的类
// 用于获取Jedis连接对象等相关操作可能在与Redis底层交互时发挥作用比如获取原生的Jedis连接来执行一些特定的Redis命令等情况。
@Autowired
private JedisPoolWrapper jedisPoolWrapper;
/**
*
* -2
* -1
* >=00
* STOCK_REDUCE_LUALuaRedis
* KEYS[1]ARGV[1]RedisEXISTS
* EXISTS1DECRBY00INCRBY -1
* 0EXISTS0 -2
* RedisLua
*/
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";
/**
*
* reduceStockRedisTemplateRedisRedisCallbackRedisJedis使LuaSTOCK_REDUCE_LUA
* stockKeystockChangeLuaCollections.unmodifiableListArrays.asListLua
* Lua -2 -10
*
* @Description
* @param stockKey RedisID
* @param stockChange Lua
* @return ObjectLua -2 -10
*/
public Object reduceStock(String stockKey,String stockChange){
public Object reduceStock(String stockKey, String stockChange) {
Object result = redisTemplate.execute((RedisCallback<Object>) redisConnection -> {
Jedis jedis = (Jedis)redisConnection.getNativeConnection();
return jedis.eval(STOCK_REDUCE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey))
,Collections.unmodifiableList(Arrays.asList(stockChange)));
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
return jedis.eval(STOCK_REDUCE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey)),
Collections.unmodifiableList(Arrays.asList(stockChange)));
});
return result;
}
}

@ -17,23 +17,39 @@ import javax.servlet.http.HttpServletRequest;
* @Date 2019/1/5 16:19
* @CONTACT 317758022@qq.com
* @DESC
* BaseController
* 使Spring@AutowiredCommonCacheUtilCookieUtilJsonUtilUser
* 使Slf4j便便
*/
@Slf4j
public class BaseController {
// 通过Spring的依赖注入机制自动注入一个CommonCacheUtil对象这里未展示其具体代码但推测是用于操作缓存的工具类比如操作Redis缓存等用于存储和获取一些常用数据
// 后续会使用这个对象从缓存中获取与当前登录用户相关的信息(比如存储在缓存中的用户对象的序列化字符串等内容)。
@Autowired
private CommonCacheUtil commonCacheUtil;
User getCurrentUser(HttpServletRequest httpServletRequest){
/**
* User
* HttpServletRequestHTTPloginTokenCookieCookieUtil.readLoginToken
* StringUtils.isEmpty(loginToken)trueSnailmallException
* 使CommonCacheUtilgetCacheValueuserJsonStrJSON
* nullSnailmallException使ResponseEnum.NEED_LOGIN
* 使JsonUtil.Str2ObjUserUser.classUser便使
*
* @param httpServletRequest HTTPCookie
* @return UserUser
*/
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;
}
}

@ -14,35 +14,76 @@ import org.springframework.web.bind.annotation.RestController;
* @Date 2019/1/5 21:44
* @CONTACT 317758022@qq.com
* @DESC
* OrderManageControllerSpring RESTful
* @RestControllerJSONAPI
* @RequestMapping"/manage/order"
* IOrderServiceSpringServerResponse便
*/
@RestController
@RequestMapping("/manage/order")
public class OrderManageController {
// 通过Spring的依赖注入机制自动注入一个IOrderService接口的实现类对象IOrderService应该定义了一系列与订单业务相关的方法比如获取订单列表、查询订单详情等操作的抽象方法
// 具体的业务逻辑实现在其对应的实现类中由Spring根据配置去查找并注入合适的实现类实例这个控制器类中的各个方法会调用IOrderService的相应方法来完成具体的订单业务处理。
@Autowired
private IOrderService orderService;
/**
* "/manage/order/list.do"GET@RequestMappingGETmethod
* pageNum@RequestParam"1"pageSize"10"10
* orderServicemanageListIOrderService
* orderService.manageListServerResponse<PageInfo>PageInfoJSON
*
* @param pageNum 1
* @param pageSize 便1010
* @return ServerResponse<PageInfo>ServerResponsePageInfo
*/
@RequestMapping("list.do")
public ServerResponse<PageInfo> orderList(@RequestParam(value = "pageNum",defaultValue = "1") int pageNum,
@RequestParam(value = "pageSize",defaultValue = "10") int pageSize){
return orderService.manageList(pageNum,pageSize);
public ServerResponse<PageInfo> 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
* orderNoLongorderServicemanageDetailIOrderService
* orderService.manageDetailServerResponse<OrderVo>OrderVoJSON
*
* @param orderNo Long便
* @return ServerResponse<OrderVo>ServerResponseOrderVo
*/
@RequestMapping("detail.do")
public ServerResponse<OrderVo> orderDetail(Long orderNo){
public ServerResponse<OrderVo> orderDetail(Long orderNo) {
return orderService.manageDetail(orderNo);
}
/**
* "/manage/order/search.do"GET
* orderNoLongpageNum"1"pageSize"10"
* orderServicemanageSearchIOrderService
* orderService.manageSearchServerResponse<PageInfo>PageInfoJSON
*
* @param orderNo 便Long
* @param pageNum 1
* @param pageSize 便1010
* @return ServerResponse<PageInfo>ServerResponsePageInfo
*/
@RequestMapping("search.do")
public ServerResponse<PageInfo> 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<PageInfo> 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
* orderNoLongorderServicemanageSendGoodsIOrderService
* orderService.manageSendGoodsServerResponse<String>JSON
*
* @param orderNo 便Long
* @return ServerResponse<String>ServerResponseString
*/
@RequestMapping("send_goods.do")
public ServerResponse<String> orderSendGoods(Long orderNo){
public ServerResponse<String> orderSendGoods(Long orderNo) {
return orderService.manageSendGoods(orderNo);
}
}

@ -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;
/**
* @MapperMyBatisMapperMyBatisSQL
* MyBatis
* OrderItemMapperOrderItemOrderItem
* IDMyBatisSQLSQLXMLSQL
*/
@Mapper
public interface OrderItemMapper {
/**
* idDELETE
* IntegeridSQLMyBatis
* 10
*
* @param id Integer
* @return 10
*/
int deleteByPrimaryKey(Integer id);
/**
* INSERT
* OrderItemrecord
* MyBatisSQLINSERT1
*
* @param record OrderItemMyBatisSQL
* @return 1
*/
int insert(OrderItem record);
/**
* insertINSERTSQL
* OrderItemrecordSQLMyBatisrecordnull
* 1null
*
* @param record OrderItemMyBatisnullSQL
* @return 1
*/
int insertSelective(OrderItem record);
/**
* idSELECT
* IntegeridMyBatisSQLSELECT
* OrderItemnull便
*
* @param id Integernull
* @return OrderItemnull使
*/
OrderItem selectByPrimaryKey(Integer id);
/**
* idUPDATESQL
* OrderItemrecordMyBatisrecordnullSQLnull
* 1null
*
* @param record OrderItemMyBatisnullSQL
* @return 1
*/
int updateByPrimaryKeySelective(OrderItem record);
/**
* idUPDATE
* OrderItemrecordnull
* MyBatisSQLUPDATErecord1
*
* @param record OrderItemMyBatisSQL
* @return 1
*/
int updateByPrimaryKey(OrderItem record);
/**
* orderNoIDuserIdSELECT
* LongorderNoIntegerIDuserIdMyBatisSQLSELECTID
* List<OrderItem>OrderItem便
* 使@ParamMyBatisSQLSQL
*
* @param orderNo LonguserId
* @param userId IDIntegerorderNo
* @return List<OrderItem>ID使
*/
List<OrderItem> getByOrderNoUserId(@Param("orderNo") Long orderNo, @Param("userId") Integer userId);
/**
* INSERT
* List<OrderItem>orderItemListOrderItem
* MyBatisSQLINSERT便
* void
* 使@ParamMyBatisSQLSQL
*
* @param orderItemList List<OrderItem>MyBatisSQL
*/
void batchInsert(@Param("orderItemList") List<OrderItem> orderItemList);
/**
* orderNoSELECT
* LongorderNoMyBatisSQLSELECT
* List<OrderItem>OrderItem便
* 使@ParamMyBatisSQLSQL
*
* @param orderNo Long便使
* @return List<OrderItem>使
*/
List<OrderItem> getByOrderNo(@Param("orderNo") Long orderNo);
}

@ -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;
/**
* @MapperMyBatisMapperMyBatis
* SQLOrderMapperOrderOrderItem
* OrderIDSQLXMLSQL
*/
@Mapper
public interface OrderMapper {
/**
* idDELETE
* IntegeridMyBatisSQLMyBatis
* 10
*
* @param id Integer
* @return 10
*/
int deleteByPrimaryKey(Integer id);
/**
* INSERT
* OrderrecordOrderID
* MyBatisSQLINSERT1
*
* @param record OrderMyBatisSQL
* @return 1
*/
int insert(Order record);
/**
* insertINSERTSQL
* OrderrecordSQLMyBatisrecordnull
* 1null
*
* @param record OrderMyBatisnullSQL
* @return 1
*/
int insertSelective(Order record);
/**
* idSELECT
* IntegeridMyBatisSQLSELECT
* OrderIDnull便
*
* @param id Integernull
* @return Ordernull使
*/
Order selectByPrimaryKey(Integer id);
/**
* idUPDATESQL
* OrderrecordMyBatisrecordnullSQLnull
* 1null
*
* @param record OrderMyBatisnullSQL
* @return 1
*/
int updateByPrimaryKeySelective(Order record);
/**
* idUPDATE
* Orderrecordnull
* MyBatisSQLUPDATErecord1
*
* @param record OrderMyBatisSQL
* @return 1
*/
int updateByPrimaryKey(Order record);
/**
* IDuserIdorderNoSELECT
* IntegerIDuserIdLongorderNoMyBatisSQLSELECTID
* OrderIDnull便
* 使@ParamMyBatisSQLSQL
*
* @param userId IDIntegerorderNo
* @param orderNo LonguserIdnull
* @return OrderIDnull使
*/
Order selectByUserIdOrderNo(@Param("userId") Integer userId, @Param("orderNo") Long orderNo);
/**
* orderNoSELECT
* LongorderNoMyBatisSQLSELECT
* OrderIDnull便
*
* @param orderNo Longnull
* @return Ordernull使
*/
Order selectByOrderNo(Long orderNo);
/**
* IDuserIdSELECT
* IntegeruserIdIDMyBatisSQLSELECT
* List<Order>OrderID便
*
* @param userId IDInteger
* @return List<Order>ID使
*/
List<Order> selectByUserId(Integer userId);
/**
* orderNoOrderItemSELECT
* LongorderNoMyBatisSQLSELECT
* List<OrderItem>OrderItem便
*
* @param orderNo Long
* @return List<OrderItem>使
*/
List<OrderItem> getByOrderNo(Long orderNo);
/**
* SELECT
* MyBatisSQLSELECT
* List<Order>OrderID便
*
* @return List<Order>使
*/
List<Order> selectAllOrder();
//定时关单
/**
* statusdateSELECT
* IntegerstatusStringdateMyBatisSQLSELECTdateSQL
* List<Order>OrderID便
* 使@ParamMyBatisSQLSQL
*
* @param status Integerdate
* @param date String"yyyy-MM-dd HH:mm:ss"SQLstatus
* @return List<Order>使
*/
List<Order> selectOrderStatusByCreateTime(@Param("status") Integer status, @Param("date") String date);
//关闭订单
int closeOrderByOrderId(Integer id);
/**
* UPDATEIDidSQLSQL
* IntegeridMyBatisSQLUPDATE
* 1
*
* @param id Integer
* @return
*/
int closeOrderByOrderId(Integer id);
//关闭订单
}

@ -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
* @MapperMyBatisMapperMyBatis
* MyBatisSQL
* PayInfoMapperPayInfoPayInfo
* SQLXMLSQL
*/
@Mapper
public interface PayInfoMapper {
/**
* idDELETE
* IntegeridMyBatisSQLMyBatis
* 10
*
* @param id Integer
* @return 10
*/
int deleteByPrimaryKey(Integer id);
/**
* INSERT
* PayInforecordPayInfo
* MyBatisSQLINSERT1
*
* @param record PayInfoMyBatisSQL
* @return 1
*/
int insert(PayInfo record);
/**
* insertINSERTSQL
* PayInforecordSQLMyBatisrecordnull
* 1null
*
* @param record PayInfoMyBatisnullSQL
* @return 1
*/
int insertSelective(PayInfo record);
/**
* idSELECT
* IntegeridMyBatisSQLSELECT
* PayInfonull便
*
* @param id Integernull
* @return PayInfonull使
*/
PayInfo selectByPrimaryKey(Integer id);
/**
* idUPDATESQL
* PayInforecordMyBatisrecordnullSQLnull
* 1null
*
* @param record PayInfoMyBatisnullSQL
* @return 1
*/
int updateByPrimaryKeySelective(PayInfo record);
/**
* idUPDATE
* PayInforecordnull
* MyBatisSQLUPDATErecord1
*
* @param record PayInfoMyBatisSQL
* @return 1
*/
int updateByPrimaryKey(PayInfo record);
}

@ -4,26 +4,55 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* CartJava
* 使Lombok @Data GetterSettertoStringequalshashCode便
* @AllArgsConstructor 便
* @NoArgsConstructor JSON
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Cart {
/**
* Integer便
*/
private Integer id;
/**
* IDIntegerID
*/
private Integer userId;
/**
* IDIntegerID便
*/
private Integer productId;
/**
* Integer
*/
private Integer quantity;
/**
* 01Integer便
*/
private Integer checked;
/**
* 使 @JsonFormat JSONWeb 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;
}

@ -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;
/**
* OrderJava
* Lombok @Data GetterSettertoStringequalshashCode使便
* @AllArgsConstructor 便
* @NoArgsConstructor JSON使
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
/**
* Integer便
*/
private Integer id;
/**
* Long便
*/
private Long orderNo;
/**
* IDIntegerID
*/
private Integer userId;
/**
* IDInteger便
*/
private Integer shippingId;
/**
* 使BigDecimal
*/
private BigDecimal payment;
/**
* 123Integer便
*/
private Integer paymentType;
/**
* 使Integer使BigDecimal
*/
private Integer postage;
/**
* 01234Integer
*/
private Integer status;
/**
* 使Date @JsonFormat "yyyy-MM-dd HH:mm:ss.SSS"JSONWeb 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;
}

@ -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;
/**
* OrderItemJava
* Lombok @Data GetterSettertoStringequalshashCode便
* @AllArgsConstructor
* @NoArgsConstructor JSON使
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderItem {
/**
* 便Integer
*/
private Integer id;
/**
* ID便IntegerID
*/
private Integer userId;
/**
* Long
*/
private Long orderNo;
/**
* IDInteger便
*/
private Integer productId;
/**
* 便StringT使
*/
private String productName;
/**
* 使String便
*/
private String productImage;
/**
* 使BigDecimalBigDecimalBigDecimal
*/
private BigDecimal currentUnitPrice;
/**
* Integer
*/
private Integer quantity;
/**
* 使BigDecimalcurrentUnitPricequantityBigDecimal
*/
private BigDecimal totalPrice;
/**
* 使 @JsonFormat "yyyy-MM-dd HH:mm:ss.SSS"JSONWeb 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;
}

@ -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
* PayInfoJava
* Lombok
* - @Data GetterSettertoStringequalshashCode便使
* - @NoArgsConstructor JSON访
* - @AllArgsConstructor 便PayInfo
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PayInfo {
/**
* 便Integer
*/
private Integer id;
/**
* ID便IntegerID
*/
private Integer userId;
/**
* Long
*/
private Long orderNo;
/**
* 123Integer便
*/
private Integer payPlatform;
/**
* String退
*/
private String platformNumber;
/**
* "SUCCESS""FAIL""PROCESSING"String便
*/
private String platformStatus;
/**
* Date
*/
private Date createTime;
/**
* Date
*/
private Date updateTime;
}

@ -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;
/**
* ProductJava
* Lombok
* - @Data GetterSettertoStringequalshashCode使便便使
* - @AllArgsConstructor
* - @NoArgsConstructor JSON访使
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
/**
* 便Integer
*/
private Integer id;
/**
* ID便IntegerID
*/
private Integer categoryId;
/**
* 便String
*/
private String name;
/**
* String
*/
private String subtitle;
/**
* 使String便
*/
private String mainImage;
/**
* String
*/
private String subImages;
/**
* 使便String
*/
private String detail;
/**
* 使BigDecimalBigDecimalBigDecimal
*/
private BigDecimal price;
/**
* Integer
*/
private Integer stock;
/**
* 012Integer
*/
private Integer status;
/**
* Date
*/
private Date createTime;
/**
* Date
*/
private Date updateTime;
}

@ -4,35 +4,80 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* ShippingJava
*
* Lombok
* - @Data GetterSettertoStringequalshashCode使便便使
* - @NoArgsConstructor JSON访使
* - @AllArgsConstructor 便
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Shipping {
/**
* 便Integer
*/
private Integer id;
/**
* ID便IntegerID
*/
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;
/**
* StringXXXXXXXXX
*/
private String receiverAddress;
/**
* 使String
*/
private String receiverZip;
/**
* 使 @JsonFormat yyyy-MM-dd HH:mm:ss.SSSJSONWeb 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.SSSDate
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS")
private Date updateTime;
}

@ -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
* UserJava
* 使Lombok
* - @Data GetterSettertoStringequalshashCode便使
* - @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;
/**
* Stringquestion
*/
private String answer;
/**
* 01Integer访
*/
//角色0-管理员,1-普通用户
private Integer role;
/**
* Date
*/
private Date createTime;
/**
* Date
*/
private Date updateTime;
}

@ -18,26 +18,48 @@ import java.util.List;
* @Date 2019/1/7 13:23
* @CONTACT 317758022@qq.com
* @DESC
* MessageReceiverRabbitMQ
* 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<MessageVo> 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);
}
}

@ -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
* CartProductVoValue ObjectVO
* 便Serializable使便
* LombokGetterSetter便
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CartProductVo implements Serializable {
/**
* Integer
*/
private Integer id;
/**
* ID便IntegerID
*/
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;
/**
* 使BigDecimalBigDecimalBigDecimal
*/
private BigDecimal productPrice;
/**
* 012Integer
*/
private Integer productStatus;
/**
* 使BigDecimalproductPricequantityBigDecimal
*/
private BigDecimal productTotalPrice;
/**
* Integer
*/
private Integer productStock;
private Integer productChecked;//此商品是否勾选
private String limitQuantity;//限制数量的一个返回结果
/**
* 01Integer
*/
private Integer productChecked;
/**
* 5String便
*/
private String limitQuantity;
}

@ -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
* CartVoValue ObjectVO便
* Lombok @Data GetterSetter使便
*/
@Data
public class CartVo {
/**
* CartProductVoCartProductVo
* 便使
*/
private List<CartProductVo> cartProductVoList;
/**
* 使BigDecimalBigDecimal
*
*/
private BigDecimal cartTotalPrice;
private Boolean allChecked;//是否已经都勾选
/**
* Booleantruefalse
*
*/
private Boolean allChecked;
/**
* IP访
* CartProductVo访便
*/
private String imageHost;
}

@ -7,11 +7,32 @@ import lombok.Data;
* @Date 2019/1/7 16:17
* @CONTACT 317758022@qq.com
* @DESC
* MessageVoValue ObjectVO
* Lombok @Data GetterSetter便使便
*/
@Data
public class MessageVo {
/**
* IntegerID
*/
private Integer userId;
/**
* ID使
* 使便Integer
*/
private Integer shippingId;
/**
* CartVo
* 便使CartVo
*/
private CartVo cartVo;
/**
*
* 便long
*/
private long orderNo;
}

@ -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
* OrderItemVoValue ObjectVO便
* Lombok @Data GetterSetter使便
*/
@Data
public class OrderItemVo {
/**
* Long便
*/
private Long orderNo;
/**
* Integer便
*/
private Integer productId;
/**
* 便StringT使
*/
private String productName;
/**
* 使String便
*/
private String productImage;
/**
* 使BigDecimalBigDecimalBigDecimal
*/
private BigDecimal currentUnitPrice;
/**
* Integer
*/
private Integer quantity;
/**
* 使BigDecimalcurrentUnitPricequantityBigDecimal
*/
private BigDecimal totalPrice;
/**
* String便
*/
private String createTime;
}

@ -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
* OrderProductVoValue ObjectVO便
* Lombok @Data GetterSetter使便
*/
@Data
public class OrderProductVo {
/**
* OrderItemVoOrderItemVo
* 便使
*/
private List<OrderItemVo> orderItemVoList;
/**
* 使BigDecimalOrderItemVoBigDecimal
*
*/
private BigDecimal productTotalPrice;
/**
* IP访
* OrderItemVo访便
*/
private String imageHost;
}

@ -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
* OrderVoValue ObjectVO便
* Lombok @Data GetterSetter使便
*/
@Data
public class OrderVo {
/**
*
* 便Long
*/
private Long orderNo;
/**
* 使BigDecimalBigDecimalBigDecimal
*/
private BigDecimal payment;
/**
* 123Integer便
*/
private Integer paymentType;
/**
* paymentType使String
*/
private String paymentTypeDesc;
/**
* 使Integer使BigDecimalInteger
*/
private Integer postage;
/**
* 01234Integer
*/
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;
/**
* OrderItemVoOrderItemVo
* 便退使List<OrderItemVo>
*/
//订单的明细
private List<OrderItemVo> orderItemVoList;
/**
* IP访
* OrderItemVo访便String
*/
private String imageHost;
/**
* ID使
* 使便Integer
*/
private Integer shippingId;
/**
* StringString
*/
private String receiverName;
/**
* ShippingVo便使ShippingVo
*/
private ShippingVo shippingVo;
}

@ -7,22 +7,49 @@ import lombok.Data;
* @Date 2019/1/5 21:48
* @CONTACT 317758022@qq.com
* @DESC
* ShippingVoValue ObjectVO便
* Lombok @Data GetterSetter使便
*/
@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;
/**
* StringXXXXXXXXX
*/
private String receiverAddress;
/**
* 使String
*/
private String receiverZip;
}

@ -122,6 +122,10 @@
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
<build>

@ -10,6 +10,327 @@ import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* @Author swg.
* @Date 2019/1/3 19:13
* @CONTACT 317758022@qq.com
* @DESC
*/
@Service
@Slf4j
public class FileServiceImpl implements IFileService{
@Override
public String upload(MultipartFile file, String path) {
String fileName = file.getOriginalFilename();
//扩展名
//abc.jpg
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1);
String uploadFileName = UUID.randomUUID().toString()+"."+fileExtensionName;
log.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName);
File fileDir = new File(path);
if(!fileDir.exists()){
fileDir.setWritable(true);
fileDir.mkdirs();
}
log.info("【文件上传路径为:{}】",fileDir);
File targetFile = new File(path,uploadFileName);
try {
file.transferTo(targetFile);
//文件已经上传成功了
log.info("【文件上传本地服务器成功】");
FtpUtil.uploadFile(Lists.newArrayList(targetFile));
//已经上传到ftp服务器上
log.info("【文件上传到文件服务器成功】");
targetFile.delete();
log.info("【删除本地文件】");
} catch (IOException e) {
log.error("上传文件异常",e);
return null;
}
//A:abc.jpg
//B:abc.jpg
return targetFile.getName();
}
}
package com.njupt.swg.service;
import com.google.common.collect.Lists;
import com.njupt.swg.common.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* @Author swg.
* @Date 2019/1/3 19:13
* @CONTACT 317758022@qq.com
* @DESC
*/
@Service
@Slf4j
public class FileServiceImpl implements IFileService{
@Override
public String upload(MultipartFile file, String path) {
String fileName = file.getOriginalFilename();
//扩展名
//abc.jpg
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1);
String uploadFileName = UUID.randomUUID().toString()+"."+fileExtensionName;
log.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName);
File fileDir = new File(path);
if(!fileDir.exists()){
fileDir.setWritable(true);
fileDir.mkdirs();
}
log.info("【文件上传路径为:{}】",fileDir);
File targetFile = new File(path,uploadFileName);
try {
file.transferTo(targetFile);
//文件已经上传成功了
log.info("【文件上传本地服务器成功】");
FtpUtil.uploadFile(Lists.newArrayList(targetFile));
//已经上传到ftp服务器上
log.info("【文件上传到文件服务器成功】");
targetFile.delete();
log.info("【删除本地文件】");
} catch (IOException e) {
log.error("上传文件异常",e);
return null;
}
//A:abc.jpg
//B:abc.jpg
return targetFile.getName();
}
}
package com.njupt.swg.service;
import com.google.common.collect.Lists;
import com.njupt.swg.common.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* @Author swg.
* @Date 2019/1/3 19:13
* @CONTACT 317758022@qq.com
* @DESC IFileService
* FTP
*/
@Service
@Slf4j
public class FileServiceImpl implements IFileService {
/**
* MultipartFile
*
* @param file
* @param path FTP
* @return FTPnull
*/
@Override
public String upload(MultipartFile file, String path) {
// 获取上传文件的原始文件名例如abc.jpg
String fileName = file.getOriginalFilename();
// 提取文件的扩展名,即从文件名中获取最后一个点(.之后的部分比如对于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);
// 判断该目录是否存在,如果不存在则进行创建目录操作。
// 先设置目录可写权限有些系统可能需要显式设置然后创建多层级目录mkdirs会创建所有不存在的父目录
if (!fileDir.exists()) {
fileDir.setWritable(true);
fileDir.mkdirs();
}
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文件服务器了记录日志信息。
log.info("【文件上传到文件服务器成功】");
// 删除本地已经上传到FTP服务器的临时文件释放本地磁盘空间因为已经不需要在本地保留该文件副本了。
targetFile.delete();
log.info("【删除本地文件】");
} catch (IOException e) {
// 如果在文件上传过程包括保存到本地、上传到FTP服务器等操作涉及到的IO操作出现异常
// 则记录错误日志信息并返回null表示上传失败。
log.error("上传文件异常", e);
return null;
}
// 返回上传到FTP服务器后的文件名这个文件名可能在后续业务中用于记录、关联等操作。
// 例如在数据库中记录该文件在FTP服务器上的存储名称等情况。
return targetFile.getName();
}
}
package com.njupt.swg.service;
import com.google.common.collect.Lists;
import com.njupt.swg.common.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* FileServiceImplIFileService
* MultipartFileFTPFtpUtil
* 访便
*
* @Author swg.
* @Date 2019/1/3 19:13
* @CONTACT 317758022@qq.com
* @DESC
*/
@Service
// @Service注解用于标记该类是Spring框架中的一个服务层组件意味着这个类会被Spring容器管理在其他地方如控制层可以通过依赖注入的方式使用这个类的实例
// 同时Spring会对其进行相关的生命周期管理以及一些配置和增强操作如AOP切面等若有配置的话方便业务逻辑的组织和复用。
@Slf4j
// 使用Lombok的 @Slf4j注解自动生成名为log的SLF4J日志记录器用于在文件上传的各个步骤中记录关键信息如文件名称、路径等以及出现异常情况时记录详细的错误信息
// 方便后续查看日志来了解文件上传的执行情况排查可能出现的问题例如文件上传失败的原因、是否成功传输到FTP服务器等情况。
public class FileServiceImpl implements IFileService {
/**
* MultipartFile
*
* @param file MultipartFileSpring
* 便
* @param path FTP
* "/upload"
* @return String
* 访null
*/
@Override
public String upload(MultipartFile file, String path) {
String fileName = file.getOriginalFilename();
// 获取上传文件的原始文件名,这个文件名是客户端上传文件时原本的名称,例如客户端上传了一个名为 "abc.jpg" 的图片文件,这里获取到的就是 "abc.jpg"
// 通过这个原始文件名可以提取文件的扩展名等信息,用于后续生成新的文件名以及进行相关的文件操作判断等情况。
//扩展名
//abc.jpg
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".") + 1);
// 从原始文件名中提取文件的扩展名,通过查找文件名中最后一个 "." 出现的位置,然后取其后面的字符串部分,得到文件的扩展名,
// 例如对于文件名 "abc.jpg",通过该操作获取到的扩展名就是 "jpg",用于后续构建新的文件名(确保新文件名保留正确的文件类型扩展名),便于识别文件类型以及正确的访问和处理文件。
String uploadFileName = UUID.randomUUID().toString() + "." + fileExtensionName;
// 使用Java的UUID通用唯一识别码生成一个随机的字符串并与获取到的文件扩展名拼接起来组成新的文件名
// 这样做的目的是为了避免文件名冲突(特别是在多用户同时上传文件或者重复上传同名文件的情况下),确保每个上传的文件在服务器上有一个唯一的标识名称,
// 例如生成的新文件名可能类似 "550e8400-e29b-41d4-a716-446655440000.jpg",方便后续文件的存储、管理以及访问操作。
log.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}", fileName, path, uploadFileName);
// 使用日志记录器记录文件上传操作开始时的关键信息,包括原始文件名、上传的目标路径以及生成的新文件名,方便后续查看日志了解文件上传的初始情况,
// 同时在出现问题时也可以通过这些记录来排查是哪个文件在哪个路径下上传出现了异常等情况。
File fileDir = new File(path);
if (!fileDir.exists()) {
fileDir.setWritable(true);
fileDir.mkdirs();
}
// 根据传入的文件上传路径创建一个File对象用于表示对应的目录如果该目录不存在则先设置其可写权限确保后续可以创建文件等操作
// 然后通过mkdirs方法创建该目录及其所有必要的父目录如果不存在的话保证文件上传时有对应的本地存储目录可用避免因目录不存在导致文件保存失败的情况。
log.info("【文件上传路径为:{}】", fileDir);
// 使用日志记录创建好的文件上传路径对应的File对象信息方便后续查看实际使用的文件存储目录情况确认目录是否创建正确以及是否符合预期等情况
// 也有助于排查可能因目录问题导致的文件上传异常情况。
File targetFile = new File(path, uploadFileName);
// 根据文件上传路径和新生成的文件名创建一个用于保存上传文件的目标File对象这个对象代表了文件在本地服务器上最终要保存的位置和对应的文件名
// 后续会将上传的文件内容写入到这个目标文件中,完成文件在本地服务器的临时存储操作。
try {
file.transferTo(targetFile);
// 将上传的MultipartFile对象中的文件内容传输并保存到之前创建的本地目标文件targetFile
// 这个操作会将客户端上传的文件数据实际写入到服务器本地的文件系统中完成文件在本地服务器的临时存储如果这个过程出现I/O异常等问题会抛出IOException异常
// 例如文件权限不足、磁盘空间不足等原因可能导致传输失败需要在catch块中进行相应的异常处理。
//文件已经上传成功了
log.info("【文件上传本地服务器成功】");
// 使用日志记录文件成功上传到本地服务器的信息,方便后续查看文件上传的执行进度以及确认本地存储这一步是否成功完成,
// 若后续出现问题如无法上传到FTP服务器等情况可以通过这个记录来判断是否是本地存储环节之后出现的问题。
FtpUtil.uploadFile(Lists.newArrayList(targetFile));
// 调用FtpUtil工具类的uploadFile方法将包含目标文件targetFile的列表传递进去执行将文件上传到FTP服务器的操作
// FtpUtil类应该是专门用于处理FTP文件传输相关逻辑的工具类通过它实现与FTP服务器的连接、文件上传等功能若这个过程出现异常如FTP连接失败、权限问题等
// 会在FtpUtil内部进行相应的处理可能记录日志、抛出异常等情况具体取决于FtpUtil的实现这里只是调用其方法来触发上传到FTP服务器的操作。
//已经上传到ftp服务器上
log.info("【文件上传到文件服务器成功】");
// 使用日志记录文件成功上传到FTP服务器的信息表明文件已经从本地服务器进一步传输到了FTP服务器上完成了整个文件上传流程中的关键步骤
// 通过这个记录可以方便后续查看文件是否完整地按照预期上传到了指定的FTP服务器若出现问题如文件在FTP服务器上不可访问等情况可以据此排查是FTP上传环节出现的问题。
targetFile.delete();
log.info("【删除本地文件】");
// 在文件成功上传到FTP服务器后删除本地临时保存的文件以释放本地服务器的磁盘空间避免不必要的文件冗余存储
// 通过这个操作本地服务器只起到一个临时中转存储的作用最终文件存储在FTP服务器上而本地只保留相关的文件上传记录如文件名等信息用于后续访问等操作
} catch (IOException e) {
log.error("上传文件异常", e);
// 如果在文件上传过程包括本地保存或者上传到FTP服务器等操作中出现IOException异常使用日志记录器记录详细的错误信息包括异常堆栈信息
// 方便后续查看日志来排查具体是哪个环节出现了I/O相关的问题例如是文件传输失败、FTP连接异常还是其他文件操作异常等情况同时返回null表示文件上传失败。
return null;
}
//A:abc.jpg
//B:abc.jpg
return targetFile.getName();
// 返回上传后文件在服务器上对应的文件名这里是经过重命名后的新文件名即之前生成的uploadFileName
// 这个文件名可以在其他地方如数据库中记录、返回给前端用于构建文件访问链接等使用用于标识和访问已经上传到FTP服务器上的文件完成文件上传操作并返回相应的文件名结果。
}
}
package com.njupt.swg.service;
import com.google.common.collect.Lists;
import com.njupt.swg.common.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* FileServiceImplIFileService
* MultipartFileFTPFtpUtil

@ -2,6 +2,89 @@ package com.njupt.swg.service;
import org.springframework.web.multipart.MultipartFile;
/**
* @Author swg.
* @Date 2019/1/3 19:12
* @CONTACT 317758022@qq.com
* @DESC
*/
public interface IFileService {
String upload(MultipartFile file, String path);
}
package com.njupt.swg.service;
import org.springframework.web.multipart.MultipartFile;
/**
* @Author swg.
* @Date 2019/1/3 19:12
* @CONTACT 317758022@qq.com
* @DESC
*/
public interface IFileService {
String upload(MultipartFile file, String path);
}
package com.njupt.swg.service;
import org.springframework.web.multipart.MultipartFile;
/**
* @Author swg.
* @Date 2019/1/3 19:12
* @CONTACT 317758022@qq.com
* @DESC
*
* 便
*/
public interface IFileService {
/**
*
*
* @param file SpringMultipartFile
*
* @param path 使
*
* @return
* null
*/
String upload(MultipartFile file, String path);
}
package com.njupt.swg.service;
import org.springframework.web.multipart.MultipartFile;
/**
* IFileService
* 使
* 便
*
* @Author swg.
* @Date 2019/1/3 19:12
* @CONTACT 317758022@qq.com
* @DESC
*/
public interface IFileService {
/**
*
* MultipartFile
*
*
* @param file MultipartFileSpring
* 便
*
* @param path
*
* "/upload" "upload" FTP访
* @return String
* 访
* null
*/
String upload(MultipartFile file, String path);
}
package com.njupt.swg.service;
import org.springframework.web.multipart.MultipartFile;
/**
* IFileService
* 使

@ -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 GetterSettertoStringhashCodeequals
* @CONTACT 317758022@qq.com
*/
@Data
public class ProductDetailVo {
/**
*
*/
private Integer id;
/**
* IDID
*/
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;
/**
* mainImagesubImages使
* 访URL使
*/
private String imageHost;
/**
* ID
*/
private Integer parentCategoryId;
}

@ -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 ObjectVO
*
* 便Lombok @Data
* GetterSettertoStringhashCodeequals
*/
@Data
public class ProductListVo {
/**
* 便ID
*/
private Integer id;
/**
* IDID便
*
*/
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;
}
Loading…
Cancel
Save