初始化

This commit is contained in:
Qi 2025-03-25 19:26:17 +08:00
parent 4d23b57af2
commit bcb200888e
43 changed files with 218 additions and 2553 deletions

View File

@ -7,14 +7,14 @@
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="YuNan-system-start" />
<module name="ECS-system-start" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="ECS-system-start" options="-parameters" />
<module name="YuNan-cmd" options="" />
<module name="YuNan-system-start" options="-parameters" />
</option>
</component>
</project>

17
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="0@localhost" uuid="7ace6006-b2c1-43d5-b332-1f049544c3d7">
<driver-ref>redis</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
<jdbc-url>jdbc:redis://localhost:6379/0</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/YuNan-system-start/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/YuNan-system-start/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/ECS-system-start/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/ECS-system-start/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />

View File

@ -11,6 +11,11 @@
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

View File

@ -4,12 +4,12 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>yunan-boot-parent</groupId>
<artifactId>org.yunanframework.boot</artifactId>
<groupId>ecs-boot-parent</groupId>
<artifactId>org.ecsframework.boot</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>YuNan-system-start</artifactId>
<artifactId>ECS-system-start</artifactId>
<packaging>jar</packaging>
<dependencies>

View File

@ -1,17 +1,16 @@
package com.yunan;
package com.ecs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Slf4j
@SpringBootApplication(scanBasePackages = "com.yunan")
@SpringBootApplication(scanBasePackages = "com.ecs")
@EnableCaching//开启缓存
@EnableScheduling // 启用定时任务
public class YuNanDemoApplication {

View File

@ -1,4 +1,4 @@
package com.yunan.config;
package com.ecs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@ -1,4 +1,4 @@
package com.yunan.config;
package com.ecs.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,4 +1,4 @@
package com.yunan.config;
package com.ecs.config;
//
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;

View File

@ -0,0 +1,15 @@
package com.ecs.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@RestController //注解标识这是一个控制器类
@RequestMapping("/auth")
@Slf4j
@Api(tags = "身份验证接口")
public class AuthController {
}

View File

@ -0,0 +1,4 @@
package com.ecs.entity;
public class User {
}

View File

@ -1,4 +1,4 @@
package com.yunan.handler;
package com.ecs.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.MethodArgumentNotValidException;

View File

@ -0,0 +1,31 @@
spring:
application:
name: ECS
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://116.62.245.34:3306/ecs
username: root
password: e531fc3ca462bd0c
mvc:
pathmatch:
matching-strategy: ant-path-matcher
---
#****************************Redis***************************
spring:
redis:
host: localhost # Redis 服务器地址
port: 6379 # Redis 服务器端口
password: # 如果 Redis 设置了密码
database: 0 # 使用的数据库索引,默认是 0
server:
port: 8080
logging:
level:
org.springframework.boot.context.web: DEBUG

View File

@ -0,0 +1,5 @@
<?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.ecs.mapper.AuthMapper">
</mapper>

View File

@ -1,7 +1,6 @@
package com.yunan;
package com.ecs;
import org.springframework.boot.test.context.SpringBootTest;
import org.testng.annotations.Test;
class YuNanDemoApplicationTests {

View File

@ -1,19 +0,0 @@
package com.yunan.constant;
public class ResponseCode {
// 成功
public static final int SUCCESS = 200;
// 失败
public static final int ERROR = 500;
// 其他常用的状态码
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
// 你可以根据需要继续添加其他状态码
}

View File

@ -1,77 +0,0 @@
package com.yunan.controller;
import com.yunan.constant.ResponseCode;
import com.yunan.response.ApiResponse;
import com.yunan.dto.LoginDTO;
import com.yunan.dto.RegisterDTO;
import com.yunan.service.AuthService;
import com.yunan.service.EmailService;
import com.yunan.util.VerificationCodeUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.validation.Valid;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController //注解标识这是一个控制器类
@RequestMapping("/auth")
@Slf4j
@Api(tags = "身份验证接口")
public class AuthController {
@Resource
private AuthService authService;
@Autowired
private EmailService emailService;
/**
* 用户登入
* @param loginDTO
*/
@PostMapping("/login")
@ApiOperation("用户登入")
public ResponseEntity<Object> login(@RequestBody LoginDTO loginDTO) {
return authService.login(loginDTO);
}
/**
* 用户注册
* @param registerDTO
*/
@PostMapping("/register")
@ApiOperation("用户注册")
public ResponseEntity<Object> register(@RequestBody @Valid RegisterDTO registerDTO) throws MessagingException, UnsupportedEncodingException {
return authService.register(registerDTO);
}
/**
* 发送验证码
* @param registerDTO
* @return {@link ApiResponse }<{@link String }>
* @throws MessagingException
*/
@PostMapping("/sendEmail")
@ApiOperation("邮箱验证码")
public ApiResponse<String> sendEmail(@RequestBody @Valid RegisterDTO registerDTO) throws MessagingException {
return emailService.sendMail(registerDTO.getEmail());
}
}

View File

@ -1,33 +0,0 @@
package com.yunan.controller;
import com.yunan.dto.Captcha;
import com.yunan.response.Result;
import com.yunan.service.CaptchaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/captcha")
@Api(tags = "验证码拼图")
public class CaptchaController {
@Autowired
private CaptchaService captchaService;
@ApiOperation(value = "生成验证码拼图")
@PostMapping("get-captcha")
public Result getCaptcha(@RequestBody Captcha captcha) {
return Result.ok(captchaService.getCaptcha(captcha));
}
}

View File

@ -1,56 +0,0 @@
package com.yunan.dto;
import lombok.Data;
@Data
public class Captcha {
/**
* 随机字符串
**/
private String nonceStr;
/**
* 验证值
**/
private String value;
/**
* 生成的画布的base64
**/
private String canvasSrc;
/**
* 画布宽度
**/
private Integer canvasWidth;
/**
* 画布高度
**/
private Integer canvasHeight;
/**
* 生成的阻塞块的base64
**/
private String blockSrc;
/**
* 阻塞块宽度
**/
private Integer blockWidth;
/**
* 阻塞块高度
**/
private Integer blockHeight;
/**
* 阻塞块凸凹半径
**/
private Integer blockRadius;
/**
* 阻塞块的横轴坐标
**/
private Integer blockX;
/**
* 阻塞块的纵轴坐标
**/
private Integer blockY;
/**
* 图片获取位置
**/
private Integer place;
}

View File

@ -1,37 +0,0 @@
package com.yunan.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
@ApiModel("用户登入请求")
@Data
public class LoginDTO {
@NotBlank(message = "用户名不能为空")
@ApiModelProperty(value = "用户名")
private String username;
@Email(message = "请输入有效的邮箱")
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "电话号码")
private String phoneNumber;
@ApiModelProperty(value = "邮箱或者电话号码", required = true)
private String emailOrPhoneNumber;
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
private String token;
}

View File

@ -1,33 +0,0 @@
package com.yunan.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
@ApiModel("用户注册请求")
@Data
public class RegisterDTO {
@NotBlank(message = "用户名不能为空")
@ApiModelProperty(value = "用户名")
private String username;
@Email(message = "请输入有效的邮箱")
@ApiModelProperty(value = "邮箱", required = true)
private String email;
@ApiModelProperty(value = "电话号码")
private String phoneNumber;
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码", required = true)
private String password;
@ApiModelProperty(value = "验证码", required = true)
private String code;
private String token;
}

View File

@ -1,40 +0,0 @@
package com.yunan.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户实体类
*/
@Data
public class Users implements Serializable {
private long id;
/** 用户名*/
private String username;
/** 密码*/
private String password;
/** 邮箱*/
private String email;
/** 手机号*/
private String phoneNumber;
/** 创建用户信息时间 */
private Date createdTime;
/** 修改用户信息时间 */
private String updatedTime;
/** 用户状态*/
private int state;
@Override
public String toString() {
return "Users{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", createTime='" + createdTime + '\'' +
", updateTime='" + updatedTime + '\'' +
", state=" + state +
'}';
}
}

View File

@ -1,10 +0,0 @@
package com.yunan.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yunan.dto.Users;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AuthMapper extends BaseMapper<Users> {
}

View File

@ -1,46 +0,0 @@
package com.yunan.response;
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public ApiResponse(int code, String message) {
this.code = code;
this.message = message;
}
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
// Getters and Setters
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@ -1,48 +0,0 @@
package com.yunan.response;
// ErrorResponse.java
public class ErrorResponse {
private String message; // 错误消息
private int code; // 错误代码 (可选)
private long timestamp; // 错误发生的时间戳 (可选)
// 构造方法
public ErrorResponse(String message) {
this.message = message;
this.timestamp = System.currentTimeMillis(); // 当前时间戳
this.code = 400; // 默认为 400 错误代码
}
// 构造方法带自定义错误码
public ErrorResponse(String message, int code) {
this.message = message;
this.code = code;
this.timestamp = System.currentTimeMillis();
}
// Getter Setter
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}

View File

@ -1,68 +0,0 @@
package com.yunan.response;
public class Result {
private int code; // 状态码200代表成功其他代表失败
private String message; // 响应信息
private Object data; // 响应数据
// 默认构造函数
public Result() {
}
// 构造函数
public Result(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
// 成功返回
public static Result ok(Object data) {
return new Result(200, "成功", data); // 200代表成功
}
// 错误返回
public static Result error(String message) {
return new Result(500, message, null); // 500代表服务器错误message为错误信息
}
// 错误返回带code和message
public static Result error(int code, String message) {
return new Result(code, message, null); // 根据传入的code返回不同的错误
}
// get set 方法
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
// 重写toString方法方便调试输出
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}

View File

@ -1,17 +0,0 @@
package com.yunan.service;
import com.yunan.dto.LoginDTO;
import com.yunan.dto.RegisterDTO;
import org.springframework.http.ResponseEntity;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import java.io.UnsupportedEncodingException;
public interface AuthService {
ResponseEntity<Object> register(RegisterDTO registerDTO) throws UnsupportedEncodingException, AddressException, MessagingException;
ResponseEntity<Object> login(LoginDTO loginDTO);
}

View File

@ -1,88 +0,0 @@
package com.yunan.service;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import com.yunan.dto.Captcha;
import com.yunan.util.CaptchaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.awt.image.BufferedImage;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
public class CaptchaService {
/**
* 拼图验证码允许偏差
**/
private static Integer ALLOW_DEVIATION = 3;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 校验验证码
* @param imageKey
* @param imageCode
* @return boolean
**/
public String checkImageCode(String imageKey, String imageCode) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
String text = ops.get("imageCode:" + imageKey);
if(StrUtil.isBlank(text)){
return "验证码已失效";
}
// 根据移动距离判断验证是否成功
if (Math.abs(Integer.parseInt(text) - Integer.parseInt(imageCode)) > ALLOW_DEVIATION) {
return "验证失败,请控制拼图对齐缺口";
}
return null;
}
/**
* 缓存验证码有效期15分钟
* @param key
* @param code
**/
public void saveImageCode(String key, String code) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set("imageCode:" + key, code, 15, TimeUnit.MINUTES);
}
/**
* 获取验证码拼图生成的抠图和带抠图阴影的大图及抠图坐标
**/
public Object getCaptcha(Captcha captcha) {
//参数校验
CaptchaUtils.checkCaptcha(captcha);
//获取画布的宽高
int canvasWidth = captcha.getCanvasWidth();
int canvasHeight = captcha.getCanvasHeight();
//获取阻塞块的宽高/半径
int blockWidth = captcha.getBlockWidth();
int blockHeight = captcha.getBlockHeight();
int blockRadius = captcha.getBlockRadius();
//获取资源图
BufferedImage canvasImage = CaptchaUtils.getBufferedImage(captcha.getPlace());
//调整原图到指定大小
canvasImage = CaptchaUtils.imageResize(canvasImage, canvasWidth, canvasHeight);
//随机生成阻塞块坐标
int blockX = CaptchaUtils.getNonceByRange(blockWidth, canvasWidth - blockWidth - 10);
int blockY = CaptchaUtils.getNonceByRange(10, canvasHeight - blockHeight + 1);
//阻塞块
BufferedImage blockImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);
//新建的图像根据轮廓图颜色赋值源图生成遮罩
CaptchaUtils.cutByTemplate(canvasImage, blockImage, blockWidth, blockHeight, blockRadius, blockX, blockY);
// 移动横坐标
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
// 缓存
saveImageCode(nonceStr,String.valueOf(blockX));
//设置返回参数
captcha.setNonceStr(nonceStr);
captcha.setBlockY(blockY);
captcha.setBlockSrc(CaptchaUtils.toBase64(blockImage, "png"));
captcha.setCanvasSrc(CaptchaUtils.toBase64(canvasImage, "png"));
return captcha;
}
}

View File

@ -1,15 +0,0 @@
package com.yunan.service;
import com.yunan.response.ApiResponse;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
@Service
public interface EmailService {
ApiResponse<String> sendMail(String email) throws MessagingException;
}

View File

@ -1,43 +0,0 @@
package com.yunan.service;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class RedisExpireTask {
private final long time = 3600000; //一小时
private RedisTemplate<String, Object> redisTemplate;
public RedisExpireTask(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 每隔一分钟执行一次
@Scheduled(fixedRate = time)
public void removeExpiredKeys() {
long now = System.currentTimeMillis();
// 获取过期的 hashKey
Set<Object> expiredHashKeys = redisTemplate.opsForZSet().rangeByScore("rightCode:expire", 0, now);
if (expiredHashKeys != null && !expiredHashKeys.isEmpty()) {
// Set<Object> 转换为 Set<String>
Set<String> expiredHashKeysSet = expiredHashKeys.stream()
.map(Object::toString) // Object 转换为 String
.collect(Collectors.toSet());
// 删除过期的数据
redisTemplate.opsForZSet().remove("rightCode:expire", expiredHashKeysSet.toArray(new String[0]));
// 同时从哈希表中删除对应的 hashKey
for (String hashKey : expiredHashKeysSet) {
redisTemplate.opsForHash().delete("rightCode", hashKey);
}
}
}
}

View File

@ -1,173 +0,0 @@
package com.yunan.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunan.dto.LoginDTO;
import com.yunan.dto.RegisterDTO;
import com.yunan.dto.Users;
import com.yunan.mapper.AuthMapper;
import com.yunan.response.ErrorResponse;
import com.yunan.service.AuthService;
import com.yunan.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Random;
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {
@Resource
private AuthMapper authMapper;
@Autowired
RedisUtils redisUtils;
// 手机号正则表达式
private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
// 邮箱正则表达式
private static final String EMAIL_REGEX =
"^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
public static boolean isValidPhone(String phone) {
return phone != null && phone.matches(PHONE_REGEX);
}
public static boolean isValidEmail(String input) {
return input != null && input.matches(EMAIL_REGEX);
}
@Override
public ResponseEntity<Object> register(RegisterDTO registerDTO) {
//获取正确的验证码
Object rightCode = redisUtils.hGet("rightCode", registerDTO.getEmail());
//处理registerDTO对数据为空的逻辑
// 验证验证码的正确性
if(registerDTO.getCode().equals(rightCode)){
LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Users::getEmail, registerDTO.getEmail());
if(authMapper.selectOne(queryWrapper) != null) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(new ErrorResponse("用户已存在!!!"));
}
//为用户创建默认用户名
String userName = createUserName();
Users user = new Users();
//设置账号创建时间
Date currentDate = new Date();
user.setCreatedTime(currentDate);
user.setUsername(userName);
user.setPassword(registerDTO.getPassword());
user.setEmail(registerDTO.getEmail());
//状态设置 " 1 " 正常账号
user.setState(1);
boolean isSaved = authMapper.insert(user) > 0;
//测试
// boolean isSaved = false;
// boolean isSaved = true;
if (isSaved) {
return ResponseEntity
.status(HttpStatus.OK)
.body("注册成功!");
} else {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("数据库存储失败,请稍后重试");
}
} else{
// 如果验证码为空或者与正确的验证码不匹配返回错误信息
return ResponseEntity
.status(HttpStatus.BAD_REQUEST) // 设置 HTTP 状态码为 400
.body(new ErrorResponse("验证码不正确或已过期")); // 返回自定义的错误消息
}
}
@Override
public ResponseEntity<Object> login(LoginDTO loginDTO) {
log.info("emai: {}", isValidEmail(loginDTO.getEmailOrPhoneNumber()));
log.info("phone: {}", isValidPhone(loginDTO.getEmailOrPhoneNumber()));
if(isValidPhone(loginDTO.getEmailOrPhoneNumber())) {
loginDTO.setPhoneNumber(loginDTO.getEmailOrPhoneNumber());
}else if(isValidEmail(loginDTO.getEmailOrPhoneNumber())) {
loginDTO.setEmail(loginDTO.getEmailOrPhoneNumber());
}else{
ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("账号校验失败");
}
if(loginDTO.getEmail() != null){
LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Users::getEmail, loginDTO.getEmail());
Users users = authMapper.selectOne(queryWrapper);
if(users == null){
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("邮箱用户不存在!!!"));
}
String rightPassword = users.getPassword();
if(rightPassword.equals(loginDTO.getPassword())){
return ResponseEntity
.status(HttpStatus.OK)
.body("登入成功!!!");
}else {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("邮箱或密码错误!!!");
}
}else if(loginDTO.getPhoneNumber() != null){
LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Users::getPhoneNumber, loginDTO.getPhoneNumber());
Users users = authMapper.selectOne(queryWrapper);
if(users == null){
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("手机号用户不存在!!"));
}
String rightPassword = users.getPassword();
if(rightPassword.equals(loginDTO.getPassword())){
return ResponseEntity
.status(HttpStatus.OK)
.body("登入成功!!!");
}else {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("手机号或密码错误!!!");
}
}else{
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("其他错误待管理员处理!!!"));
}
}
//随机生成用户名算法
public String createUserName() {
// 字符集包括小写字母和数字
String chars = "abcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder randomPart = new StringBuilder();
// 随机生成6位字符
Random random = new Random();
for (int i = 0; i < 6; i++) {
randomPart.append(chars.charAt(random.nextInt(chars.length())));
}
// 获取当前时间戳秒级
long timestamp = System.currentTimeMillis() / 1000;
// 拼接生成用户名例如 "user_xxx123_1638326400"
return "user_" + randomPart.toString() + "_" + timestamp;
}
}

View File

@ -1,52 +0,0 @@
package com.yunan.service.impl;
import com.yunan.constant.ResponseCode;
import com.yunan.response.ApiResponse;
import com.yunan.service.EmailService;
import com.yunan.util.RedisUtils;
import com.yunan.util.VerificationCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.concurrent.TimeUnit;
@Service
public class EmailServiceImpl implements EmailService {
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String from;
@Autowired
RedisUtils redisUtils;
public ApiResponse<String> sendMail(String email) throws MessagingException {
// 生成验证码
String code = VerificationCodeUtils.generateCode(6);
// 发送邮件
String subject = "注册验证码";
String content = "尊敬的用户,您的验证码为:" + code + ",有效期5分钟,请勿泄漏!";
// 创建邮件消息
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(email);
helper.setSubject(subject);
helper.setText(content, true);
// 发送邮件
mailSender.send(message);
redisUtils.hPutWithExpire("rightCode", email, code, 5, TimeUnit.MINUTES);
return new ApiResponse<>(ResponseCode.SUCCESS, "验证码已发送");
}
}

View File

@ -1,229 +0,0 @@
package com.yunan.util;
import com.yunan.dto.Captcha;
import org.apache.commons.lang3.RandomUtils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.Objects;
import java.util.Random;
public class CaptchaUtils {
/**
* 网络图片地址
**/
private final static String IMG_URL = "https://loyer.wang/view/ftp/wallpaper/%s.jpg";
/**
* 本地图片地址
**/
private final static String IMG_PATH = "C:/Users/NanYu/Pictures/ComputerPaper/%s.jpg";
/**
* 入参校验设置默认值
**/
public static void checkCaptcha(Captcha captcha) {
//设置画布宽度默认值
if (captcha.getCanvasWidth() == null) {
captcha.setCanvasWidth(320);
}
//设置画布高度默认值
if (captcha.getCanvasHeight() == null) {
captcha.setCanvasHeight(155);
}
//设置阻塞块宽度默认值
if (captcha.getBlockWidth() == null) {
captcha.setBlockWidth(65);
}
//设置阻塞块高度默认值
if (captcha.getBlockHeight() == null) {
captcha.setBlockHeight(55);
}
//设置阻塞块凹凸半径默认值
if (captcha.getBlockRadius() == null) {
captcha.setBlockRadius(9);
}
//设置图片来源默认值
if (captcha.getPlace() == null) {
captcha.setPlace(0);
}
}
/**
* 获取指定范围内的随机数
**/
public static int getNonceByRange(int start, int end) {
Random random = new Random();
return random.nextInt(end - start + 1) + start;
}
/**
* 获取验证码资源图
**/
public static BufferedImage getBufferedImage(Integer place) {
try {
//随机图片
// int nonce = getNonceByRange(0, 1000);
int nonce = 111;
//获取网络资源图片
if (0 == place) {
String imgUrl = String.format(IMG_URL, nonce);
URL url = new URL(imgUrl);
return ImageIO.read(url.openStream());
}
//获取本地图片
else {
String imgPath = String.format(IMG_PATH, nonce);
File file = new File(imgPath);
return ImageIO.read(file);
}
} catch (Exception e) {
System.out.println("获取拼图资源失败");
//异常处理
return null;
}
}
/**
* 调整图片大小
**/
public static BufferedImage imageResize(BufferedImage bufferedImage, int width, int height) {
Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = resultImage.createGraphics();
graphics2D.drawImage(image, 0, 0, null);
graphics2D.dispose();
return resultImage;
}
/**
* 抠图并生成阻塞块
**/
public static void cutByTemplate(BufferedImage canvasImage, BufferedImage blockImage, int blockWidth, int blockHeight, int blockRadius, int blockX, int blockY) {
BufferedImage waterImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);
//阻塞块的轮廓图
int[][] blockData = getBlockData(blockWidth, blockHeight, blockRadius);
//创建阻塞块具体形状
for (int i = 0; i < blockWidth; i++) {
for (int j = 0; j < blockHeight; j++) {
try {
//原图中对应位置变色处理
if (blockData[i][j] == 1) {
//背景设置为黑色
waterImage.setRGB(i, j, Color.BLACK.getRGB());
blockImage.setRGB(i, j, canvasImage.getRGB(blockX + i, blockY + j));
//轮廓设置为白色取带像素和无像素的界点判断该点是不是临界轮廓点
if (blockData[i + 1][j] == 0 || blockData[i][j + 1] == 0 || blockData[i - 1][j] == 0 || blockData[i][j - 1] == 0) {
blockImage.setRGB(i, j, Color.WHITE.getRGB());
waterImage.setRGB(i, j, Color.WHITE.getRGB());
}
}
//这里把背景设为透明
else {
blockImage.setRGB(i, j, Color.TRANSLUCENT);
waterImage.setRGB(i, j, Color.TRANSLUCENT);
}
} catch (ArrayIndexOutOfBoundsException e) {
//防止数组下标越界异常
}
}
}
//在画布上添加阻塞块水印
addBlockWatermark(canvasImage, waterImage, blockX, blockY);
}
/**
* 构建拼图轮廓轨迹
**/
private static int[][] getBlockData(int blockWidth, int blockHeight, int blockRadius) {
int[][] data = new int[blockWidth][blockHeight];
double po = Math.pow(blockRadius, 2);
//随机生成两个圆的坐标在4个方向上 随机找到2个方向添加凸/
///凹1
int face1 = RandomUtils.nextInt(0,4);
///凹2
int face2;
//保证两个凸/凹不在同一位置
do {
face2 = RandomUtils.nextInt(0,4);
} while (face1 == face2);
//获取凸/凹起位置坐标
int[] circle1 = getCircleCoords(face1, blockWidth, blockHeight, blockRadius);
int[] circle2 = getCircleCoords(face2, blockWidth, blockHeight, blockRadius);
//随机凸/凹类型
int shape = getNonceByRange(0, 1);
//圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心a,b,半径为r的圆
//计算需要的小图轮廓用二维数组来表示二维数组有两张值0和1其中0表示没有颜色1有颜色
for (int i = 0; i < blockWidth; i++) {
for (int j = 0; j < blockHeight; j++) {
data[i][j] = 0;
//创建中间的方形区域
if ((i >= blockRadius && i <= blockWidth - blockRadius && j >= blockRadius && j <= blockHeight - blockRadius)) {
data[i][j] = 1;
}
double d1 = Math.pow(i - Objects.requireNonNull(circle1)[0], 2) + Math.pow(j - circle1[1], 2);
double d2 = Math.pow(i - Objects.requireNonNull(circle2)[0], 2) + Math.pow(j - circle2[1], 2);
//创建两个凸/
if (d1 <= po || d2 <= po) {
data[i][j] = shape;
}
}
}
return data;
}
/**
* 根据朝向获取圆心坐标
*/
private static int[] getCircleCoords(int face, int blockWidth, int blockHeight, int blockRadius) {
//
if (0 == face) {
return new int[]{blockWidth / 2 - 1, blockRadius};
}
//
else if (1 == face) {
return new int[]{blockRadius, blockHeight / 2 - 1};
}
//
else if (2 == face) {
return new int[]{blockWidth / 2 - 1, blockHeight - blockRadius - 1};
}
//
else if (3 == face) {
return new int[]{blockWidth - blockRadius - 1, blockHeight / 2 - 1};
}
return null;
}
/**
* 在画布上添加阻塞块水印
*/
private static void addBlockWatermark(BufferedImage canvasImage, BufferedImage blockImage, int x, int y) {
Graphics2D graphics2D = canvasImage.createGraphics();
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.8f));
graphics2D.drawImage(blockImage, x, y, null);
graphics2D.dispose();
}
/**
* BufferedImage转BASE64
*/
public static String toBase64(BufferedImage bufferedImage, String type) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, type, byteArrayOutputStream);
String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return String.format("data:image/%s;base64,%s", type, base64);
} catch (IOException e) {
System.out.println("图片资源转换BASE64失败");
//异常处理
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
package com.yunan.util;
import java.util.Random;
public class VerificationCodeUtils {
/**
* 生成随机验证码
*
* @param length 验证码长度
* @return 验证码
*/
public static String generateCode(int length) {
StringBuilder s = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
int n = random.nextInt(10);
s.append(n);
}
return s.toString();
}
}

View File

@ -1,61 +0,0 @@
spring:
application:
name: YuNan-demo
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yuna_blog_sys
username: root
password: wucong.0235332
mvc:
pathmatch:
matching-strategy: ant-path-matcher
---
#****************************mail***************************
spring:
mail:
# SMTP服务器这个是QQ邮箱的 其他邮箱请另行百度
host: smtp.qq.com
port: 465
# 发送验证码的邮箱
username: 3194726156@qq.com
# 授权码
password: ljtcrhshbfjodgfh
# 编码
default-encoding: utf-8
# 其它参数
properties:
mail:
smtp:
# 如果是用 SSL 方式,需要配置如下属性,使用qq邮箱的话需要开启
ssl:
enable: true
required: true
# 邮件接收时间的限制,单位毫秒
timeout: 10000
# 连接时间的限制,单位毫秒
connectiontimeout: 10000
# 邮件发送时间的限制,单位毫秒
writetimeout: 10000
---
#****************************Redis***************************
spring:
redis:
host: localhost # Redis 服务器地址
port: 6379 # Redis 服务器端口
password: # 如果 Redis 设置了密码
database: 0 # 使用的数据库索引,默认是 0
server:
port: 8080
logging:
level:
org.springframework.boot.context.web: DEBUG

View File

@ -1,11 +0,0 @@
<?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.yunan.mapper.AuthMapper">
<insert id="insertUser" parameterType="com.yunan.dto.RegisterDTO">
insert into users (username, password, email) values (#{username}, #{password}, #{email})
</insert>
<select id="findUserByEmail" resultType="com.yunan.dto.Users">
select * from users where email = #{email}
</select>
</mapper>

View File

@ -3,14 +3,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>yunan-boot-parent</groupId>
<artifactId>org.yunanframework.boot</artifactId>
<groupId>ecs-boot-parent</groupId>
<artifactId>org.ecsframework.boot</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<!-- 子模块列表 -->
<module>YuNan-system-start</module>
<module>ECS-system-start</module>
</modules>
<parent>