表名:user
列名 | 数据类型 | 描述 |
---|---|---|
id | int | 自增ID |
username | varchar | 用户名 |
password | varchar | 密码 |
nickname | varchar | 昵称 |
这个方案只保留了id、username、password和nickname四个字段,以最简单的方式存储用户基本信息。需要注意的是,密码应该进行安全处理(如加密),避免泄露敏感信息。如果后续有新增信息需求,则可以随时更改表结构,添加相应的列即可。
-- 创建 usertable 数据库
CREATE DATABASE IF NOT EXISTS usertable;
-- 切换至 usertable 数据库
USE usertable;
-- 创建 user 表
CREATE TABLE IF NOT EXISTS user (
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
nickname VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 添加一些测试数据
INSERT INTO user (username, password, nickname) VALUES ('user1', 'password1', '张三');
INSERT INTO user (username, password, nickname) VALUES ('user2', 'password2', '李四');
INSERT INTO user (username, password, nickname) VALUES ('user3', 'password3', '王五');
这段SQL代码用于创建一个名为user
的表格,并且添加了一些简单的测试数据。其中,id
列使用了自增主键约束,保证数据的唯一性。username
列使用了unique
约束,确保用户名的唯一性。请注意,utf8mb4
是一种更高效和更通用的字符编码,支持更广泛的Unicode字符集,所以它比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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.11version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>userTableartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>userTablename>
<description>userTabledescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.7.6version>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-jpaartifactId>
<version>2.5.6version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-coreartifactId>
<version>5.6.3.Finalversion>
dependency>
<dependency>
<groupId>javax.persistencegroupId>
<artifactId>javax.persistence-apiartifactId>
<version>2.2version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.8.0version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-apiartifactId>
<version>0.11.2version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-implartifactId>
<version>0.11.2version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-jacksonartifactId>
<version>0.11.2version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.8.0version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
# ?????
spring.datasource.url=jdbc:mysql://localhost:3306/usertable?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# ???????
server.port=8081
# MyBatis-Plus ??
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
mybatis-plus.type-aliases-package=com.example.demo.entity
mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.use-generated-keys=true
mybatis-plus.configuration.map-enum-as-ordinal=false
mybatis-plus.configuration.enum-handler=com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user") // 声明对应的数据库表名(user)
public class User {
private Long id;
private String username;
private String password;
private String nickname;
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private Integer code; // 状态码
private String message; // 状态信息
private Object data; // 数据
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 允许来自本地的8080端口发起的跨域请求
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true).maxAge(3600);
}
};
}
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.usertable.Bean.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.usertable.Bean.User;
public interface UserService extends IService<User> {
/**
* 分页查询用户列表
*/
IPage<User> selectPage(Page<User> page);
/**
* 用户注册
*/
boolean register(User user);
/**
* 用户登录
*/
User login(String username, String password);
}
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.usertable.Bean.User;
import com.example.usertable.Mapper.UserMapper;
import com.example.usertable.Service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public IPage<User> selectPage(Page<User> page) {
return baseMapper.selectPage(page, null);
}
/**
* 注册新用户(需先检查用户名是否已被使用)
*/
@Override
public boolean register(User user) {
String username = user.getUsername();
// 根据用户名查询用户
User u = this.getOne(new QueryWrapper<User>().eq("username", username));
if (u != null) {
// 用户名已存在
return false;
} else {
// 将用户保存到数据库
return this.save(user);
}
}
/**
* 用户登录(根据用户名和密码查询用户)
*/
@Override
public User login(String username, String password) {
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
// 用户名和密码不能为空
return null;
}
return this.getOne(new QueryWrapper<User>()
.eq("username", username)
.eq("password", password));
}
}
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.usertable.Bean.Result;
import com.example.usertable.Bean.User;
import com.example.usertable.Service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@AllArgsConstructor
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
/**
* 获取用户列表(分页)
*/
@GetMapping("/")
public Result list(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
// 构建分页对象
Page<User> page = new Page<>(pageNum, pageSize);
// 分页查询用户数据
IPage<User> userPage = userService.page(page, null);
return Result.builder()
.code(200)
.message("获取成功")
.data(userPage)
.build();
}
/**
* 根据 ID 获取用户信息
*/
@GetMapping("/{id}")
public Result detail(@PathVariable Long id) {
// 查询用户信息
User user = userService.getById(id);
if (user != null) {
return Result.builder()
.code(200)
.message("获取成功")
.data(user)
.build();
} else {
return Result.builder()
.code(404)
.message("用户不存在")
.build();
}
}
/**
* 注册
*/
@PostMapping("/register")
public Result register(@RequestBody @Validated User user) {
boolean success = userService.register(user);
if (success) {
return Result.builder()
.code(200)
.message("注册成功")
.data(user)
.build();
} else {
return Result.builder()
.code(500)
.message("用户名已存在")
.build();
}
}
/**
* 登录
*/
@PostMapping("/login")
public Result login(@RequestBody User user) {
String username = user.getUsername();
String password = user.getPassword();
// 查询用户
User loginUser = userService.login(username, password);
if (loginUser != null) {
return Result.builder()
.code(200)
.message("登录成功")
.data(loginUser)
.build();
} else {
return Result.builder()
.code(401)
.message("用户名或密码错误")
.build();
}
}
/**
* 新增用户
*/
@PostMapping("/")
public Result add(@RequestBody @Validated User user) {
boolean success = userService.save(user);
if (success) {
return Result.builder()
.code(200)
.message("新增成功")
.data(user)
.build();
} else {
return Result.builder()
.code(500)
.message("新增失败")
.data(user)
.build();
}
}
/**
* 更新用户信息
*/
@PutMapping("/{id}")
public Result update(@PathVariable Long id, @RequestBody @Validated User user) {
user.setId(id);
boolean success = userService.updateById(user);
if (success) {
return Result.builder()
.code(200)
.message("更新成功")
.data(user)
.build();
} else {
return Result.builder()
.code(500)
.message("更新失败")
.data(user)
.build();
}
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
boolean success = userService.removeById(id);
if (success) {
return Result.builder()
.code(200)
.message("删除成功")
.build();
} else {
return Result.builder()
.code(500)
.message("删除失败")
.build();
}
}
}
测试所有的接口
以下是基于8081端口号的 Postman 测试:
请求地址:http://localhost:8081/users/
请求方法:GET
请求参数:
参数名称 | 参数类型 | 是否必须 | 默认值 | 参数说明 |
---|---|---|---|---|
pageNum | Integer | 否 | 1 | 当前页码 |
pageSize | Integer | 否 | 10 | 每页记录数 |
成功响应:
{
"code": 200,
"message": "获取成功",
"data": {
"records": [
{
"id": 1,
"username": "user1",
"password": "password1",
"nickname": "张三"
},
{
"id": 2,
"username": "user2",
"password": "password2",
"nickname": "李四"
},
{
"id": 3,
"username": "user3",
"password": "password3",
"nickname": "王五"
}
],
"total": 0,
"size": 10,
"current": 1,
"orders": [],
"optimizeCountSql": true,
"hitCount": false,
"countId": null,
"maxLimit": null,
"searchCount": true,
"pages": 0
}
}
请求地址:http://localhost:8081/users/{id}
请求方法:GET
请求路径参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
id | Long | 是 | 1 | 用户 ID |
成功响应:
{
"code": 200,
"message": "获取成功",
"data": {
"id": 1,
"username": "user1",
"password": "password1",
"nickname": "张三"
}
}
请求地址:http://localhost:8081/users/register
请求方法:POST
请求参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
username | String | 是 | user-11 | 用户名 |
password | String | 是 | pass-11 | 密码 |
nickname | String | 否 | lihua | 昵名 |
请求示例:
{
"username": "user-11",
"password": "pass-11",
"nickname":"lihua"
}
成功响应:
{
"code": 200,
"message": "注册成功",
"data": {
"id": 4,
"username": "user-11",
"password": "pass-11",
"nickname": "lihua"
}
}
失败响应:
{
"code": 500,
"message": "用户名已存在"
}
请求地址:http://localhost:8081/users/login
请求方法:POST
请求参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
username | String | 是 | user-1 | 用户名 |
password | String | 是 | pass-1 | 密码 |
请求示例:
{
"username": "user2",
"password": "password2"
}
成功响应:
{
"code": 200,
"message": "登录成功",
"data": {
"password": "password2",
"nickname": "李四",
"id": 2,
"username": "user2"
}
}
失败响应:
{
"code": 401,
"message": "用户名或密码错误",
"data": null
}
请求地址:http://localhost:8081/users/
请求方法:POST
请求参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
username | String | 是 | user-12 | 用户名 |
password | String | 是 | pass-12 | 密码 |
nickname | String | 否 | 小李 | 昵名 |
请求示例:
{
"username": "user-12",
"password": "pass-12",
"nickname": "小李"
}
成功响应:
{
"code": 200,
"message": "新增成功",
"data": {
"id": 5,
"username": "user-12",
"password": "pass-12",
"nickname": "小李"
}
}
失败响应:
{
"code": 500,
"message": "新增失败"
}
请求地址:http://localhost:8081/users/{id}
请求方法:PUT
请求路径参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
username | String | 否 | user-12-update | 用户名 |
password | String | 否 | pass-12-update | 密码 |
String | 否 | [email protected] | 邮箱 | |
phone | String | 否 | 12345678901 | 手机号 |
请求示例:
{
"username": "user-13",
"password": "pass-13",
"nickname": "小李"
}
成功响应:
{
"code": 200,
"message": "更新成功",
"data": {
"id": 1,
"username": "user-13",
"password": "pass-13",
"nickname": "小李"
}
}
失败响应:
{
"code": 500,
"message": "更新失败"
}
请求地址:http://localhost:8081/users/{id}
请求方法:DELETE
请求路径参数:
参数名称 | 参数类型 | 是否必须 | 示例值 | 参数说明 |
---|---|---|---|---|
id | Long | 是 | 12 | 用户 ID |
成功响应:
{
"code": 200,
"message": "删除成功"
}
失败响应:
{
"code": 500,
"message": "删除失败"
}
{
"name": "LoginAndRegister",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
"" ,
""
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"devServer" : {
"port" : 8080, //浏览器运行端口
"disableHostCheck" : true, //设置跳过host检查
"proxy" : {
"/api" : {
"target" : "http://localhost:8081", //目标接口域名
"changeOrigin" : true, //是否跨域
"secure" : false, // 设置支持https协议的代理
"pathRewrite":{"^/api":""}
}
}
}
}
}
<template>
<view class="content">
<image class="logo" src="/static/logo.png">image>
<view class="text-area">
<text class="title">{{title}}text>
<button @click="handleClick">点击我button>
view>
view>
template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
handleClick() {
console.log('您点击了该按钮!')
}
}
}
script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
style>
<template>
<view class="login">
<image class="logo" src="/static/logo.png">image>
<view class="input-item">
<input type="text" v-model="username" placeholder="请输入用户名">
view>
<view class="input-item">
<input type="password" v-model="password" placeholder="请输入密码">
view>
<button @click="login" class="login-btn">登录button>
<button @click="goToRegister" class="register-btn">注册button>
view>
template>
<script>
import axios from 'axios'
export default {
data() {
return {
username: '',
password: ''
}
},
methods: {
login() {
if (!this.username || !this.password) {
uni.showToast({
title: '请填写用户名和密码',
icon: 'none'
})
return
}
// 发送请求验证用户
axios.post('/api/users/login', {
username: this.username,
password: this.password
}).then(res => {
console.log(res.data.code)
if (res.data.code === 200) {
// 将用户信息保存到客户端本地缓存中
uni.setStorageSync('userInfo', {
id: res.data.data.id,
username: res.data.data.username,
password: res.data.data.password,
nickname: res.data.data.nickname
})
// 跳转到首页
uni.navigateTo({
url: '/pages/index/index'
})
} else {
uni.showToast({
title: res.data.message,
icon: 'none'
})
}
}).catch(err => {
uni.showToast({
title: '网络请求失败,请重试',
icon: 'none'
})
})
},
goToRegister() {
uni.navigateTo({
url: '/pages/register/register'
})
}
}
}
script>
<style scoped>
.login {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 100rpx;
}
.logo {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.login-form {
width: 90%;
padding: 40rpx;
background-color: #fff;
border-radius: 5rpx;
}
.input-item {
width: 80%;
margin: 10rpx 0;
border-bottom: 1rpx solid #ddd;
}
input {
width: 100%;
height: 50rpx;
padding: 10rpx;
font-size: 16rpx;
outline: none;
border: none;
}
.login-btn {
display: block;
margin: 30rpx auto 0;
width: 80%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background-color: #007aff;
color: #fff;
border-radius: 5rpx;
font-size: 20rpx;
}
.register-btn {
margin-top: 20rpx;
color: #007aff;
width: 60%;
height: 80rpx;
}
style>
<template>
<view class="register">
<image class="logo" src="/static/logo.png">image>
<form class="register-form">
<view class="input-item">
<input type="text" v-model="nickname" placeholder="请输入昵称">
view>
<view class="input-item">
<input type="text" v-model="username" placeholder="请输入用户名">
view>
<view class="input-item">
<input type="password" v-model="password" placeholder="请输入密码">
view>
<view class="input-item">
<input type="password" v-model="confirmPassword" placeholder="请确认密码">
view>
<button type="button" class="register-btn" @click="register">注册button>
form>
<button class="back-btn" @click="goBack">返回button>
view>
template>
<script>
import axios from 'axios';
export default {
data() {
return {
username: '',
password: '',
confirmPassword: '',
nickname: ''
};
},
methods: {
async register() {
if(!this.username || !this.password || !this.nickname) {
uni.showToast({
title: '请填写完整信息',
icon: 'none'
});
return;
}
if(this.password !== this.confirmPassword) {
uni.showToast({
title: '两次输入密码不一致',
icon: 'none'
});
return;
}
try {
const response = await axios.post('/api/users/register', {
username: this.username,
password: this.password,
nickname: this.nickname
});
const responseData = response.data;
if(responseData.code === 200) {
uni.showToast({
title: responseData.message,
icon: 'success'
});
uni.navigateTo({
url: '/pages/login/login'
});
} else {
uni.showToast({
title: responseData.message,
icon: 'none'
});
}
} catch (error) {
let errorMessage = '注册失败,请稍后再试';
if(error.response) {
if(error.response.status === 500) {
errorMessage = error.response.data.message;
}
}
uni.showToast({
title: errorMessage,
icon: 'none'
});
}
},
goBack() {
uni.navigateBack();
}
}
};
script>
<style scoped>
.register {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 100rpx;
}
.logo {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.register-form {
width: 90%;
padding: 40rpx;
background-color: #fff;
border-radius: 5rpx;
}
.input-item {
margin: 10rpx 0;
border-bottom: 1rpx solid #ddd;
}
input {
width: 100%;
height: 50rpx;
padding: 10rpx;
font-size: 16rpx;
outline: none;
border: none;
}
.register-btn {
display: block;
margin: 30rpx auto 0;
width: 90%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background-color: #007aff;
color: #fff;
border-radius: 5rpx;
font-size: 20rpx;
}
.back-btn {
margin-top: 20rpx;
color: #007aff;
width: 60%;
height: 80rpx;
}
style>