✨ Feat: [后台]:完成注册,登录,获取验证码接口
This commit is contained in:
parent
41532ef476
commit
ae652e9c08
@ -9,7 +9,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>business</artifactId>
|
<artifactId>bookmark-business</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<modules>
|
<modules>
|
||||||
<module>user</module>
|
<module>user</module>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fanxb</groupId>
|
<groupId>com.fanxb</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>bookmark-common</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>business</artifactId>
|
<artifactId>bookmark-business</artifactId>
|
||||||
<groupId>com.fanxb</groupId>
|
<groupId>com.fanxb</groupId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>user</artifactId>
|
<artifactId>bookmark-business-user</artifactId>
|
||||||
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,8 +1,13 @@
|
|||||||
package com.fanxb.bookmark.business.user.controller;
|
package com.fanxb.bookmark.business.user.controller;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import com.fanxb.bookmark.business.user.entity.LoginBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import com.fanxb.bookmark.business.user.entity.RegisterBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import com.fanxb.bookmark.business.user.service.UserService;
|
||||||
|
import com.fanxb.bookmark.common.entity.Result;
|
||||||
|
import com.fanxb.bookmark.common.entity.User;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类功能简述:
|
* 类功能简述:
|
||||||
@ -15,8 +20,48 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
@GetMapping("/test")
|
@Autowired
|
||||||
public String test() {
|
private UserService userService;
|
||||||
return "abce";
|
|
||||||
|
/**
|
||||||
|
* Description: 注册用户
|
||||||
|
*
|
||||||
|
* @param body 注册表单
|
||||||
|
* @return com.fanxb.bookmark.common.entity.Result
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 16:34
|
||||||
|
*/
|
||||||
|
@PutMapping("")
|
||||||
|
public Result register(@RequestBody RegisterBody body) {
|
||||||
|
userService.register(body);
|
||||||
|
return Result.success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 用户登录
|
||||||
|
*
|
||||||
|
* @param user 登录表单
|
||||||
|
* @return com.fanxb.bookmark.common.entity.Result
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 16:35
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Result login(@RequestBody LoginBody body) {
|
||||||
|
return Result.success(userService.login(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 获取验证码
|
||||||
|
*
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return com.fanxb.bookmark.common.entity.Result
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/5 17:37
|
||||||
|
*/
|
||||||
|
@GetMapping("/authCode")
|
||||||
|
public Result getAuthCode(@Param("email") String email) {
|
||||||
|
userService.sendAuthCode(email);
|
||||||
|
return Result.success(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package com.fanxb.bookmark.business.user.dao;
|
package com.fanxb.bookmark.business.user.dao;
|
||||||
|
|
||||||
|
import com.fanxb.bookmark.common.entity.User;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类功能简述:
|
* 类功能简述:
|
||||||
* 类功能详述:
|
* 类功能详述:
|
||||||
@ -7,5 +11,35 @@ package com.fanxb.bookmark.business.user.dao;
|
|||||||
* @author fanxb
|
* @author fanxb
|
||||||
* @date 2019/7/6 11:36
|
* @date 2019/7/6 11:36
|
||||||
*/
|
*/
|
||||||
public class UserDao {
|
@Component
|
||||||
|
public interface UserDao {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 新增一个用户
|
||||||
|
*
|
||||||
|
* @param user user
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 11:37
|
||||||
|
*/
|
||||||
|
void addOne(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 通过用户名或者email获取用户信息
|
||||||
|
*
|
||||||
|
* @param str username/email
|
||||||
|
* @return com.fanxb.bookmark.common.entity.User
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 16:45
|
||||||
|
*/
|
||||||
|
User selectByUsernameOrEmail(String str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 更新用户上次登录时间
|
||||||
|
*
|
||||||
|
* @param time 时间
|
||||||
|
* @param userId 用户id
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 16:46
|
||||||
|
*/
|
||||||
|
void updateLastLoginTime(@Param("time") long time, @Param("userId") int userId);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
package com.fanxb.bookmark.business.user.entity;
|
package com.fanxb.bookmark.business.user.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类功能简述:
|
* 类功能简述:登录表单
|
||||||
* 类功能详述:
|
* 类功能详述:
|
||||||
*
|
*
|
||||||
* @author fanxb
|
* @author fanxb
|
||||||
* @date 2019/7/6 17:25
|
* @date 2019/7/6 17:25
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
public class LoginBody {
|
public class LoginBody {
|
||||||
|
/**
|
||||||
|
* 用户名或邮箱
|
||||||
|
*/
|
||||||
|
private String str;
|
||||||
|
private String password;
|
||||||
|
private boolean rememberMe;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
package com.fanxb.bookmark.business.user.entity;
|
package com.fanxb.bookmark.business.user.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类功能简述:
|
* 类功能简述:登录返回数据
|
||||||
* 类功能详述:
|
* 类功能详述:
|
||||||
*
|
*
|
||||||
* @author fanxb
|
* @author fanxb
|
||||||
* @date 2019/7/6 16:52
|
* @date 2019/7/6 16:52
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
public class LoginRes {
|
public class LoginRes {
|
||||||
|
private String token;
|
||||||
|
private int userId;
|
||||||
|
private String username;
|
||||||
|
private String email;
|
||||||
|
private String lastLoginTime;
|
||||||
|
private String icon;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
package com.fanxb.bookmark.business.user.entity;
|
package com.fanxb.bookmark.business.user.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类功能简述:
|
* 类功能简述: 注册表单
|
||||||
* 类功能详述:
|
* 类功能详述:
|
||||||
*
|
*
|
||||||
* @author fanxb
|
* @author fanxb
|
||||||
* @date 2019/7/6 11:23
|
* @date 2019/7/6 11:23
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
public class RegisterBody {
|
public class RegisterBody {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String email;
|
||||||
|
private String authCode;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.fanxb.bookmark.business.user.service;
|
||||||
|
|
||||||
|
import com.fanxb.bookmark.business.user.dao.UserDao;
|
||||||
|
import com.fanxb.bookmark.business.user.entity.LoginBody;
|
||||||
|
import com.fanxb.bookmark.business.user.entity.LoginRes;
|
||||||
|
import com.fanxb.bookmark.business.user.entity.RegisterBody;
|
||||||
|
import com.fanxb.bookmark.common.Constant;
|
||||||
|
import com.fanxb.bookmark.common.entity.MailInfo;
|
||||||
|
import com.fanxb.bookmark.common.entity.User;
|
||||||
|
import com.fanxb.bookmark.common.exception.CustomException;
|
||||||
|
import com.fanxb.bookmark.common.exception.FormDataException;
|
||||||
|
import com.fanxb.bookmark.common.util.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类功能简述:
|
||||||
|
* 类功能详述:
|
||||||
|
*
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/5 17:39
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private static final String DEFAULT_ICON = "defaultIcon.png";
|
||||||
|
/**
|
||||||
|
* 短期jwt失效时间
|
||||||
|
*/
|
||||||
|
private static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000;
|
||||||
|
/**
|
||||||
|
* 长期jwt失效时间
|
||||||
|
*/
|
||||||
|
private static final long LONG_EXPIRE_TIME = 30 * TimeUtil.DAY_MS;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDao userDao;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 向目标发送验证码
|
||||||
|
*
|
||||||
|
* @param email 目标
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/5 17:48
|
||||||
|
*/
|
||||||
|
public void sendAuthCode(String email) {
|
||||||
|
MailInfo info = new MailInfo();
|
||||||
|
info.setSubject("签签世界注册验证码");
|
||||||
|
int code = StringUtil.getRandomNumber(6, 6);
|
||||||
|
info.setContent("欢迎注册 签签世界 ,本次验证码");
|
||||||
|
info.setContent(code + " 是您的验证码,注意验证码有效期为15分钟哦!");
|
||||||
|
info.setReceiver(email);
|
||||||
|
if (Constant.isDev) {
|
||||||
|
code = 123456;
|
||||||
|
} else {
|
||||||
|
MailUtil.sendTextMail(info);
|
||||||
|
}
|
||||||
|
RedisUtil.set(Constant.authCodeKey(email), String.valueOf(code), Constant.AUTH_CODE_EXPIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 用户注册
|
||||||
|
*
|
||||||
|
* @param body 注册表单
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 11:30
|
||||||
|
*/
|
||||||
|
public void register(RegisterBody body) {
|
||||||
|
String codeKey = Constant.authCodeKey(body.getEmail());
|
||||||
|
String realCode = RedisUtil.get(codeKey, String.class);
|
||||||
|
if ((!StringUtil.isEmpty(realCode)) && (!realCode.equals(body.getAuthCode()))) {
|
||||||
|
throw new CustomException("验证码错误");
|
||||||
|
}
|
||||||
|
RedisUtil.delete(codeKey);
|
||||||
|
User user = new User();
|
||||||
|
user.setUsername(body.getUsername());
|
||||||
|
user.setEmail(body.getEmail());
|
||||||
|
user.setIcon(DEFAULT_ICON);
|
||||||
|
user.setPassword(HashUtil.sha1(HashUtil.md5(body.getPassword())));
|
||||||
|
user.setCreateTime(System.currentTimeMillis());
|
||||||
|
user.setLastLoginTime(0);
|
||||||
|
userDao.addOne(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 登录
|
||||||
|
*
|
||||||
|
* @param body 登录表单
|
||||||
|
* @return LoginRes
|
||||||
|
* @author fanxb
|
||||||
|
* @date 2019/7/6 16:37
|
||||||
|
*/
|
||||||
|
public LoginRes login(LoginBody body) {
|
||||||
|
User userInfo = userDao.selectByUsernameOrEmail(body.getStr());
|
||||||
|
if (userInfo == null) {
|
||||||
|
throw new FormDataException("账号/密码错误");
|
||||||
|
}
|
||||||
|
if (!HashUtil.sha1(HashUtil.md5(body.getPassword())).equals(userInfo.getPassword())) {
|
||||||
|
throw new FormDataException("账号/密码错误");
|
||||||
|
}
|
||||||
|
Map<String, String> data = new HashMap<>(1);
|
||||||
|
data.put("userId", String.valueOf(userInfo.getUserId()));
|
||||||
|
String token = JwtUtil.encode(data, Constant.jwtSecret, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
|
||||||
|
LoginRes res = new LoginRes();
|
||||||
|
res.setToken(token);
|
||||||
|
res.setUserId(userInfo.getUserId());
|
||||||
|
res.setUsername(userInfo.getUsername());
|
||||||
|
res.setEmail(userInfo.getEmail());
|
||||||
|
res.setIcon(userInfo.getIcon());
|
||||||
|
userDao.updateLastLoginTime(System.currentTimeMillis(), userInfo.getUserId());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.fanxb.bookmark.business.user.dao.UserDao">
|
||||||
|
|
||||||
|
|
||||||
|
<insert id="addOne">
|
||||||
|
insert into user (username, email, icon, password, createTime, lastLoginTime)
|
||||||
|
value
|
||||||
|
(#{username}, #{email}, #{icon}, #{password}, #{createTime}, #{lastLoginTime})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.User">
|
||||||
|
select
|
||||||
|
userId,
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
icon,
|
||||||
|
password,
|
||||||
|
createTime
|
||||||
|
from user
|
||||||
|
where username = #{str} or email = #{str}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="updateLastLoginTime">
|
||||||
|
update user
|
||||||
|
set lastLoginTime = #{time}
|
||||||
|
where userId= #{userId}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
|
||||||
|
</mapper>
|
@ -9,7 +9,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>web</artifactId>
|
<artifactId>bookmark-web</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fanxb</groupId>
|
<groupId>com.fanxb</groupId>
|
||||||
<artifactId>user</artifactId>
|
<artifactId>bookmark-business-user</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.fanxb.bookmark.web;
|
package com.fanxb.bookmark.web;
|
||||||
|
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
* @date 2019/7/4 19:38
|
* @date 2019/7/4 19:38
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication(scanBasePackages = "com.fanxb.bookmark")
|
@SpringBootApplication(scanBasePackages = "com.fanxb.bookmark")
|
||||||
|
@MapperScan(basePackages = "com.fanxb.bookmark.**.dao")
|
||||||
public class Application {
|
public class Application {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
|
12
bookMarkService/web/src/main/resources/application-dev.yml
Normal file
12
bookMarkService/web/src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
spring:
|
||||||
|
redis:
|
||||||
|
host: 192.168.64.129
|
||||||
|
port: 6380
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
url: jdbc:mysql://192.168.64.129:3307/bookmark?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
|
||||||
|
mail:
|
||||||
|
host: heiout.tapme.top
|
||||||
|
username: bookmark@mail.tapme.top
|
||||||
|
password: abcdefg123456
|
||||||
|
port: 587
|
@ -1,9 +1,43 @@
|
|||||||
|
isDev: true
|
||||||
|
jwtSecret: abcdefgh
|
||||||
|
server:
|
||||||
|
port: 8088
|
||||||
|
servlet:
|
||||||
|
context-path: /bookmark/api/
|
||||||
spring:
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
application:
|
||||||
|
name: bookmark
|
||||||
|
flyway:
|
||||||
|
baseline-on-migrate: true
|
||||||
|
cache:
|
||||||
|
type: redis
|
||||||
|
redis:
|
||||||
|
database: 0
|
||||||
|
host: localhost
|
||||||
|
password:
|
||||||
|
timeout: 500ms
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 1000ms
|
||||||
|
max-idle: 20
|
||||||
|
min-idle: 2
|
||||||
|
datasource:
|
||||||
|
name: mysql
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
druid:
|
||||||
|
url: jdbc:mysql://localhost:3306/bookmark?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
# 其他连接池参数使用默认值
|
||||||
mail:
|
mail:
|
||||||
host:
|
host:
|
||||||
username:
|
username:
|
||||||
password:
|
password:
|
||||||
|
port: 465
|
||||||
properties:
|
properties:
|
||||||
mail:
|
mail:
|
||||||
smtp:
|
smtp:
|
||||||
@ -11,6 +45,8 @@ spring:
|
|||||||
starttls:
|
starttls:
|
||||||
enable: true
|
enable: true
|
||||||
required: true
|
required: true
|
||||||
port: 465
|
|
||||||
server:
|
mybatis:
|
||||||
port: 8088
|
configuration:
|
||||||
|
log-impl: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE `user` (
|
||||||
|
`userId` int UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(20) NOT NULL
|
||||||
|
COMMENT '用户名20位以内数字,字母组合',
|
||||||
|
`email` varchar(40) NOT NULL,
|
||||||
|
`icon` varchar(50) NOT NULL,
|
||||||
|
`password` char(40) NOT NULL
|
||||||
|
COMMENT '明文(6-20位数字密码组合)+userId 进行sha1签名',
|
||||||
|
`createTime` bigint NOT NULL DEFAULT 0,
|
||||||
|
`lastLoginTime` bigint NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`userId`)
|
||||||
|
)
|
||||||
|
ENGINE = InnoDB;
|
Loading…
x
Reference in New Issue
Block a user