commit
a013fac823
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ApifoxUploaderProjectSetting">
|
||||||
|
<option name="apiAccessToken" value="APS-Du6Eoh7CmJJcLtPBY40931ru8MEovCoV" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="true" />
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="unilife-server" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -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="unilife@localhost" uuid="9c6c9710-15d0-4710-8fca-930cc43549e9">
|
||||||
|
<driver-ref>mysql.8</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mysql://localhost:3306/unilife</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>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/unilife-server/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/unilife-server/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://maven.aliyun.com/repository/public" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/backend/pom.xml" />
|
||||||
|
<option value="$PROJECT_DIR$/unilife-server/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/unilife.iml" filepath="$PROJECT_DIR$/.idea/unilife.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "vue-unilife",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "^3.5.13",
|
||||||
|
"axios": "^1.8.3",
|
||||||
|
"element-plus": "^2.9.7",
|
||||||
|
"vee-validate": "^4.15.0",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"yup": "^1.6.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"typescript": "~5.7.2",
|
||||||
|
"vite": "^6.2.0",
|
||||||
|
"vue-tsc": "^2.2.4"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import LogPage from './components/LogPage.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LogPage/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,37 @@
|
|||||||
|
import request from '../utils/request';
|
||||||
|
|
||||||
|
// 用户注册
|
||||||
|
export function register(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/register',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户密码登录
|
||||||
|
export function login(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/login',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取邮箱验证码
|
||||||
|
export function getEmailCode(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/email/code',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 邮箱验证码登录
|
||||||
|
export function loginWithCode(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/login/code',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
After Width: | Height: | Size: 496 B |
@ -0,0 +1,9 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import './style.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.mount('#app')
|
||||||
|
app.use(ElementPlus)
|
@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<{}, {}, any>;
|
||||||
|
export default component;
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
:root {
|
||||||
|
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: 'http://localhost:8080',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
console.log(1);
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
console.log(2);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// 对响应错误做些什么
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default service;
|
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
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>com.example</groupId>
|
||||||
|
<artifactId>unilife-server</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>backend</name>
|
||||||
|
<description>backend</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<spring-boot.version>2.7.6</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.36</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||||
|
<version>4.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.16</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- redis-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>redis.clients</groupId>
|
||||||
|
<artifactId>jedis</artifactId>
|
||||||
|
<version>5.1.3</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 邮箱服务-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- IP地址服务-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>2.7.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.example.unilife.BackendApplication</mainClass>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>repackage</id>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.unilife;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class UniLifeApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(UniLifeApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.unilife.common.constant;
|
||||||
|
|
||||||
|
|
||||||
|
public class RedisConstant {
|
||||||
|
public static final String LOGIN_EMAIL_KEY="login:email:";
|
||||||
|
public static final String LOGIN_EMAIL_LIMIT_KEY = "login:email:limit:";
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.unilife.common.result;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Result<T>{
|
||||||
|
/***
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
/***
|
||||||
|
* 消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
/***
|
||||||
|
*数据
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return 结果对象
|
||||||
|
* @param <T> 数据类型
|
||||||
|
*/
|
||||||
|
public static <T>Result<T>success(T data){
|
||||||
|
return new Result<T>(200, "success", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功返回结果
|
||||||
|
* @return 结果对象
|
||||||
|
*/
|
||||||
|
public static Result<Void> success() {
|
||||||
|
return new Result<>(200, "success", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success(T data, String message) {
|
||||||
|
return new Result<>(200, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败返回结果
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 消息
|
||||||
|
* @return 结果对象
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(Integer code, String message) {
|
||||||
|
return new Result<>(code, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(T data,String message){
|
||||||
|
return new Result<>(200,message,null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.unilife.controller;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.model.dto.EmailDTO;
|
||||||
|
import com.unilife.model.dto.LogDTO;
|
||||||
|
import com.unilife.model.dto.LoginDTO;
|
||||||
|
import com.unilife.model.dto.LoginEmailDTO;
|
||||||
|
import com.unilife.service.UserService;
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
@Api(tags = "用户管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
@Slf4j
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@ApiOperation(value = "用户注册")
|
||||||
|
@PostMapping("register")
|
||||||
|
public Result register(@RequestBody LoginDTO loginDTO, HttpServletRequest request) {
|
||||||
|
return userService.register(loginDTO,request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "用户登录")
|
||||||
|
@PostMapping("login")
|
||||||
|
public Result login(@RequestBody LogDTO logDTO,HttpServletRequest request) { return userService.login(logDTO,request); }
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取邮箱验证码")
|
||||||
|
@PostMapping("code")
|
||||||
|
public Result getCode(@RequestBody EmailDTO emailDto,HttpServletRequest request) {
|
||||||
|
String email=emailDto.getEmail();
|
||||||
|
log.debug("收到的原始邮箱: {}", email);
|
||||||
|
return userService.sendVerificationCode(email,request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "邮箱验证码登录")
|
||||||
|
@PostMapping("login/code")
|
||||||
|
public Result loginWithEmailCode(@RequestBody LoginEmailDTO loginEmailDTO,HttpServletRequest request) {
|
||||||
|
return userService.loginWithEmail(loginEmailDTO,request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.unilife.mapper;
|
||||||
|
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface UserMapper {
|
||||||
|
void insert(User user);
|
||||||
|
User FindByEmail(@Param("email") String email, @Param("password") String password);
|
||||||
|
User getUserByEmail(String email);
|
||||||
|
void UpdateIPLocation(@Param("email") String email,@Param("loginIp") String loginIp);
|
||||||
|
User FindByOnlyEmail(@Param("email") String email);
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.unilife.model.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class EmailDTO {
|
||||||
|
String email;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.unilife.model.dto;
|
||||||
|
//这个才是登录的DTO
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LogDTO {
|
||||||
|
private String email;
|
||||||
|
private String password;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.unilife.model.dto;
|
||||||
|
//这个是注册的DTO
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class LoginDTO {
|
||||||
|
private String username;
|
||||||
|
private String email;
|
||||||
|
private String password;
|
||||||
|
private String nickname;
|
||||||
|
private String studentId;
|
||||||
|
private String department;
|
||||||
|
private String major;
|
||||||
|
private String grade;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.unilife.model.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class LoginEmailDTO {
|
||||||
|
private String email;
|
||||||
|
private String code;
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.unilife.model.entity;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class User implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
private String bio;
|
||||||
|
|
||||||
|
private Byte gender;
|
||||||
|
|
||||||
|
private String studentId;
|
||||||
|
|
||||||
|
private String department;
|
||||||
|
|
||||||
|
private String major;
|
||||||
|
|
||||||
|
private String grade;
|
||||||
|
|
||||||
|
private Integer points = 0;
|
||||||
|
|
||||||
|
private Byte role = 0;
|
||||||
|
|
||||||
|
private Byte status = 1;
|
||||||
|
|
||||||
|
private Byte isVerified = 0;
|
||||||
|
|
||||||
|
private String loginIp;
|
||||||
|
|
||||||
|
private LocalDateTime loginTime;
|
||||||
|
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LogVO {
|
||||||
|
private Integer id;
|
||||||
|
private String username;
|
||||||
|
private String nickname;
|
||||||
|
private String avatar;
|
||||||
|
private Byte role;
|
||||||
|
private Byte isVerified;
|
||||||
|
private Byte status;
|
||||||
|
private String loginIp;
|
||||||
|
|
||||||
|
public LogVO(Integer id,String username,String nickname,String avatar,Byte role,Byte isVerified,Byte status,String loginIp)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.nickname = nickname;
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.role = role;
|
||||||
|
this.isVerified = isVerified;
|
||||||
|
this.status = status;
|
||||||
|
this.loginIp = loginIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LoginVO {
|
||||||
|
private Integer id;
|
||||||
|
private String username;
|
||||||
|
private String nickname;
|
||||||
|
private String loginIp;
|
||||||
|
|
||||||
|
public LoginVO(Integer id, String username, String nickname,String loginIp)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.nickname = nickname;
|
||||||
|
this.loginIp = loginIp;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.model.dto.LoginDTO;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public interface IPLocationService {
|
||||||
|
public String getIPLocation(String ip);
|
||||||
|
public String getClientIP(HttpServletRequest request);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.model.dto.LogDTO;
|
||||||
|
import com.unilife.model.dto.LoginDTO;
|
||||||
|
import com.unilife.model.dto.LoginEmailDTO;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
public interface UserService {
|
||||||
|
Result register(LoginDTO loginDTO, HttpServletRequest request);
|
||||||
|
|
||||||
|
Result login(LogDTO logDTO, HttpServletRequest request);
|
||||||
|
|
||||||
|
Result sendVerificationCode(String email,HttpServletRequest request);
|
||||||
|
|
||||||
|
Result loginWithEmail(LoginEmailDTO loginEmailDTO, HttpServletRequest request);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,244 @@
|
|||||||
|
package com.unilife.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.unilife.common.constant.RedisConstant;
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.UserMapper;
|
||||||
|
import com.unilife.model.dto.LogDTO;
|
||||||
|
import com.unilife.model.dto.LoginDTO;
|
||||||
|
import com.unilife.model.dto.LoginEmailDTO;
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import com.unilife.model.vo.LogVO;
|
||||||
|
import com.unilife.model.vo.LoginVO;
|
||||||
|
import com.unilife.service.IPLocationService;
|
||||||
|
import com.unilife.service.UserService;
|
||||||
|
import com.unilife.utils.RegexUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.unilife.common.constant.RedisConstant.LOGIN_EMAIL_KEY;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@Service
|
||||||
|
public class UserServiceImpl implements UserService {
|
||||||
|
@Autowired
|
||||||
|
private IPLocationService ipLocationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JavaMailSender mailSender;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
@Value("${spring.mail.username}")
|
||||||
|
private String from;
|
||||||
|
|
||||||
|
final int CODE_EXPIRE_MINUTES = 10;
|
||||||
|
final int LIMIT_SECONDS=60;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result register(LoginDTO loginDTO, HttpServletRequest request) {
|
||||||
|
if(loginDTO.getEmail().isEmpty() || loginDTO.getPassword().isEmpty()) {
|
||||||
|
return Result.error(400,"邮箱或密码不能为空");
|
||||||
|
}
|
||||||
|
if(loginDTO.getPassword().length() < 6) {
|
||||||
|
return Result.error(400,"密码长度过短!");
|
||||||
|
}
|
||||||
|
User getuser = userMapper.FindByOnlyEmail(loginDTO.getEmail());
|
||||||
|
if(getuser != null) {
|
||||||
|
return Result.error(400,"用户已存在!");
|
||||||
|
}
|
||||||
|
User user = new User();
|
||||||
|
BeanUtil.copyProperties(loginDTO,user);
|
||||||
|
String IPAddress = ipLocationService.getClientIP(request);
|
||||||
|
String Location = ipLocationService.getIPLocation(IPAddress);
|
||||||
|
user.setLoginIp(Location);
|
||||||
|
userMapper.insert(user);
|
||||||
|
LoginVO loginVO = new LoginVO(Math.toIntExact(user.getId()),user.getUsername()
|
||||||
|
,user.getNickname(),user.getLoginIp());
|
||||||
|
return Result.success(loginVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result login(LogDTO logDTO,HttpServletRequest request) {
|
||||||
|
User user = new User();
|
||||||
|
BeanUtil.copyProperties(logDTO,user);//将登录的前端传来的消息拷贝给这个user
|
||||||
|
User getuser = userMapper.FindByEmail(user.getEmail(),user.getPassword());
|
||||||
|
if(getuser == null)
|
||||||
|
{
|
||||||
|
return Result.error(logDTO,"用户不存在,登录失败!");
|
||||||
|
}
|
||||||
|
if(!user.getPassword().equals(getuser.getPassword()))
|
||||||
|
{
|
||||||
|
return Result.error(logDTO,"密码错误,登录失败!");
|
||||||
|
}
|
||||||
|
String LastLogIpLocation = getuser.getLoginIp();
|
||||||
|
String IPAddress = ipLocationService.getClientIP(request);
|
||||||
|
String Location = ipLocationService.getIPLocation(IPAddress);
|
||||||
|
getuser.setLoginIp(Location);
|
||||||
|
userMapper.UpdateIPLocation(getuser.getEmail(), getuser.getLoginIp());
|
||||||
|
LogVO logVO = new LogVO(Math.toIntExact(getuser.getId()), getuser.getUsername(), getuser.getNickname(),
|
||||||
|
getuser.getAvatar(), getuser.getRole(), getuser.getIsVerified(), getuser.getStatus(),getuser.getLoginIp());
|
||||||
|
return Result.success(logVO,"上次登录IP归属地为" + LastLogIpLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result sendVerificationCode(String email,HttpServletRequest request) {
|
||||||
|
//1.校验邮箱是否合法
|
||||||
|
boolean emailInvalid = RegexUtils.isEmailInvalid(email);
|
||||||
|
if(emailInvalid){
|
||||||
|
return Result.error(400,"邮箱格式不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.防止频繁发送验证码
|
||||||
|
String countKey = RedisConstant.LOGIN_EMAIL_LIMIT_KEY + email;
|
||||||
|
Boolean setSuccess = stringRedisTemplate.opsForValue().setIfAbsent(
|
||||||
|
countKey,
|
||||||
|
"1",
|
||||||
|
Duration.ofSeconds(LIMIT_SECONDS)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Boolean.FALSE.equals(setSuccess)) {
|
||||||
|
return Result.error(null, "请求过于频繁,请稍后再试");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//3.生成随机验证码
|
||||||
|
String code = RandomUtil.randomNumbers(6);
|
||||||
|
log.debug("成功生成验证码,邮箱{},验证码{}", email, code);
|
||||||
|
|
||||||
|
|
||||||
|
//4.发送验证码到邮箱
|
||||||
|
try {
|
||||||
|
//构建邮件
|
||||||
|
MimeMessage message=mailSender.createMimeMessage();
|
||||||
|
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||||
|
|
||||||
|
helper.setFrom(from);
|
||||||
|
helper.setTo(email);
|
||||||
|
helper.setSubject("UniLife - 登录验证码");
|
||||||
|
|
||||||
|
String IPAddress = ipLocationService.getClientIP(request);
|
||||||
|
String Location = ipLocationService.getIPLocation(IPAddress);
|
||||||
|
String content = "<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #eee; border-radius: 5px;\">" +
|
||||||
|
"<h2 style=\"color: #333;\">您好!</h2>" +
|
||||||
|
"<p>感谢您使用UniLife平台,本次登录地为" + Location + "您的验证码是:</p>" +
|
||||||
|
"<div style=\"background-color: #f5f5f5; padding: 10px; text-align: center; font-size: 24px; font-weight: bold; letter-spacing: 5px; margin: 20px 0;\">" +
|
||||||
|
code +
|
||||||
|
"</div>" +
|
||||||
|
"<p>此验证码将在10分钟内有效。</p>" +
|
||||||
|
"<p>如果您没有请求此验证码,请忽略此邮件。</p>" +
|
||||||
|
"<p style=\"margin-top: 30px; font-size: 12px; color: #888;\">" +
|
||||||
|
"这是一封自动生成的邮件,请勿直接回复。" +
|
||||||
|
"</p></div>";
|
||||||
|
|
||||||
|
helper.setText(content, true);
|
||||||
|
|
||||||
|
//4.发送邮件
|
||||||
|
mailSender.send(message);
|
||||||
|
}catch (MessagingException e){
|
||||||
|
log.error("邮件发送失败");
|
||||||
|
return Result.error(400,"邮件发送失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//5.存储随机产生的验证码,设置有效期为十分钟
|
||||||
|
stringRedisTemplate.opsForValue().set(LOGIN_EMAIL_KEY + email, code, Duration.ofMinutes(CODE_EXPIRE_MINUTES));
|
||||||
|
|
||||||
|
return Result.success(200,"验证码已发送");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result loginWithEmail(LoginEmailDTO loginEmailDTO,HttpServletRequest request) {
|
||||||
|
String email=loginEmailDTO.getEmail();
|
||||||
|
|
||||||
|
if(RegexUtils.isEmailInvalid(email)){
|
||||||
|
return Result.error(null,"请输入正确的邮箱");
|
||||||
|
}
|
||||||
|
|
||||||
|
String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.LOGIN_EMAIL_KEY + email);
|
||||||
|
if (cacheCode == null) {
|
||||||
|
return Result.error(null, "验证码已过期或未发送,请重新获取");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 校验验证码是否正确
|
||||||
|
String code = loginEmailDTO.getCode();
|
||||||
|
if (!cacheCode.equals(code)) {
|
||||||
|
return Result.error(null, "验证码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验证通过,删除验证码
|
||||||
|
stringRedisTemplate.delete(RedisConstant.LOGIN_EMAIL_KEY + email);
|
||||||
|
|
||||||
|
// 5. 查询用户是否存在
|
||||||
|
User user=userMapper.getUserByEmail(email);
|
||||||
|
if(user == null){
|
||||||
|
user = createUserWithEmail(email,request);
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.生成登录凭证
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
// 8. 返回用户信息和登录凭证
|
||||||
|
Map<String, Object> userInfo = new HashMap<>();
|
||||||
|
//HashMap userInfo.put("token", token);
|
||||||
|
userInfo.put("user", user);
|
||||||
|
|
||||||
|
return Result.success(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用邮箱信息创建新用户
|
||||||
|
*/
|
||||||
|
private User createUserWithEmail(String email,HttpServletRequest request) {
|
||||||
|
User user = new User();
|
||||||
|
user.setEmail(email);
|
||||||
|
user.setNickname("用户" + RandomUtil.randomString(6)); // 生成随机昵称
|
||||||
|
String username = email.split("@")[0]+"_"+ RandomUtil.randomString(4); // 使用@前面的部分作为用户名
|
||||||
|
user.setUsername(username);
|
||||||
|
|
||||||
|
String password = RandomUtil.randomString(6);
|
||||||
|
String IPAddress = ipLocationService.getClientIP(request);
|
||||||
|
String Location = ipLocationService.getIPLocation(IPAddress);
|
||||||
|
user.setPassword(password);
|
||||||
|
|
||||||
|
user.setRole((byte)0); // 普通用户角色
|
||||||
|
user.setStatus((byte)1); // 正常状态
|
||||||
|
user.setIsVerified((byte)0); // 未验证
|
||||||
|
user.setPoints(0); // 初始积分
|
||||||
|
user.setGender((byte)0);
|
||||||
|
user.setLoginIp(Location);//注册地IP
|
||||||
|
// 保存用户
|
||||||
|
try {
|
||||||
|
userMapper.insert(user);
|
||||||
|
}catch (Exception e){
|
||||||
|
log.error("用户创建失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.unilife.utils;
|
||||||
|
|
||||||
|
public class RegexPatterns {
|
||||||
|
/**
|
||||||
|
* 手机号正则
|
||||||
|
*/
|
||||||
|
public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";
|
||||||
|
/**
|
||||||
|
* 邮箱正则
|
||||||
|
*/
|
||||||
|
public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
|
||||||
|
/**
|
||||||
|
* 密码正则。4~32位的字母、数字、下划线
|
||||||
|
*/
|
||||||
|
public static final String PASSWORD_REGEX = "^\\w{4,32}$";
|
||||||
|
/**
|
||||||
|
* 验证码正则, 6位数字或字母
|
||||||
|
*/
|
||||||
|
public static final String VERIFY_CODE_REGEX = "^[a-zA-Z\\d]{6}$";
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,78 @@
|
|||||||
|
<?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.unilife.mapper.UserMapper">
|
||||||
|
<resultMap id="userResultMap" type="com.unilife.model.entity.User">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="email" property="email"/>
|
||||||
|
<result column="password" property="password"/>
|
||||||
|
<result column="username" property="username"/>
|
||||||
|
<result column="nickname" property="nickname"/>
|
||||||
|
<result column="avatar" property="avatar"/>
|
||||||
|
<result column="role" property="role"/>
|
||||||
|
<result column="is_verified" property="isVerified"/>
|
||||||
|
<result column="status" property="status"/>
|
||||||
|
<result column="login_ip" property="loginIp"/>
|
||||||
|
</resultMap>
|
||||||
|
<insert id="insert" parameterType="com.unilife.model.entity.User" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO users (
|
||||||
|
username, email, password,nickname,avatar,bio, gender,student_id,department, major,grade,points,role,status,is_verified,login_ip,login_time
|
||||||
|
) VALUES (
|
||||||
|
#{username},
|
||||||
|
#{email},
|
||||||
|
#{password},
|
||||||
|
#{nickname},
|
||||||
|
#{avatar},
|
||||||
|
#{bio},
|
||||||
|
#{gender},
|
||||||
|
#{studentId},
|
||||||
|
#{department},
|
||||||
|
#{major},
|
||||||
|
#{grade},
|
||||||
|
#{points},
|
||||||
|
#{role},
|
||||||
|
#{status},
|
||||||
|
#{isVerified},
|
||||||
|
#{loginIp},
|
||||||
|
#{loginTime}
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="FindByEmail" resultMap="userResultMap">
|
||||||
|
SELECT id, email, password, username, nickname, avatar, role, is_verified, status,login_ip
|
||||||
|
FROM users
|
||||||
|
WHERE email = #{email} AND password = #{password}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="FindByOnlyEmail" resultMap="userResultMap">
|
||||||
|
SELECT id, email, password, username, nickname, avatar, role, is_verified, status,login_ip
|
||||||
|
FROM users
|
||||||
|
WHERE email = #{email}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="UpdateIPLocation" parameterType="com.unilife.model.entity.User">
|
||||||
|
UPDATE users
|
||||||
|
SET login_ip = #{loginIp}
|
||||||
|
WHERE email = #{email}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="getUserByEmail" resultType="com.unilife.model.entity.User">
|
||||||
|
select username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
nickname,
|
||||||
|
avatar,
|
||||||
|
bio,
|
||||||
|
gender,
|
||||||
|
student_id,
|
||||||
|
department,
|
||||||
|
major,
|
||||||
|
grade,
|
||||||
|
points,
|
||||||
|
role,
|
||||||
|
status,
|
||||||
|
is_verified,
|
||||||
|
login_ip,
|
||||||
|
login_time
|
||||||
|
from users where email=#{email};
|
||||||
|
</select>
|
||||||
|
</mapper>
|
@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>hello word!!!</h1>
|
||||||
|
<p>this is a html page</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.unilife;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class BackendApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
After Width: | Height: | Size: 268 KiB |
Loading…
Reference in new issue