添加基础工具类
This commit is contained in:
parent
566ce528f0
commit
4b4cd29c68
|
@ -0,0 +1,35 @@
|
||||||
|
package com.example.takeawaysystemserver.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ethereal
|
||||||
|
* @date 2024/3/20
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||||
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
|
redisTemplate.setConnectionFactory(factory);
|
||||||
|
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||||
|
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||||
|
redisTemplate.setKeySerializer(stringRedisSerializer);
|
||||||
|
redisTemplate.setHashKeySerializer(stringRedisSerializer);
|
||||||
|
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.example.takeawaysystemserver.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2024/1/29
|
||||||
|
* @Version 1.0
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebMvcConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebMvcConfigurer corsConfigurer() {
|
||||||
|
return new WebMvcConfigurer() {
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry registry) {
|
||||||
|
registry
|
||||||
|
.addMapping("/**")
|
||||||
|
.allowedOriginPatterns("*") // 允许所有域
|
||||||
|
.allowedMethods("*") // 允许任何方法(post、get等)
|
||||||
|
.allowedHeaders("*") // 允许任何请求头
|
||||||
|
.allowCredentials(false) // 允许证书、cookie
|
||||||
|
.exposedHeaders(HttpHeaders.SET_COOKIE)
|
||||||
|
.maxAge(3600L); // maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.example.takeawaysystemserver.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ethereal
|
||||||
|
* @date 2024/7/7
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
public interface CommonConstant {
|
||||||
|
String TOKEN_SECRET = "sadao_idfdv_uvnbdson_wd01jsdnvcz";
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.example.takeawaysystemserver.constant;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2024/7/7
|
||||||
|
* @Version 1.0
|
||||||
|
* @description http返回状态码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum HttpStatus {
|
||||||
|
|
||||||
|
SUCCESS(200,"操作成功"),
|
||||||
|
|
||||||
|
BAD_REQUEST(400,"参数列表错误"),
|
||||||
|
|
||||||
|
TOKEN_EXCEPTION(401,"token验证错误"),
|
||||||
|
|
||||||
|
ERROR(500,"系统内部错误");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
private final String desc;
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.example.takeawaysystemserver.util;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2024/7/7
|
||||||
|
* @Version 1.0
|
||||||
|
* @description 密码加密类
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DESUtil {
|
||||||
|
/**
|
||||||
|
* 传入文本内容,返回 SHA-256 串
|
||||||
|
*
|
||||||
|
* @param strText 传入加密文本
|
||||||
|
* @return 加密后文本
|
||||||
|
*/
|
||||||
|
public String SHA256(final String strText) {
|
||||||
|
return SHA(strText, "SHA-256");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传入文本内容,返回 SHA-512 串
|
||||||
|
*
|
||||||
|
* @param strText 传入加密文本
|
||||||
|
* @return 加密后文本
|
||||||
|
*/
|
||||||
|
public String SHA512(final String strText) {
|
||||||
|
return SHA(strText, "SHA-512");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* md5加密
|
||||||
|
* @param strText 传入加密文本
|
||||||
|
* @return 加密后文本
|
||||||
|
*/
|
||||||
|
public String SHAMD5(String strText) {
|
||||||
|
return SHA(strText, "MD5");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串 SHA 加密
|
||||||
|
*/
|
||||||
|
private String SHA(final String strText, final String strType) {
|
||||||
|
// 返回值
|
||||||
|
String strResult = null;
|
||||||
|
|
||||||
|
// 是否是有效字符串
|
||||||
|
if (strText != null && strText.length() > 0) {
|
||||||
|
try {
|
||||||
|
// SHA 加密开始
|
||||||
|
// 创建加密对象 并传入加密类型
|
||||||
|
MessageDigest messageDigest = MessageDigest.getInstance(strType);
|
||||||
|
// 传入要加密的字符串
|
||||||
|
messageDigest.update(strText.getBytes());
|
||||||
|
// 得到 byte 类型结果
|
||||||
|
byte[] byteBuffer = messageDigest.digest();
|
||||||
|
|
||||||
|
// 将 byte 转换为 string
|
||||||
|
StringBuilder strHexString = new StringBuilder();
|
||||||
|
// 遍历 byte buffer
|
||||||
|
for (byte b : byteBuffer) {
|
||||||
|
String hex = Integer.toHexString(0xff & b);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
strHexString.append('0');
|
||||||
|
}
|
||||||
|
strHexString.append(hex);
|
||||||
|
}
|
||||||
|
// 得到返回结果
|
||||||
|
strResult = strHexString.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package com.example.takeawaysystemserver.util;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.JWTCreator;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||||
|
import com.auth0.jwt.exceptions.TokenExpiredException;
|
||||||
|
import com.auth0.jwt.interfaces.Claim;
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import com.example.takeawaysystemserver.constant.CommonConstant;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2024/7/7
|
||||||
|
* @Version 1.0
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtUtil {
|
||||||
|
public static final Integer EXPIRE_TIME = 1; // 1天
|
||||||
|
/**
|
||||||
|
* 生成token
|
||||||
|
* @param map 传入map
|
||||||
|
* @param expired token存储时间
|
||||||
|
* @return token
|
||||||
|
*/
|
||||||
|
public static String getToken(Map<String,String> map, Integer expired){
|
||||||
|
Calendar instance = Calendar.getInstance();
|
||||||
|
//设置过期时间单位:天
|
||||||
|
instance.add(Calendar.DATE,expired);
|
||||||
|
//创建JWT builder
|
||||||
|
JWTCreator.Builder builder = JWT.create();
|
||||||
|
//payload
|
||||||
|
map.forEach(builder::withClaim);
|
||||||
|
//制定令牌过期时间
|
||||||
|
return builder.withExpiresAt(instance.getTime())
|
||||||
|
//sign
|
||||||
|
.sign(Algorithm.HMAC256(CommonConstant.TOKEN_SECRET));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证token合法性 || 获取token信息方法
|
||||||
|
* @param token 传入token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DecodedJWT verify(String token){
|
||||||
|
return JWT.require(Algorithm.HMAC256(CommonConstant.TOKEN_SECRET)).build().verify(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取payload
|
||||||
|
* @param token 传入token
|
||||||
|
* @return 返回payload
|
||||||
|
*/
|
||||||
|
public static Map<String, Claim> getPayload(String token){
|
||||||
|
Map<String,Claim> claims = null;
|
||||||
|
try{
|
||||||
|
claims = JWT.require(Algorithm.HMAC256(CommonConstant.TOKEN_SECRET)).build().verify(token).getClaims();
|
||||||
|
} catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return claims;
|
||||||
|
} catch (NullPointerException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取过期时间
|
||||||
|
*/
|
||||||
|
public static Date getExpiresAt(String token){
|
||||||
|
return JwtUtil.getExpiresAt(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断token是否过期
|
||||||
|
* @return true:过期 false:没过期
|
||||||
|
*/
|
||||||
|
public Boolean isTokenExpired(String token){
|
||||||
|
try {
|
||||||
|
Date expiration = JwtUtil.getExpiresAt(token);
|
||||||
|
return expiration.before(new Date());
|
||||||
|
} catch (TokenExpiredException e){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.example.takeawaysystemserver.util;
|
||||||
|
|
||||||
|
import com.example.takeawaysystemserver.constant.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2024/7/7
|
||||||
|
* @Version 1.0
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class R<T> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功
|
||||||
|
*/
|
||||||
|
public static final int SUCCESS = HttpStatus.SUCCESS.getCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*/
|
||||||
|
public static final int FAIL = HttpStatus.ERROR.getCode();
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static <T> R<T> ok() {
|
||||||
|
return restResult(null, SUCCESS, "操作成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> ok(T data) {
|
||||||
|
return restResult(data, SUCCESS, "操作成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> ok(T data, String msg) {
|
||||||
|
return restResult(data, SUCCESS, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> fail() {
|
||||||
|
return restResult(null, FAIL, "操作失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> fail(String msg) {
|
||||||
|
return restResult(null, FAIL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> fail(T data) {
|
||||||
|
return restResult(data, FAIL, "操作失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> fail(T data, String msg) {
|
||||||
|
return restResult(data, FAIL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> R<T> fail(Integer code, String msg) {
|
||||||
|
return restResult(null, code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> R<T> restResult(T data, Integer code, String msg) {
|
||||||
|
R<T> apiResult = new R<>();
|
||||||
|
apiResult.setCode(code);
|
||||||
|
apiResult.setData(data);
|
||||||
|
apiResult.setMsg(msg);
|
||||||
|
return apiResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Boolean isError(R<T> ret) {
|
||||||
|
return !isSuccess(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Boolean isSuccess(R<T> ret) {
|
||||||
|
return R.SUCCESS == ret.getCode();
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,140 @@
|
||||||
|
package com.example.takeawaysystemserver.util;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 慕华
|
||||||
|
* @date 2023/9/29
|
||||||
|
* @Version 1.0
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TimeUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前毫秒转int
|
||||||
|
*/
|
||||||
|
public static Integer getCurTimestamp() {
|
||||||
|
long l = System.currentTimeMillis();
|
||||||
|
return (int) (l / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今天0点的时间戳
|
||||||
|
*/
|
||||||
|
public static Integer getCurDayZeroTimestamp(){
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
cal.set(Calendar.SECOND, 0);
|
||||||
|
cal.set(Calendar.MINUTE, 0);
|
||||||
|
cal.set(Calendar.MILLISECOND, 0);
|
||||||
|
return (int) (cal.getTimeInMillis()/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*获取 当月0点的时间戳
|
||||||
|
*/
|
||||||
|
public static Integer getCurMonthZeroTimestamp(){
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.set(cal.get(Calendar.YEAR),cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0,0);
|
||||||
|
cal.set(Calendar.DAY_OF_MONTH,cal.getActualMinimum(Calendar.DAY_OF_MONTH));
|
||||||
|
return (int) (cal.getTimeInMillis()/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今年 0点的时间戳
|
||||||
|
*/
|
||||||
|
public static Integer getCurYearZeroTimestamp(){
|
||||||
|
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
c.add(Calendar.YEAR, 0);
|
||||||
|
//设置为1号,当前日期既为本年第一天
|
||||||
|
c.set(Calendar.DAY_OF_YEAR, 1);
|
||||||
|
//将小时至0
|
||||||
|
c.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
//将分钟至0
|
||||||
|
c.set(Calendar.MINUTE, 0);
|
||||||
|
//将秒至0
|
||||||
|
c.set(Calendar.SECOND,0);
|
||||||
|
//将毫秒至0
|
||||||
|
c.set(Calendar.MILLISECOND, 0);
|
||||||
|
// 获取今天第一天的时间戳
|
||||||
|
return (int) (c.getTimeInMillis()/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将时间转换为时间戳
|
||||||
|
*/
|
||||||
|
public static String dateToStamp(String s) throws ParseException {
|
||||||
|
String res;
|
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
Date date = simpleDateFormat.parse(s);
|
||||||
|
long ts = date.getTime()/ 1000;
|
||||||
|
res = String.valueOf(ts);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将时间戳转换为时间
|
||||||
|
*/
|
||||||
|
public static String stampToDate(Integer s){
|
||||||
|
String res;
|
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
long lt = Long.valueOf(s);
|
||||||
|
Date date = new Date(lt*1000);
|
||||||
|
res = simpleDateFormat.format(date);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取本周周一的时间戳
|
||||||
|
*/
|
||||||
|
public Long getSundayTime() {
|
||||||
|
// 获取当前日期
|
||||||
|
LocalDate currentDate = LocalDate.now();
|
||||||
|
|
||||||
|
// 计算本周的周日日期
|
||||||
|
LocalDate sundayDate = currentDate.with(DayOfWeek.MONDAY);
|
||||||
|
|
||||||
|
// 将周日日期转换为 LocalDateTime,时间部分默认为 00:00
|
||||||
|
LocalDateTime mondayTime = sundayDate.atStartOfDay();
|
||||||
|
|
||||||
|
// 将 LocalDateTime 转换为 Instant
|
||||||
|
Instant instant = mondayTime.toInstant(ZoneOffset.UTC);
|
||||||
|
|
||||||
|
// 获取对应的 Unix 时间戳(毫秒级别)
|
||||||
|
return instant.toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getNowDate(){
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNowYear(){
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
String s = Integer.toString(calendar.get(Calendar.YEAR));
|
||||||
|
return s.substring(s.length() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNowMonth(){
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
return String.format("%02d", calendar.get(Calendar.MONTH) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convertMonth(Long time){
|
||||||
|
LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault());
|
||||||
|
return dateTime.getYear() + String.format("%02d",dateTime.getMonthValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convertYear(Long time){
|
||||||
|
LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault());
|
||||||
|
return dateTime.getYear() + "";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue